generatePkcs12 static method

Uint8List generatePkcs12(
  1. String privateKey,
  2. List<String> certificates, {
  3. String? password,
  4. String keyPbe = 'PBE-SHA1-3DES',
  5. String certPbe = 'PBE-SHA1-RC2-40',
  6. String digestAlgorithm = 'SHA-1',
  7. int macIter = 2048,
  8. Uint8List? salt,
  9. Uint8List? certSalt,
  10. Uint8List? keySalt,
  11. String? friendlyName,
  12. Uint8List? localKeyId,
})

Generates a PKCS12 file according to RFC 7292.

  • privateKey = A private key in PEM format.
  • certificates = A list of certificates in PEM format.
  • password = The password used for encryption.
  • keyPbe = The encryption algorithm used to encrypt the private key.
  • certPbe = The encryption algorithm used to encrypt the certificates.
  • digetAlgorithm = The digest algorithm used for the mac key derivation
  • macIter = The iteration count for the key derivation
  • salt = The salt used for the key derivation, if left out, it will be generated
  • certSalt = The salt used for the key derivation for cert encryption, if left out salt will be used.
  • keySalt = The salt used for the key derivation for key encryption, if left out salt will be used.
  • friendlyName = The name to be used to place as an attribue.
  • localKeyId = The id to be used to place as an attribue. If left, it will be generated.

Possible values for keyPbe and certPbe:

  • PBE-SHA1-RC4-128
  • PBE-SHA1-RC4-40
  • PBE-SHA1-3DES ( default for keyPbe )
  • PBE-SHA1-2DES
  • PBE-SHA1-RC2-128
  • PBE-SHA1-RC2-40 ( default for certPbe)

Possible values for digestAlgorithm:

  • SHA-1 ( DEFAULT)
  • SHA-224
  • SHA-256
  • SHA-384
  • SHA-512

IMPORTANT: This method generates a PKCS12 file that only supports PASSWORD PRIVACY and PASSWORD INTEGRITY mode. This means that the private key and certificates are encrypted with the given password and the HMAC is generated using the given password.

If keyPbe or certPbe are set to NONE or the password is left out, there will be no encryption. If the password is left out, no HMAC is generated

Implementation

