Generates a dummy certificate. privkey: CA private key cacert: CA certificate commonname: Common name for the generated certificate. sans: A list of Subject Alternate Names. organization: Organization name for the generated certificate. crl_url: URL of CRL distribution
(
privkey: rsa.RSAPrivateKey,
cacert: x509.Certificate,
commonname: str | None,
sans: Iterable[x509.GeneralName],
organization: str | None = None,
crl_url: str | None = None,
)
| 312 | |
| 313 | |
| 314 | def dummy_cert( |
| 315 | privkey: rsa.RSAPrivateKey, |
| 316 | cacert: x509.Certificate, |
| 317 | commonname: str | None, |
| 318 | sans: Iterable[x509.GeneralName], |
| 319 | organization: str | None = None, |
| 320 | crl_url: str | None = None, |
| 321 | ) -> Cert: |
| 322 | """ |
| 323 | Generates a dummy certificate. |
| 324 | |
| 325 | privkey: CA private key |
| 326 | cacert: CA certificate |
| 327 | commonname: Common name for the generated certificate. |
| 328 | sans: A list of Subject Alternate Names. |
| 329 | organization: Organization name for the generated certificate. |
| 330 | crl_url: URL of CRL distribution point |
| 331 | |
| 332 | Returns cert if operation succeeded, None if not. |
| 333 | """ |
| 334 | builder = x509.CertificateBuilder() |
| 335 | builder = builder.issuer_name(cacert.subject) |
| 336 | builder = builder.add_extension( |
| 337 | x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]), critical=False |
| 338 | ) |
| 339 | builder = builder.public_key(cacert.public_key()) |
| 340 | |
| 341 | now = datetime.datetime.now() |
| 342 | builder = builder.not_valid_before(now + CERT_VALIDITY_OFFSET) |
| 343 | builder = builder.not_valid_after(now + CERT_VALIDITY_OFFSET + CERT_EXPIRY) |
| 344 | |
| 345 | subject = [] |
| 346 | is_valid_commonname = commonname is not None and len(commonname) < 64 |
| 347 | if is_valid_commonname: |
| 348 | assert commonname is not None |
| 349 | subject.append(x509.NameAttribute(NameOID.COMMON_NAME, commonname)) |
| 350 | if organization is not None: |
| 351 | assert organization is not None |
| 352 | subject.append(x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization)) |
| 353 | builder = builder.subject_name(x509.Name(subject)) |
| 354 | builder = builder.serial_number(x509.random_serial_number()) |
| 355 | |
| 356 | # RFC 5280 §4.2.1.6: subjectAltName is critical if subject is empty. |
| 357 | builder = builder.add_extension( |
| 358 | x509.SubjectAlternativeName(_fix_legacy_sans(sans)), |
| 359 | critical=not is_valid_commonname, |
| 360 | ) |
| 361 | |
| 362 | # https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.1 |
| 363 | # Per RFC 5280 §4.2.1.2, the AKI's keyIdentifier in a child certificate |
| 364 | # MUST be byte-equal to the issuer's stored SubjectKeyIdentifier. If we |
| 365 | # recompute it from the public key with `from_issuer_public_key()`, we |
| 366 | # always derive a SHA-1 digest, which mismatches whenever the issuer's |
| 367 | # SKI was generated by a different method (RFC 7093 truncated |
| 368 | # SHA-256/384/512, hardware-rooted CAs, or any custom value). This |
| 369 | # mismatch is rejected by strict TLS chain builders (`X509_V_FLAG_X509_STRICT`, |
| 370 | # Python `ssl`, Go `crypto/x509`) with "authority and subject key |
| 371 | # identifier mismatch". Most notably, cert-manager >=1.18 / Go >=1.25 |
no test coverage detected
searching dependent graphs…