Extract friendly names attached to certificates.
OpenSSL staples each certificate's friendly name to the X509 with
X509_alias_set1. Mimic this. pyOpenSSL expects to find it there.
Update-Note: We actually parse some attributes now. PKCS#12 files with
malformed ones may not parse.
Change-Id: I3b78958eedf195509cd222ea4f0c884be3753770
Reviewed-on: https://boringssl-review.googlesource.com/28551
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/pkcs8/pkcs12_test.cc b/crypto/pkcs8/pkcs12_test.cc
index d1d09fa..5fb7903 100644
--- a/crypto/pkcs8/pkcs12_test.cc
+++ b/crypto/pkcs8/pkcs12_test.cc
@@ -1165,7 +1165,8 @@
static const char kUnicodePassword[] = u8"Hello, 世界";
static void TestImpl(const char *name, bssl::Span<const uint8_t> der,
- const char *password) {
+ const char *password,
+ const char *friendly_name) {
SCOPED_TRACE(name);
bssl::UniquePtr<STACK_OF(X509)> certs(sk_X509_new_null());
ASSERT_TRUE(certs);
@@ -1177,6 +1178,17 @@
ASSERT_EQ(1u, sk_X509_num(certs.get()));
ASSERT_TRUE(key);
+
+ int actual_name_len;
+ const uint8_t *actual_name =
+ X509_alias_get0(sk_X509_value(certs.get(), 0), &actual_name_len);
+ if (friendly_name == nullptr) {
+ EXPECT_EQ(nullptr, actual_name);
+ } else {
+ EXPECT_EQ(friendly_name,
+ std::string(reinterpret_cast<const char *>(actual_name),
+ static_cast<size_t>(actual_name_len)));
+ }
}
static void TestCompat(bssl::Span<const uint8_t> der) {
@@ -1204,33 +1216,35 @@
}
TEST(PKCS12Test, TestOpenSSL) {
- TestImpl("OpenSSL", kOpenSSL, kPassword);
+ TestImpl("OpenSSL", kOpenSSL, kPassword, nullptr);
}
TEST(PKCS12Test, TestNSS) {
- TestImpl("NSS", kNSS, kPassword);
+ TestImpl("NSS", kNSS, kPassword, "Internet Widgits Pty Ltd");
}
TEST(PKCS12Test, TestWindows) {
- TestImpl("Windows", kWindows, kPassword);
+ // kWindows has a friendlyName, but only on the key, where we ignore it, and
+ // not the certificate.
+ TestImpl("Windows", kWindows, kPassword, nullptr);
}
TEST(PKCS12Test, TestPBES2) {
- TestImpl("PBES2", kPBES2, kPassword);
+ TestImpl("PBES2", kPBES2, kPassword, nullptr);
}
TEST(PKCS12Test, TestEmptyPassword) {
- TestImpl("EmptyPassword (empty password)", kEmptyPassword, "");
- TestImpl("EmptyPassword (null password)", kEmptyPassword, nullptr);
+ TestImpl("EmptyPassword (empty password)", kEmptyPassword, "", nullptr);
+ TestImpl("EmptyPassword (null password)", kEmptyPassword, nullptr, nullptr);
}
TEST(PKCS12Test, TestNullPassword) {
- TestImpl("NullPassword (empty password)", kNullPassword, "");
- TestImpl("NullPassword (null password)", kNullPassword, nullptr);
+ TestImpl("NullPassword (empty password)", kNullPassword, "", nullptr);
+ TestImpl("NullPassword (null password)", kNullPassword, nullptr, nullptr);
}
TEST(PKCS12Test, TestUnicode) {
- TestImpl("Unicode", kUnicode, kUnicodePassword);
+ TestImpl("Unicode", kUnicode, kUnicodePassword, nullptr);
}
TEST(PKCS12Test, TestWindowsCompat) {
@@ -1419,6 +1433,17 @@
EXPECT_EQ(0, X509_cmp(sk_X509_value(chain.get(), i),
sk_X509_value(certs2.get(), i + offset)));
}
+ if (sk_X509_num(certs2.get()) > 0) {
+ int actual_name_len;
+ const uint8_t *actual_name =
+ X509_alias_get0(sk_X509_value(certs2.get(), 0), &actual_name_len);
+ if (name == NULL) {
+ EXPECT_EQ(nullptr, actual_name);
+ } else {
+ EXPECT_EQ(name, std::string(reinterpret_cast<const char *>(actual_name),
+ static_cast<size_t>(actual_name_len)));
+ }
+ }
// Check that writing to a |BIO| does the same thing.
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
diff --git a/crypto/pkcs8/pkcs8_x509.c b/crypto/pkcs8/pkcs8_x509.c
index 24aaa27..3cdbddb 100644
--- a/crypto/pkcs8/pkcs8_x509.c
+++ b/crypto/pkcs8/pkcs8_x509.c
@@ -293,18 +293,93 @@
static const uint8_t kCertBag[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x0c, 0x0a, 0x01, 0x03};
+// 1.2.840.113549.1.9.20
+static const uint8_t kFriendlyName[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x09, 0x14};
+
+// 1.2.840.113549.1.9.21
+static const uint8_t kLocalKeyID[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x09, 0x15};
+
// 1.2.840.113549.1.9.22.1
static const uint8_t kX509Certificate[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x09, 0x16, 0x01};
+// parse_bag_attributes parses the bagAttributes field of a SafeBag structure.
+// It sets |*out_friendly_name| to a newly-allocated copy of the friendly name,
+// encoded as a UTF-8 string, or NULL if there is none. It returns one on
+// success and zero on error.
+static int parse_bag_attributes(CBS *attrs, uint8_t **out_friendly_name,
+ size_t *out_friendly_name_len) {
+ *out_friendly_name = NULL;
+ *out_friendly_name_len = 0;
+
+ // See https://tools.ietf.org/html/rfc7292#section-4.2.
+ while (CBS_len(attrs) != 0) {
+ CBS attr, oid, values;
+ if (!CBS_get_asn1(attrs, &attr, CBS_ASN1_SEQUENCE) ||
+ !CBS_get_asn1(&attr, &oid, CBS_ASN1_OBJECT) ||
+ !CBS_get_asn1(&attr, &values, CBS_ASN1_SET) ||
+ CBS_len(&attr) != 0) {
+ OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+ goto err;
+ }
+ if (CBS_mem_equal(&oid, kFriendlyName, sizeof(kFriendlyName))) {
+ // See https://tools.ietf.org/html/rfc2985, section 5.5.1.
+ CBS value;
+ if (*out_friendly_name != NULL ||
+ !CBS_get_asn1(&values, &value, CBS_ASN1_BMPSTRING) ||
+ CBS_len(&values) != 0 ||
+ CBS_len(&value) == 0) {
+ OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+ goto err;
+ }
+ // Convert the friendly name to UTF-8.
+ CBB cbb;
+ if (!CBB_init(&cbb, CBS_len(&value))) {
+ OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ while (CBS_len(&value) != 0) {
+ uint32_t c;
+ if (!cbs_get_ucs2_be(&value, &c) ||
+ !cbb_add_utf8(&cbb, c)) {
+ OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_INVALID_CHARACTERS);
+ CBB_cleanup(&cbb);
+ goto err;
+ }
+ }
+ if (!CBB_finish(&cbb, out_friendly_name, out_friendly_name_len)) {
+ OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
+ CBB_cleanup(&cbb);
+ goto err;
+ }
+ }
+ }
+
+ return 1;
+
+err:
+ OPENSSL_free(*out_friendly_name);
+ *out_friendly_name = NULL;
+ *out_friendly_name_len = 0;
+ return 0;
+}
+
// PKCS12_handle_safe_bag parses a single SafeBag element in a PKCS#12
// structure.
static int PKCS12_handle_safe_bag(CBS *safe_bag, struct pkcs12_context *ctx) {
- CBS bag_id, wrapped_value;
+ CBS bag_id, wrapped_value, bag_attrs;
if (!CBS_get_asn1(safe_bag, &bag_id, CBS_ASN1_OBJECT) ||
!CBS_get_asn1(safe_bag, &wrapped_value,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)
- /* Ignore the bagAttributes field. */) {
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
+ OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+ return 0;
+ }
+ if (CBS_len(safe_bag) == 0) {
+ CBS_init(&bag_attrs, NULL, 0);
+ } else if (!CBS_get_asn1(safe_bag, &bag_attrs, CBS_ASN1_SET) ||
+ CBS_len(safe_bag) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
return 0;
}
@@ -369,7 +444,17 @@
return 0;
}
- if (0 == sk_X509_push(ctx->out_certs, x509)) {
+ uint8_t *friendly_name;
+ size_t friendly_name_len;
+ if (!parse_bag_attributes(&bag_attrs, &friendly_name, &friendly_name_len)) {
+ X509_free(x509);
+ return 0;
+ }
+ int ok = friendly_name_len == 0 ||
+ X509_alias_set1(x509, friendly_name, friendly_name_len);
+ OPENSSL_free(friendly_name);
+ if (!ok ||
+ 0 == sk_X509_push(ctx->out_certs, x509)) {
X509_free(x509);
return 0;
}
@@ -861,14 +946,6 @@
return 1;
}
-// 1.2.840.113549.1.9.20
-static const uint8_t kFriendlyName[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x09, 0x14};
-
-// 1.2.840.113549.1.9.21
-static const uint8_t kLocalKeyID[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x01, 0x09, 0x15};
-
// 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,