static Uint8List generatePkcs12(
  String privateKey,
  List<String> certificates, {
  String? password,
  String keyPbe = 'PBE-SHA1-3DES',
  String certPbe = 'PBE-SHA1-RC2-40',
  String digestAlgorithm = 'SHA-1',
  int macIter = 2048,
  Uint8List? salt,
  Uint8List? certSalt,
  Uint8List? keySalt,
  String? friendlyName,
  Uint8List? localKeyId,
}) {
  Uint8List? pwFormatted;
  if (password != null) {
    pwFormatted =
        formatPkcs12Password(Uint8List.fromList(password.codeUnits));
  }

  // GENERATE SALT
  if (salt == null) {
    salt = _generateSalt();
  }

  if (certSalt == null) {
    certSalt = salt;
  }

  if (keySalt == null) {
    keySalt = salt;
  }

  // GENERATE LOCAL KEY ID
  if (localKeyId == null) {
    localKeyId = _generateLocalKeyId();
  }

  // CREATE SAFEBAGS WITH PEMS WRAPPED IN CERTBAG
  var safeBags = _generateSafeBagsForCerts(certificates, localKeyId,
      friendlyName: friendlyName);
  var safeContentsCert = ASN1SafeContents(safeBags);

  // CREATE CONTENT INFO
  var contentInfoCert;
  var contentInfoKey;
  if (certPbe != 'NONE' && pwFormatted != null) {
    var params = ASN1Sequence(
      elements: [
        ASN1OctetString(octets: certSalt),
        ASN1Integer(
          BigInt.from(macIter),
        ),
      ],
    );
    var contentEncryptionAlgorithm = ASN1AlgorithmIdentifier(
      _oiFromAlgorithm(certPbe),
      parameters: params,
    );

    Uint8List encryptedContent = _encrypt(
      safeContentsCert.encode(),
      certPbe,
      pwFormatted,
      certSalt,
      macIter,
      'SHA-1',
    );

    var encryptedContentInfo = ASN1EncryptedContentInfo.forData(
        contentEncryptionAlgorithm, encryptedContent);

    var encryptedData = ASN1EncryptedData(encryptedContentInfo);
    contentInfoCert = ASN1ContentInfo.forEncryptedData(encryptedData);
  } else {
    contentInfoCert = ASN1ContentInfo.forData(
      ASN1OctetString(
        octets: safeContentsCert.encode(),
      ),
    );
  }
  if (keyPbe != 'NONE' && pwFormatted != null) {
    var params = ASN1Sequence(elements: [
      ASN1OctetString(octets: keySalt),
      ASN1Integer(BigInt.from(macIter)),
    ]);
    var contentEncryptionAlgorithm = ASN1AlgorithmIdentifier(
      _oiFromAlgorithm(keyPbe),
      parameters: params,
    );
    var privateKeyInfo = _getPrivateKeyInfoFromPem(privateKey);
    Uint8List encryptedContent = _encrypt(
      privateKeyInfo.encode(),
      keyPbe,
      pwFormatted,
      keySalt,
      macIter,
      'SHA-1',
    );

    // CREATE SAFEBAG FOR PRIVATEKEY WRAPPED IN KEYBAG
    var safeBagsKey = _generateSafeBagsForShroudedKey(
      ASN1Sequence(elements: [
        contentEncryptionAlgorithm,
        ASN1OctetString(octets: encryptedContent)
      ]),
      localKeyId,
      friendlyName: friendlyName,
    );

    var safeContentsKey = ASN1SafeContents(safeBagsKey);
    contentInfoKey = ASN1ContentInfo.forData(
      ASN1OctetString(
        octets: safeContentsKey.encode(),
      ),
    );
  } else {
    // CREATE SAFEBAG FOR PRIVATEKEY WRAPPED IN KEYBAG
    var safeBagsKey = _generateSafeBagsForKey(
      privateKey,
      localKeyId,
      friendlyName: friendlyName,
    );

    var safeContentsKey = ASN1SafeContents(safeBagsKey);

    contentInfoKey = ASN1ContentInfo.forData(
      ASN1OctetString(
        octets: safeContentsKey.encode(),
      ),
    );
  }

  // CREATE AUTHENTICATED SAFE WITH CONTENTINFO ( CERT AND KEY )
  var authSafe = ASN1AuthenticatedSafe([contentInfoCert, contentInfoKey]);

  // WRAP AUTHENTICATED SAFE WITHIN A CONTENTINFO
  var T = ASN1ContentInfo.forData(
    ASN1OctetString(
      octets: authSafe.encode(),
    ),
  );

  // GENERATE HMAC IF PASSWORD IS GIVEN
  ASN1MacData? macData;
  if (password != null) {
    var bytesForHmac = authSafe.encode();

    var pwFormatted =
        formatPkcs12Password(Uint8List.fromList(password.codeUnits));

    var generator = PKCS12ParametersGenerator(Digest(digestAlgorithm));
    generator.init(pwFormatted, salt, macIter);

    var key = generator.generateDerivedMacParameters(20);
    var m = _generateHmac(bytesForHmac, key.key, digestAlgorithm);
    macData = ASN1MacData(
      ASN1DigestInfo(
        m,
        _algorithmIdentifierFromDigest(
          digestAlgorithm,
        ),
      ),
      salt,
      BigInt.from(2048),
    );
  }
  var pfx = ASN1Pfx(
    ASN1Integer(BigInt.from(3)),
    T,
    macData: macData,
  );
  var bytes = pfx.encode();
  return bytes;
}