in crypto/pkcs8/pkcs8_x509.c [1157:1361]
const STACK_OF(X509)* chain, int key_nid, int cert_nid,
int iterations, int mac_iterations, int key_type) {
if (key_nid == 0) {
key_nid = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
}
if (cert_nid == 0) {
cert_nid = NID_pbe_WithSHA1And40BitRC2_CBC;
}
if (iterations == 0) {
iterations = PKCS12_DEFAULT_ITER;
}
if (mac_iterations == 0) {
mac_iterations = 1;
}
if (// In OpenSSL, this specifies a non-standard Microsoft key usage extension
// which we do not currently support.
key_type != 0 ||
// In OpenSSL, -1 here means to omit the MAC, which we do not
// currently support. Omitting it is also invalid for a password-based
// PKCS#12 file.
mac_iterations < 0 ||
// Don't encode empty objects.
(pkey == NULL && cert == NULL && sk_X509_num(chain) == 0)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_OPTIONS);
return 0;
}
// PKCS#12 is a very confusing recursive data format, built out of another
// recursive data format. Section 5.1 of RFC 7292 describes the encoding
// algorithm, but there is no clear overview. A quick summary:
//
// PKCS#7 defines a ContentInfo structure, which is a overgeneralized typed
// combinator structure for applying cryptography. We care about two types. A
// data ContentInfo contains an OCTET STRING and is a leaf node of the
// combinator tree. An encrypted-data ContentInfo contains encryption
// parameters (key derivation and encryption) and wraps another ContentInfo,
// usually data.
//
// A PKCS#12 file is a PFX structure (section 4), which contains a single data
// ContentInfo and a MAC over it. This root ContentInfo is the
// AuthenticatedSafe and its payload is a SEQUENCE of other ContentInfos, so
// that different parts of the PKCS#12 file can by differently protected.
//
// Each ContentInfo in the AuthenticatedSafe, after undoing all the PKCS#7
// combinators, has SafeContents payload. A SafeContents is a SEQUENCE of
// SafeBag. SafeBag is PKCS#12's typed structure, with subtypes such as KeyBag
// and CertBag. Confusingly, there is a SafeContents bag type which itself
// recursively contains more SafeBags, but we do not implement this. Bags also
// can have attributes.
//
// The grouping of SafeBags into intermediate ContentInfos does not appear to
// be significant, except that all SafeBags sharing a ContentInfo have the
// same level of protection. Additionally, while keys may be encrypted by
// placing a KeyBag in an encrypted-data ContentInfo, PKCS#12 also defines a
// key-specific encryption container, PKCS8ShroudedKeyBag, which is used
// instead.
// Note that |password| may be NULL to specify no password, rather than the
// empty string. They are encoded differently in PKCS#12. (One is the empty
// byte array and the other is NUL-terminated UCS-2.)
size_t password_len = password != NULL ? strlen(password) : 0;
uint8_t key_id[EVP_MAX_MD_SIZE];
unsigned key_id_len = 0;
if (cert != NULL && pkey != NULL) {
if (!X509_check_private_key(cert, pkey) ||
// Matching OpenSSL, use the SHA-1 hash of the certificate as the local
// key ID. Some PKCS#12 consumers require one to connect the private key
// and certificate.
!X509_digest(cert, EVP_sha1(), key_id, &key_id_len)) {
return 0;
}
}
// See https://tools.ietf.org/html/rfc7292#section-4.
PKCS12 *ret = NULL;
CBB cbb, pfx, auth_safe, auth_safe_oid, auth_safe_wrapper, auth_safe_data,
content_infos;
if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &pfx, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1_uint64(&pfx, 3) ||
// auth_safe is a data ContentInfo.
!CBB_add_asn1(&pfx, &auth_safe, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&auth_safe, &auth_safe_oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&auth_safe_oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
!CBB_add_asn1(&auth_safe, &auth_safe_wrapper,
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
!CBB_add_asn1(&auth_safe_wrapper, &auth_safe_data,
CBS_ASN1_OCTETSTRING) ||
// See https://tools.ietf.org/html/rfc7292#section-4.1. |auth_safe|'s
// contains a SEQUENCE of ContentInfos.
!CBB_add_asn1(&auth_safe_data, &content_infos, CBS_ASN1_SEQUENCE)) {
goto err;
}
// If there are any certificates, place them in CertBags wrapped in a single
// encrypted ContentInfo.
if (cert != NULL || sk_X509_num(chain) > 0) {
if (cert_nid < 0) {
// Place the certificates in an unencrypted ContentInfo. This could be
// more compactly-encoded by reusing the same ContentInfo as the key, but
// OpenSSL does not do this. We keep them separate for consistency. (Keys,
// even when encrypted, are always placed in unencrypted ContentInfos.
// PKCS#12 defines bag-level encryption for keys.)
CBB content_info, oid, wrapper, data;
if (!CBB_add_asn1(&content_infos, &content_info, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
!CBB_add_asn1(&content_info, &wrapper,
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
!CBB_add_asn1(&wrapper, &data, CBS_ASN1_OCTETSTRING) ||
!add_cert_safe_contents(&data, cert, chain, name, key_id,
key_id_len) ||
!CBB_flush(&content_infos)) {
goto err;
}
} else {
CBB plaintext_cbb;
int ok = CBB_init(&plaintext_cbb, 0) &&
add_cert_safe_contents(&plaintext_cbb, cert, chain, name, key_id,
key_id_len) &&
add_encrypted_data(
&content_infos, cert_nid, password, password_len, iterations,
CBB_data(&plaintext_cbb), CBB_len(&plaintext_cbb));
CBB_cleanup(&plaintext_cbb);
if (!ok) {
goto err;
}
}
}
// If there is a key, place it in a single KeyBag or PKCS8ShroudedKeyBag
// wrapped in an unencrypted ContentInfo. (One could also place it in a KeyBag
// inside an encrypted ContentInfo, but OpenSSL does not do this and some
// PKCS#12 consumers do not support KeyBags.)
if (pkey != NULL) {
CBB content_info, oid, wrapper, data, safe_contents, bag, bag_oid,
bag_contents;
if (// Add another data ContentInfo.
!CBB_add_asn1(&content_infos, &content_info, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
!CBB_add_asn1(&content_info, &wrapper,
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
!CBB_add_asn1(&wrapper, &data, CBS_ASN1_OCTETSTRING) ||
!CBB_add_asn1(&data, &safe_contents, CBS_ASN1_SEQUENCE) ||
// Add a SafeBag containing a PKCS8ShroudedKeyBag.
!CBB_add_asn1(&safe_contents, &bag, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&bag, &bag_oid, CBS_ASN1_OBJECT)) {
goto err;
}
if (key_nid < 0) {
if (!CBB_add_bytes(&bag_oid, kKeyBag, sizeof(kKeyBag)) ||
!CBB_add_asn1(&bag, &bag_contents,
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
!EVP_marshal_private_key(&bag_contents, pkey)) {
goto err;
}
} else {
if (!CBB_add_bytes(&bag_oid, kPKCS8ShroudedKeyBag,
sizeof(kPKCS8ShroudedKeyBag)) ||
!CBB_add_asn1(&bag, &bag_contents,
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
!PKCS8_marshal_encrypted_private_key(
&bag_contents, key_nid, NULL, password, password_len,
NULL /* generate a random salt */,
0 /* use default salt length */, iterations, pkey)) {
goto err;
}
}
size_t name_len = 0;
if (name) {
name_len = strlen(name);
}
if (!add_bag_attributes(&bag, name, name_len, key_id, key_id_len) ||
!CBB_flush(&content_infos)) {
goto err;
}
}
// Compute the MAC. Match OpenSSL in using SHA-1 as the hash function. The MAC
// covers |auth_safe_data|.
// TODO (CryptoAlg-2897): Update the default |md| to SHA-256 to align with
// OpenSSL 3.x.
const EVP_MD *mac_md = EVP_sha1();
uint8_t mac_salt[PKCS5_SALT_LEN];
if (!CBB_flush(&auth_safe_data) ||
!RAND_bytes(mac_salt, sizeof(mac_salt)) ||
!pkcs12_gen_and_write_mac(
&pfx, CBB_data(&auth_safe_data), CBB_len(&auth_safe_data), password,
password_len, mac_salt, sizeof(mac_salt), mac_iterations, mac_md)) {
goto err;
}
ret = PKCS12_new();
if (ret == NULL ||
!CBB_finish(&cbb, &ret->ber_bytes, &ret->ber_len)) {
OPENSSL_free(ret);
ret = NULL;
goto err;
}
err:
CBB_cleanup(&cbb);
return ret;
}