Use X509 certificate alias as friendlyName in PKCS12
Bug: 481
Change-Id: I5c1dd6e39874cd1e88cb6fd0b3a6c6c6fac0db85
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/51665
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/err/pkcs8.errordata b/crypto/err/pkcs8.errordata
index 9aac7e2..02a3fe3 100644
--- a/crypto/err/pkcs8.errordata
+++ b/crypto/err/pkcs8.errordata
@@ -1,3 +1,4 @@
+PKCS8,133,AMBIGUOUS_FRIENDLY_NAME
PKCS8,129,BAD_ITERATION_COUNT
PKCS8,100,BAD_PKCS12_DATA
PKCS8,101,BAD_PKCS12_VERSION
diff --git a/crypto/pkcs8/pkcs12_test.cc b/crypto/pkcs8/pkcs12_test.cc
index 958bd8d..79ebae3 100644
--- a/crypto/pkcs8/pkcs12_test.cc
+++ b/crypto/pkcs8/pkcs12_test.cc
@@ -599,3 +599,45 @@
ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(), {cert2.get(), cert3.get()}));
ExpectPKCS12Parse(p12, key1.get(), nullptr, {cert2.get(), cert3.get()});
}
+
+TEST(PKCS12Test, CreateWithAlias) {
+ bssl::UniquePtr<EVP_PKEY> key = MakeTestKey();
+ ASSERT_TRUE(key);
+ bssl::UniquePtr<X509> cert1 = MakeTestCert(key.get());
+ ASSERT_TRUE(cert1);
+ bssl::UniquePtr<X509> cert2 = MakeTestCert(key.get());
+ ASSERT_TRUE(cert2);
+
+ std::string alias = "I'm an alias";
+ int res = X509_alias_set1(
+ cert1.get(), reinterpret_cast<const unsigned char *>(alias.data()),
+ alias.size());
+ ASSERT_EQ(res, 1);
+
+ std::vector<X509 *> certs = {cert1.get(), cert2.get()};
+ std::vector<uint8_t> der;
+ ASSERT_TRUE(PKCS12CreateVector(&der, key.get(), certs));
+
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(der.data(), der.size()));
+ ASSERT_TRUE(bio);
+ bssl::UniquePtr<PKCS12> p12(d2i_PKCS12_bio(bio.get(), nullptr));
+ ASSERT_TRUE(p12);
+
+ EVP_PKEY *parsed_key = nullptr;
+ X509 *parsed_cert = nullptr;
+ STACK_OF(X509) *ca_certs = nullptr;
+ ASSERT_TRUE(
+ PKCS12_parse(p12.get(), kPassword, &parsed_key, &parsed_cert, &ca_certs));
+
+ bssl::UniquePtr<EVP_PKEY> delete_key(parsed_key);
+ bssl::UniquePtr<X509> delete_cert(parsed_cert);
+ bssl::UniquePtr<STACK_OF(X509)> delete_ca_certs(ca_certs);
+ ASSERT_EQ(sk_X509_num(ca_certs), 1UL);
+
+ int alias_len = 0;
+ const unsigned char *parsed_alias =
+ X509_alias_get0(sk_X509_value(ca_certs, 0), &alias_len);
+ ASSERT_TRUE(parsed_alias);
+ ASSERT_EQ(alias, std::string(reinterpret_cast<const char *>(parsed_alias),
+ static_cast<size_t>(alias_len)));
+}
diff --git a/crypto/pkcs8/pkcs8_x509.c b/crypto/pkcs8/pkcs8_x509.c
index e2a02e8..1ea83e7 100644
--- a/crypto/pkcs8/pkcs8_x509.c
+++ b/crypto/pkcs8/pkcs8_x509.c
@@ -993,8 +993,8 @@
// add_bag_attributes adds the bagAttributes field of a SafeBag structure,
// containing the specified friendlyName and localKeyId attributes.
-static int add_bag_attributes(CBB *bag, const char *name, const uint8_t *key_id,
- size_t key_id_len) {
+static int add_bag_attributes(CBB *bag, const char *name, size_t name_len,
+ const uint8_t *key_id, size_t key_id_len) {
if (name == NULL && key_id_len == 0) {
return 1; // Omit the OPTIONAL SET.
}
@@ -1003,7 +1003,7 @@
if (!CBB_add_asn1(bag, &attrs, CBS_ASN1_SET)) {
return 0;
}
- if (name != NULL) {
+ if (name_len != 0) {
// See https://tools.ietf.org/html/rfc2985, section 5.5.1.
if (!CBB_add_asn1(&attrs, &attr, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&attr, &oid, CBS_ASN1_OBJECT) ||
@@ -1014,7 +1014,7 @@
}
// Convert the friendly name to a BMPString.
CBS name_cbs;
- CBS_init(&name_cbs, (const uint8_t *)name, strlen(name));
+ CBS_init(&name_cbs, (const uint8_t *)name, name_len);
while (CBS_len(&name_cbs) != 0) {
uint32_t c;
if (!cbs_get_utf8(&name_cbs, &c) ||
@@ -1059,10 +1059,24 @@
}
uint8_t *buf;
int len = i2d_X509(cert, NULL);
+
+ int int_name_len = 0;
+ const char *cert_name = (const char *)X509_alias_get0(cert, &int_name_len);
+ size_t name_len = int_name_len;
+ if (name) {
+ if (name_len != 0) {
+ OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_AMBIGUOUS_FRIENDLY_NAME);
+ return 0;
+ }
+ name_len = strlen(name);
+ } else {
+ name = cert_name;
+ }
+
if (len < 0 ||
!CBB_add_space(&cert_value, &buf, (size_t)len) ||
i2d_X509(cert, &buf) < 0 ||
- !add_bag_attributes(&bag, name, key_id, key_id_len) ||
+ !add_bag_attributes(&bag, name, name_len, key_id, key_id_len) ||
!CBB_flush(cbb)) {
return 0;
}
@@ -1323,7 +1337,11 @@
goto err;
}
}
- if (!add_bag_attributes(&bag, name, key_id, key_id_len) ||
+ 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;
}
diff --git a/include/openssl/pkcs8.h b/include/openssl/pkcs8.h
index 968640b..8774681 100644
--- a/include/openssl/pkcs8.h
+++ b/include/openssl/pkcs8.h
@@ -122,6 +122,8 @@
// and decrypts it using |password|, sets |*out_key| to the included private
// key and appends the included certificates to |out_certs|. It returns one on
// success and zero on error. The caller takes ownership of the outputs.
+// Any friendlyName attributes (RFC 2985) in the PKCS#12 structure will be
+// returned on the |X509| objects as aliases. See also |X509_alias_get0|.
OPENSSL_EXPORT int PKCS12_get_key_and_certs(EVP_PKEY **out_key,
STACK_OF(X509) *out_certs,
CBS *in, const char *password);
@@ -219,6 +221,11 @@
// implemented for compatibility with external packages. Note the output still
// requires a password for the MAC. Unencrypted keys in PKCS#12 are also not
// widely supported and may not open in other implementations.
+//
+// If |cert| or |chain| have associated aliases (see |X509_alias_set1|), they
+// will be included in the output as friendlyName attributes (RFC 2985). It is
+// an error to specify both an alias on |cert| and a non-NULL |name|
+// parameter.
OPENSSL_EXPORT PKCS12 *PKCS12_create(const char *password, const char *name,
const EVP_PKEY *pkey, X509 *cert,
const STACK_OF(X509) *chain, int key_nid,
@@ -278,5 +285,6 @@
#define PKCS8_R_UNSUPPORTED_PRF 130
#define PKCS8_R_INVALID_CHARACTERS 131
#define PKCS8_R_UNSUPPORTED_OPTIONS 132
+#define PKCS8_R_AMBIGUOUS_FRIENDLY_NAME 133
#endif // OPENSSL_HEADER_PKCS8_H