createCertificate static method

X509Certificate createCertificate({
  1. required AsymmetricKeyPair<PublicKey, PrivateKey> keyPair,
  2. required AsymmetricKeyPair<PublicKey, PrivateKey> issuerKeyPair,
  3. required String subjectDn,
  4. required String issuerDn,
  5. required int serialNumber,
  6. BigInt? serialNumberBigInt,
  7. required DateTime notBefore,
  8. required DateTime notAfter,
  9. bool isCa = false,
  10. List<String>? crlUrls,
  11. List<String>? ocspUrls,
  12. List<PkiOtherName>? subjectAltNameOtherNames,
  13. List<String>? extendedKeyUsageOids,
  14. int? keyUsageBits,
})

Low-level X.509 Certificate creation.

Implementation

static X509Certificate createCertificate({
  required AsymmetricKeyPair<PublicKey, PrivateKey> keyPair,
  required AsymmetricKeyPair<PublicKey, PrivateKey> issuerKeyPair,
  required String subjectDn,
  required String issuerDn,
  required int serialNumber,
  BigInt? serialNumberBigInt,
  required DateTime notBefore,
  required DateTime notAfter,
  bool isCa = false,
  List<String>? crlUrls,
  List<String>? ocspUrls,
  List<PkiOtherName>? subjectAltNameOtherNames,
  List<String>? extendedKeyUsageOids,
  int? keyUsageBits,
}) {
  // 1. Create TBSCertificate
  final tbs = ASN1Sequence();

  // Version (v3 = 2) - [0] EXPLICIT wrapping INTEGER 2
  final versionWrapper = ASN1Sequence(tag: 0xA0);
  versionWrapper.add(ASN1Integer(BigInt.from(2)));
  tbs.add(versionWrapper);

  // Serial Number
  final BigInt serial = serialNumberBigInt ?? BigInt.from(serialNumber);
  tbs.add(ASN1Integer(serial));

  // Algorithm ID
  tbs.add(createAlgorithmIdentifier(sha256WithRSAEncryption));

  // Issuer
  tbs.add(createName(issuerDn));

  // Validity
  final validity = ASN1Sequence();
  validity.add(_encodeTime(notBefore));
  validity.add(_encodeTime(notAfter));
  tbs.add(validity);

  // Subject
  tbs.add(createName(subjectDn));

  // Subject Public Key Info
  tbs.add(createSubjectPublicKeyInfo(keyPair.publicKey as RSAPublicKey));

  // Extensions
  final extensions = ASN1Sequence();

  // Basic Constraints
  extensions.add(createExtension(
    oidBasicConstraints,
    createBasicConstraints(isCa),
    critical: true,
  ));

  // Key Usage
  extensions.add(createExtension(
    oidKeyUsage,
    createKeyUsage(isCa, keyUsageBits: keyUsageBits),
    critical: true,
  ));

  // Subject Key Identifier (SKID)
  final subjectKeyBytes =
      _encodePublicKeyInfo(keyPair.publicKey as RSAPublicKey);
  final subjectKeyId = _calculateSha1(subjectKeyBytes);
  extensions.add(createExtension(
    oidSubjectKeyIdentifier,
    ASN1OctetString(subjectKeyId),
  ));

  // Authority Key Identifier (AKID)
  // RFC 5280: The authority key identifier extension ... MUST be present in all certificates
  // ... EXCEPT ... self-signed CA certificates.
  if (subjectDn != issuerDn) {
    final issuerKeyBytes =
        _encodePublicKeyInfo(issuerKeyPair.publicKey as RSAPublicKey);
    final issuerKeyId = _calculateSha1(issuerKeyBytes);

    final akiSeq = ASN1Sequence();
    // keyIdentifier [0] IMPLICIT KeyIdentifier
    // KeyIdentifier is OCTET STRING. Implicit tag replaces it with [0] (0x80).
    final keyIdOctet = ASN1OctetString(issuerKeyId, tag: 0x80);
    akiSeq.add(keyIdOctet);

    extensions.add(createExtension(
      oidAuthorityKeyIdentifier,
      akiSeq,
    ));
  }

  if (crlUrls != null && crlUrls.isNotEmpty) {
    extensions.add(createExtension(
      oidCrlDistributionPoints,
      createCrlDistributionPoints(crlUrls),
    ));
  }

  if (ocspUrls != null && ocspUrls.isNotEmpty) {
    extensions.add(createExtension(
      oidAuthorityInfoAccess,
      _createAuthorityInfoAccess(ocspUrls),
    ));
  }

  if (subjectAltNameOtherNames != null &&
      subjectAltNameOtherNames.isNotEmpty) {
    extensions.add(createExtension(
      oidSubjectAltName,
      createSubjectAltName(subjectAltNameOtherNames),
    ));
  }

  if (extendedKeyUsageOids != null && extendedKeyUsageOids.isNotEmpty) {
    extensions.add(createExtension(
      oidExtKeyUsage,
      createExtendedKeyUsage(extendedKeyUsageOids),
    ));
  }

  // Wrap extensions in [3] Explicit
  final extWrapper = ASN1Sequence(tag: 0xA3);
  extWrapper.add(extensions);
  tbs.add(extWrapper);

  // 2. Sign
  final signature =
      signData(tbs.encodedBytes, issuerKeyPair.privateKey as RSAPrivateKey);

  // 3. Assemble Certificate
  final cert = ASN1Sequence();
  cert.add(tbs);
  cert.add(createAlgorithmIdentifier(sha256WithRSAEncryption));
  cert.add(ASN1BitString(signature));

  return X509Certificate.fromDer(cert.encodedBytes);
}