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