summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorsnej@chromium.org <snej@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-26 21:37:54 +0000
committersnej@chromium.org <snej@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-26 21:37:54 +0000
commitd3002098f8982ac54901ce9fdc8e81cdb725d8e6 (patch)
tree4cd95eb31701c7b27e0518d46f1ae0d6a62d05ab /net
parentaf3719864762ddf3e061ba24129d054e3a722930 (diff)
downloadchromium_src-d3002098f8982ac54901ce9fdc8e81cdb725d8e6.zip
chromium_src-d3002098f8982ac54901ce9fdc8e81cdb725d8e6.tar.gz
chromium_src-d3002098f8982ac54901ce9fdc8e81cdb725d8e6.tar.bz2
Mac: Make client-cert picker only show certs the server will accept.
BUG=38691 TEST=manual testing with various sites Review URL: http://codereview.chromium.org/1128008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42822 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/x509_cert_types.cc112
-rw-r--r--net/base/x509_cert_types.h133
-rw-r--r--net/base/x509_cert_types_mac.cc293
-rw-r--r--net/base/x509_cert_types_unittest.cc343
-rw-r--r--net/base/x509_certificate.cc49
-rw-r--r--net/base/x509_certificate.h96
-rw-r--r--net/base/x509_certificate_mac.cc198
-rw-r--r--net/net.gyp4
-rw-r--r--net/socket/ssl_client_socket_mac.cc29
9 files changed, 1022 insertions, 235 deletions
diff --git a/net/base/x509_cert_types.cc b/net/base/x509_cert_types.cc
new file mode 100644
index 0000000..8329216
--- /dev/null
+++ b/net/base/x509_cert_types.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/x509_cert_types.h"
+
+#include "net/base/x509_certificate.h"
+#include "base/logging.h"
+
+namespace net {
+
+bool match(const std::string &str, const std::string &against) {
+ // TODO(snej): Use the full matching rules specified in RFC 5280 sec. 7.1
+ // including trimming and case-folding: <http://www.ietf.org/rfc/rfc5280.txt>.
+ return against == str;
+}
+
+bool match(const std::vector<std::string> &rdn1,
+ const std::vector<std::string> &rdn2) {
+ // "Two relative distinguished names RDN1 and RDN2 match if they have the
+ // same number of naming attributes and for each naming attribute in RDN1
+ // there is a matching naming attribute in RDN2." --RFC 5280 sec. 7.1.
+ if (rdn1.size() != rdn2.size())
+ return false;
+ for (unsigned i1 = 0; i1 < rdn1.size(); ++i1) {
+ unsigned i2;
+ for (i2 = 0; i2 < rdn2.size(); ++i2) {
+ if (match(rdn1[i1], rdn2[i2]))
+ break;
+ }
+ if (i2 == rdn2.size())
+ return false;
+ }
+ return true;
+}
+
+
+bool CertPrincipal::Matches(const CertPrincipal& against) const {
+ return match(common_name, against.common_name) &&
+ match(common_name, against.common_name) &&
+ match(locality_name, against.locality_name) &&
+ match(state_or_province_name, against.state_or_province_name) &&
+ match(country_name, against.country_name) &&
+ match(street_addresses, against.street_addresses) &&
+ match(organization_names, against.organization_names) &&
+ match(organization_unit_names, against.organization_unit_names) &&
+ match(domain_components, against.domain_components);
+}
+
+std::ostream& operator<<(std::ostream& s, const CertPrincipal& p) {
+ s << "CertPrincipal[";
+ if (!p.common_name.empty())
+ s << "cn=\"" << p.common_name << "\" ";
+ for (unsigned i = 0; i < p.street_addresses.size(); ++i)
+ s << "street=\"" << p.street_addresses[i] << "\" ";
+ if (!p.locality_name.empty())
+ s << "l=\"" << p.locality_name << "\" ";
+ for (unsigned i = 0; i < p.organization_names.size(); ++i)
+ s << "o=\"" << p.organization_names[i] << "\" ";
+ for (unsigned i = 0; i < p.organization_unit_names.size(); ++i)
+ s << "ou=\"" << p.organization_unit_names[i] << "\" ";
+ if (!p.state_or_province_name.empty())
+ s << "st=\"" << p.state_or_province_name << "\" ";
+ if (!p.country_name.empty())
+ s << "c=\"" << p.country_name << "\" ";
+ for (unsigned i = 0; i < p.domain_components.size(); ++i)
+ s << "dc=\"" << p.domain_components[i] << "\" ";
+ return s << "]";
+}
+
+CertPolicy::Judgment CertPolicy::Check(
+ X509Certificate* cert) const {
+ // It shouldn't matter which set we check first, but we check denied first
+ // in case something strange has happened.
+
+ if (denied_.find(cert->fingerprint()) != denied_.end()) {
+ // DCHECK that the order didn't matter.
+ DCHECK(allowed_.find(cert->fingerprint()) == allowed_.end());
+ return DENIED;
+ }
+
+ if (allowed_.find(cert->fingerprint()) != allowed_.end()) {
+ // DCHECK that the order didn't matter.
+ DCHECK(denied_.find(cert->fingerprint()) == denied_.end());
+ return ALLOWED;
+ }
+
+ // We don't have a policy for this cert.
+ return UNKNOWN;
+}
+
+void CertPolicy::Allow(X509Certificate* cert) {
+ // Put the cert in the allowed set and (maybe) remove it from the denied set.
+ denied_.erase(cert->fingerprint());
+ allowed_.insert(cert->fingerprint());
+}
+
+void CertPolicy::Deny(X509Certificate* cert) {
+ // Put the cert in the denied set and (maybe) remove it from the allowed set.
+ allowed_.erase(cert->fingerprint());
+ denied_.insert(cert->fingerprint());
+}
+
+bool CertPolicy::HasAllowedCert() const {
+ return !allowed_.empty();
+}
+
+bool CertPolicy::HasDeniedCert() const {
+ return !denied_.empty();
+}
+
+} // namespace net
diff --git a/net/base/x509_cert_types.h b/net/base/x509_cert_types.h
new file mode 100644
index 0000000..cf23f58
--- /dev/null
+++ b/net/base/x509_cert_types.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2007-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_X509_TYPES_H_
+#define NET_BASE_X509_TYPES_H_
+
+#include <string.h>
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/singleton.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <wincrypt.h>
+#elif defined(OS_MACOSX)
+#include <Security/x509defs.h>
+#elif defined(USE_NSS)
+// Forward declaration; real one in <cert.h>
+struct CERTCertificateStr;
+#endif
+
+namespace net {
+
+class X509Certificate;
+
+// SHA-1 fingerprint (160 bits) of a certificate.
+struct SHA1Fingerprint {
+ bool Equals(const SHA1Fingerprint& other) const {
+ return memcmp(data, other.data, sizeof(data)) == 0;
+ }
+
+ unsigned char data[20];
+};
+
+class SHA1FingerprintLessThan
+ : public std::binary_function<SHA1Fingerprint, SHA1Fingerprint, bool> {
+ public:
+ bool operator() (const SHA1Fingerprint& lhs, const SHA1Fingerprint& rhs) const;
+};
+
+// CertPrincipal represents the issuer or subject field of an X.509 certificate.
+struct CertPrincipal {
+ CertPrincipal() { }
+ explicit CertPrincipal(const std::string& name) : common_name(name) { }
+
+ // Parses a BER-format DistinguishedName.
+ bool ParseDistinguishedName(const void* ber_name_data, size_t length);
+
+#if defined(OS_MACOSX)
+ // Parses a CSSM_X509_NAME struct.
+ void Parse(const CSSM_X509_NAME* name);
+#endif
+
+ // Returns true if all attributes of the two objects match,
+ // where "match" is defined in RFC 5280 sec. 7.1.
+ bool Matches(const CertPrincipal& against) const;
+
+ // The different attributes for a principal. They may be "".
+ // Note that some of them can have several values.
+
+ std::string common_name;
+ std::string locality_name;
+ std::string state_or_province_name;
+ std::string country_name;
+
+ std::vector<std::string> street_addresses;
+ std::vector<std::string> organization_names;
+ std::vector<std::string> organization_unit_names;
+ std::vector<std::string> domain_components;
+};
+
+// Writes a human-readable description of a CertPrincipal, for debugging.
+std::ostream& operator<<(std::ostream& s, const CertPrincipal& p);
+
+// This class is useful for maintaining policies about which certificates are
+// permitted or forbidden for a particular purpose.
+class CertPolicy {
+ public:
+ // The judgments this policy can reach.
+ enum Judgment {
+ // We don't have policy information for this certificate.
+ UNKNOWN,
+
+ // This certificate is allowed.
+ ALLOWED,
+
+ // This certificate is denied.
+ DENIED,
+ };
+
+ // Returns the judgment this policy makes about this certificate.
+ Judgment Check(X509Certificate* cert) const;
+
+ // Causes the policy to allow this certificate.
+ void Allow(X509Certificate* cert);
+
+ // Causes the policy to deny this certificate.
+ void Deny(X509Certificate* cert);
+
+ // Returns true if this policy has allowed at least one certificate.
+ bool HasAllowedCert() const;
+
+ // Returns true if this policy has denied at least one certificate.
+ bool HasDeniedCert() const;
+
+ private:
+ // The set of fingerprints of allowed certificates.
+ std::set<SHA1Fingerprint, SHA1FingerprintLessThan> allowed_;
+
+ // The set of fingerprints of denied certificates.
+ std::set<SHA1Fingerprint, SHA1FingerprintLessThan> denied_;
+};
+
+#if defined(OS_MACOSX)
+// Compares two OIDs by value.
+inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) {
+ return oid1->Length == oid2->Length &&
+ (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0);
+}
+#endif
+
+} // namespace net
+
+#endif // NET_BASE_X509_TYPES_H_
diff --git a/net/base/x509_cert_types_mac.cc b/net/base/x509_cert_types_mac.cc
new file mode 100644
index 0000000..504dde5
--- /dev/null
+++ b/net/base/x509_cert_types_mac.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/x509_cert_types.h"
+
+#include <CoreServices/CoreServices.h>
+#include <Security/Security.h>
+#include <Security/SecAsn1Coder.h>
+
+#include "base/logging.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/utf_string_conversions.h"
+
+namespace net {
+
+static const CSSM_OID* kOIDs[] = {
+ &CSSMOID_CommonName,
+ &CSSMOID_LocalityName,
+ &CSSMOID_StateProvinceName,
+ &CSSMOID_CountryName,
+ &CSSMOID_StreetAddress,
+ &CSSMOID_OrganizationName,
+ &CSSMOID_OrganizationalUnitName,
+ &CSSMOID_DNQualifier // This should be "DC" but is undoubtedly wrong.
+}; // TODO(avi): Find the right OID.
+
+// Converts raw CSSM_DATA to a std::string. (Char encoding is unaltered.)
+static std::string DataToString(CSSM_DATA data);
+
+// Converts raw CSSM_DATA in ISO-8859-1 to a std::string in UTF-8.
+static std::string Latin1DataToUTF8String(CSSM_DATA data);
+
+// Converts big-endian UTF-16 to UTF-8 in a std::string.
+// Note: The byte-order flipping is done in place on the input buffer!
+static bool UTF16BigEndianToUTF8(char16* chars, size_t length,
+ std::string* out_string);
+
+// Converts big-endian UTF-32 to UTF-8 in a std::string.
+// Note: The byte-order flipping is done in place on the input buffer!
+static bool UTF32BigEndianToUTF8(char32* chars, size_t length,
+ std::string* out_string);
+
+// Adds a type+value pair to the appropriate vector from a C array.
+// The array is keyed by the matching OIDs from kOIDS[].
+ static void AddTypeValuePair(const CSSM_OID type,
+ const std::string& value,
+ std::vector<std::string>* values[]);
+
+// Stores the first string of the vector, if any, to *single_value.
+static void SetSingle(const std::vector<std::string> &values,
+ std::string* single_value);
+
+
+void CertPrincipal::Parse(const CSSM_X509_NAME* name) {
+ std::vector<std::string> common_names, locality_names, state_names,
+ country_names;
+
+ std::vector<std::string>* values[] = {
+ &common_names, &locality_names,
+ &state_names, &country_names,
+ &(this->street_addresses),
+ &(this->organization_names),
+ &(this->organization_unit_names),
+ &(this->domain_components)
+ };
+ DCHECK(arraysize(kOIDs) == arraysize(values));
+
+ for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) {
+ CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn];
+ for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) {
+ CSSM_X509_TYPE_VALUE_PAIR pair_struct =
+ rdn_struct.AttributeTypeAndValue[pair];
+ AddTypeValuePair(pair_struct.type,
+ DataToString(pair_struct.value),
+ values);
+ }
+ }
+
+ SetSingle(common_names, &this->common_name);
+ SetSingle(locality_names, &this->locality_name);
+ SetSingle(state_names, &this->state_or_province_name);
+ SetSingle(country_names, &this->country_name);
+}
+
+
+// The following structs and templates work with Apple's very arcane and under-
+// documented SecAsn1Parser API, which is apparently the same as NSS's ASN.1
+// decoder:
+// http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn1.html
+
+// These are used to parse the contents of a raw
+// BER DistinguishedName structure.
+
+struct KeyValuePair {
+ CSSM_OID key;
+ int value_type;
+ CSSM_DATA value;
+
+ enum {
+ kTypeOther = 0,
+ kTypePrintableString,
+ kTypeIA5String,
+ kTypeT61String,
+ kTypeUTF8String,
+ kTypeBMPString,
+ kTypeUniversalString,
+ };
+};
+
+static const SecAsn1Template kStringValueTemplate[] = {
+ { SEC_ASN1_CHOICE, offsetof(KeyValuePair, value_type), },
+ { SEC_ASN1_PRINTABLE_STRING,
+ offsetof(KeyValuePair, value), 0, KeyValuePair::kTypePrintableString },
+ { SEC_ASN1_IA5_STRING,
+ offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeIA5String },
+ { SEC_ASN1_T61_STRING,
+ offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeT61String },
+ { SEC_ASN1_UTF8_STRING,
+ offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUTF8String },
+ { SEC_ASN1_BMP_STRING,
+ offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeBMPString },
+ { SEC_ASN1_UNIVERSAL_STRING,
+ offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUniversalString },
+ { 0, }
+};
+
+static const SecAsn1Template kKeyValuePairTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(KeyValuePair) },
+ { SEC_ASN1_OBJECT_ID, offsetof(KeyValuePair, key), },
+ { SEC_ASN1_INLINE, 0, &kStringValueTemplate, },
+ { 0, }
+};
+
+struct KeyValuePairs {
+ KeyValuePair* pairs;
+};
+
+static const SecAsn1Template kKeyValuePairSetTemplate[] = {
+ { SEC_ASN1_SET_OF, offsetof(KeyValuePairs,pairs),
+ kKeyValuePairTemplate, sizeof(KeyValuePairs) }
+};
+
+struct X509Name {
+ KeyValuePairs** pairs_list;
+};
+
+static const SecAsn1Template kNameTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF, offsetof(X509Name,pairs_list),
+ kKeyValuePairSetTemplate, sizeof(X509Name) }
+};
+
+bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
+ size_t length) {
+ DCHECK(ber_name_data);
+
+ // First parse the BER |name_data| into the above structs.
+ SecAsn1CoderRef coder = NULL;
+ SecAsn1CoderCreate(&coder);
+ DCHECK(coder);
+ X509Name* name = NULL;
+ OSStatus err = SecAsn1Decode(coder, ber_name_data, length, kNameTemplate,
+ &name);
+ if (err) {
+ LOG(ERROR) << "SecAsn1Decode returned " << err << "; name=" << name;
+ SecAsn1CoderRelease(coder);
+ return false;
+ }
+
+ // Now scan the structs and add the values to my string vectors.
+ // I don't store multiple common/locality/state/country names, so use
+ // temporary vectors for those.
+ std::vector<std::string> common_names, locality_names, state_names,
+ country_names;
+ std::vector<std::string>* values[] = {
+ &common_names, &locality_names,
+ &state_names, &country_names,
+ &this->street_addresses,
+ &this->organization_names,
+ &this->organization_unit_names,
+ &this->domain_components
+ };
+ DCHECK(arraysize(kOIDs) == arraysize(values));
+
+ for (int rdn=0; name[rdn].pairs_list; ++rdn) {
+ KeyValuePair *pair;
+ for (int pair_index = 0;
+ NULL != (pair = name[rdn].pairs_list[0][pair_index].pairs);
+ ++pair_index) {
+ switch (pair->value_type) {
+ case KeyValuePair::kTypeIA5String: // ASCII (that means 7-bit!)
+ case KeyValuePair::kTypePrintableString: // a subset of ASCII
+ case KeyValuePair::kTypeUTF8String: // UTF-8
+ AddTypeValuePair(pair->key, DataToString(pair->value), values);
+ break;
+ case KeyValuePair::kTypeT61String: // T61, pretend it's Latin-1
+ AddTypeValuePair(pair->key,
+ Latin1DataToUTF8String(pair->value),
+ values);
+ break;
+ case KeyValuePair::kTypeBMPString: { // UTF-16, big-endian
+ std::string value;
+ UTF16BigEndianToUTF8(reinterpret_cast<char16*>(pair->value.Data),
+ pair->value.Length / sizeof(char16),
+ &value);
+ AddTypeValuePair(pair->key, value, values);
+ break;
+ }
+ case KeyValuePair::kTypeUniversalString: { // UTF-32, big-endian
+ std::string value;
+ UTF32BigEndianToUTF8(reinterpret_cast<char32*>(pair->value.Data),
+ pair->value.Length / sizeof(char32),
+ &value);
+ AddTypeValuePair(pair->key, value, values);
+ break;
+ }
+ default:
+ DCHECK_EQ(pair->value_type, KeyValuePair::kTypeOther);
+ // We don't know what data type this is, but we'll store it as a blob.
+ // Displaying the string may not work, but at least it can be compared
+ // byte-for-byte by a Matches() call.
+ AddTypeValuePair(pair->key, DataToString(pair->value), values);
+ break;
+ }
+ }
+ }
+
+ SetSingle(common_names, &this->common_name);
+ SetSingle(locality_names, &this->locality_name);
+ SetSingle(state_names, &this->state_or_province_name);
+ SetSingle(country_names, &this->country_name);
+
+ // Releasing |coder| frees all the memory pointed to via |name|.
+ SecAsn1CoderRelease(coder);
+ return true;
+}
+
+
+// SUBROUTINES:
+
+static std::string DataToString(CSSM_DATA data) {
+ return std::string(
+ reinterpret_cast<std::string::value_type*>(data.Data),
+ data.Length);
+}
+
+static std::string Latin1DataToUTF8String(CSSM_DATA data) {
+ string16 utf16;
+ if (!CodepageToUTF16(DataToString(data), base::kCodepageLatin1,
+ base::OnStringConversionError::FAIL, &utf16))
+ return "";
+ return UTF16ToUTF8(utf16);
+}
+
+bool UTF16BigEndianToUTF8(char16* chars, size_t length,
+ std::string* out_string) {
+ for (size_t i = 0; i < length; i++)
+ chars[i] = EndianU16_BtoN(chars[i]);
+ return UTF16ToUTF8(chars, length, out_string);
+}
+
+bool UTF32BigEndianToUTF8(char32* chars, size_t length,
+ std::string* out_string) {
+ for (size_t i = 0; i < length; i++)
+ chars[i] = EndianS32_BtoN(chars[i]);
+#if defined(WCHAR_T_IS_UTF32)
+ return WideToUTF8(reinterpret_cast<const wchar_t*>(chars),
+ length, out_string);
+#else
+#error This code doesn't handle 16-bit wchar_t.
+#endif
+}
+
+ static void AddTypeValuePair(const CSSM_OID type,
+ const std::string& value,
+ std::vector<std::string>* values[]) {
+ for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
+ if (CSSMOIDEqual(&type, kOIDs[oid])) {
+ values[oid]->push_back(value);
+ break;
+ }
+ }
+}
+
+static void SetSingle(const std::vector<std::string> &values,
+ std::string* single_value) {
+ // We don't expect to have more than one CN, L, S, and C.
+ LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values";
+ if (values.size() > 0)
+ *single_value = values[0];
+}
+
+} // namespace net
diff --git a/net/base/x509_cert_types_unittest.cc b/net/base/x509_cert_types_unittest.cc
new file mode 100644
index 0000000..27104cc
--- /dev/null
+++ b/net/base/x509_cert_types_unittest.cc
@@ -0,0 +1,343 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/x509_cert_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// 0:d=0 hl=2 l= 95 cons: SEQUENCE
+// 2:d=1 hl=2 l= 11 cons: SET
+// 4:d=2 hl=2 l= 9 cons: SEQUENCE
+// 6:d=3 hl=2 l= 3 prim: OBJECT :countryName
+// 11:d=3 hl=2 l= 2 prim: PRINTABLESTRING :US
+// 15:d=1 hl=2 l= 23 cons: SET
+// 17:d=2 hl=2 l= 21 cons: SEQUENCE
+// 19:d=3 hl=2 l= 3 prim: OBJECT :organizationName
+// 24:d=3 hl=2 l= 14 prim: PRINTABLESTRING :VeriSign, Inc.
+// 40:d=1 hl=2 l= 55 cons: SET
+// 42:d=2 hl=2 l= 53 cons: SEQUENCE
+// 44:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName
+// 49:d=3 hl=2 l= 46 prim: PRINTABLESTRING :Class 1 Public Primary Certification Authority
+static const uint8_t VerisignDN[] = {
+ 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2e, 0x43,
+ 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
+ 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
+};
+
+// 0:d=0 hl=2 l= 125 cons: SEQUENCE
+// 2:d=1 hl=2 l= 11 cons: SET
+// 4:d=2 hl=2 l= 9 cons: SEQUENCE
+// 6:d=3 hl=2 l= 3 prim: OBJECT :countryName
+// 11:d=3 hl=2 l= 2 prim: PRINTABLESTRING :IL
+// 15:d=1 hl=2 l= 22 cons: SET
+// 17:d=2 hl=2 l= 20 cons: SEQUENCE
+// 19:d=3 hl=2 l= 3 prim: OBJECT :organizationName
+// 24:d=3 hl=2 l= 13 prim: PRINTABLESTRING :StartCom Ltd.
+// 39:d=1 hl=2 l= 43 cons: SET
+// 41:d=2 hl=2 l= 41 cons: SEQUENCE
+// 43:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName
+// 48:d=3 hl=2 l= 34 prim: PRINTABLESTRING :Secure Digital Certificate Signing
+// 84:d=1 hl=2 l= 41 cons: SET
+// 86:d=2 hl=2 l= 39 cons: SEQUENCE
+// 88:d=3 hl=2 l= 3 prim: OBJECT :commonName
+// 93:d=3 hl=2 l= 32 prim: PRINTABLESTRING :StartCom Certification Authority
+static const uint8_t StartComDN[] = {
+ 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e,
+ 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53,
+ 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
+};
+
+// 0:d=0 hl=3 l= 174 cons: SEQUENCE
+// 3:d=1 hl=2 l= 11 cons: SET
+// 5:d=2 hl=2 l= 9 cons: SEQUENCE
+// 7:d=3 hl=2 l= 3 prim: OBJECT :countryName
+// 12:d=3 hl=2 l= 2 prim: PRINTABLESTRING :US
+// 16:d=1 hl=2 l= 11 cons: SET
+// 18:d=2 hl=2 l= 9 cons: SEQUENCE
+// 20:d=3 hl=2 l= 3 prim: OBJECT :stateOrProvinceName
+// 25:d=3 hl=2 l= 2 prim: PRINTABLESTRING :UT
+// 29:d=1 hl=2 l= 23 cons: SET
+// 31:d=2 hl=2 l= 21 cons: SEQUENCE
+// 33:d=3 hl=2 l= 3 prim: OBJECT :localityName
+// 38:d=3 hl=2 l= 14 prim: PRINTABLESTRING :Salt Lake City
+// 54:d=1 hl=2 l= 30 cons: SET
+// 56:d=2 hl=2 l= 28 cons: SEQUENCE
+// 58:d=3 hl=2 l= 3 prim: OBJECT :organizationName
+// 63:d=3 hl=2 l= 21 prim: PRINTABLESTRING :The USERTRUST Network
+// 86:d=1 hl=2 l= 33 cons: SET
+// 88:d=2 hl=2 l= 31 cons: SEQUENCE
+// 90:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName
+// 95:d=3 hl=2 l= 24 prim: PRINTABLESTRING :http://www.usertrust.com
+//121:d=1 hl=2 l= 54 cons: SET
+//123:d=2 hl=2 l= 52 cons: SEQUENCE
+//125:d=3 hl=2 l= 3 prim: OBJECT :commonName
+//130:d=3 hl=2 l= 45 prim: PRINTABLESTRING :UTN-USERFirst-Client Authentication and Email
+static const uint8_t UserTrustDN[] = {
+ 0x30, 0x81, 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
+ 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20, 0x43, 0x69,
+ 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15,
+ 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54,
+ 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x2d, 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72,
+ 0x73, 0x74, 0x2d, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x45, 0x6d, 0x61, 0x69, 0x6c
+};
+
+// 0:d=0 hl=3 l= 190 cons: SEQUENCE
+// 3:d=1 hl=2 l= 63 cons: SET
+// 5:d=2 hl=2 l= 61 cons: SEQUENCE
+// 7:d=3 hl=2 l= 3 prim: OBJECT :commonName
+// 12:d=3 hl=2 l= 54 prim: UTF8STRING :TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı
+// 68:d=1 hl=2 l= 11 cons: SET
+// 70:d=2 hl=2 l= 9 cons: SEQUENCE
+// 72:d=3 hl=2 l= 3 prim: OBJECT :countryName
+// 77:d=3 hl=2 l= 2 prim: PRINTABLESTRING :TR
+// 81:d=1 hl=2 l= 15 cons: SET
+// 83:d=2 hl=2 l= 13 cons: SEQUENCE
+// 85:d=3 hl=2 l= 3 prim: OBJECT :localityName
+// 90:d=3 hl=2 l= 6 prim: UTF8STRING :Ankara
+// 98:d=1 hl=2 l= 93 cons: SET
+//100:d=2 hl=2 l= 91 cons: SEQUENCE
+//102:d=3 hl=2 l= 3 prim: OBJECT :organizationName
+//107:d=3 hl=2 l= 84 prim: UTF8STRING :TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005
+static const uint8_t TurkTrustDN[] = {
+ 0x30, 0x81, 0xbe, 0x31, 0x3f, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x36, 0x54, 0xc3, 0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x45,
+ 0x6c, 0x65, 0x6b, 0x74, 0x72, 0x6f, 0x6e, 0x69, 0x6b, 0x20, 0x53, 0x65, 0x72,
+ 0x74, 0x69, 0x66, 0x69, 0x6b, 0x61, 0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74,
+ 0x20, 0x53, 0x61, 0xc4, 0x9f, 0x6c, 0x61, 0x79, 0xc4, 0xb1, 0x63, 0xc4, 0xb1,
+ 0x73, 0xc4, 0xb1, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x54, 0x52, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
+ 0x06, 0x41, 0x6e, 0x6b, 0x61, 0x72, 0x61, 0x31, 0x5d, 0x30, 0x5b, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x0c, 0x54, 0x54, 0xc3, 0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55,
+ 0x53, 0x54, 0x20, 0x42, 0x69, 0x6c, 0x67, 0x69, 0x20, 0xc4, 0xb0, 0x6c, 0x65,
+ 0x74, 0x69, 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x76, 0x65, 0x20, 0x42, 0x69, 0x6c,
+ 0x69, 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x47, 0xc3, 0xbc, 0x76, 0x65, 0x6e, 0x6c,
+ 0x69, 0xc4, 0x9f, 0x69, 0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74, 0x6c, 0x65,
+ 0x72, 0x69, 0x20, 0x41, 0x2e, 0xc5, 0x9e, 0x2e, 0x20, 0x28, 0x63, 0x29, 0x20,
+ 0x4b, 0x61, 0x73, 0xc4, 0xb1, 0x6d, 0x20, 0x32, 0x30, 0x30, 0x35, 0x30, 0x1e,
+ 0x17, 0x0d, 0x30, 0x35, 0x31, 0x31, 0x30, 0x37, 0x31, 0x30, 0x30, 0x37, 0x35,
+ 0x37
+};
+
+// 33:d=2 hl=3 l= 207 cons: SEQUENCE
+// 36:d=3 hl=2 l= 11 cons: SET
+// 38:d=4 hl=2 l= 9 cons: SEQUENCE
+// 40:d=5 hl=2 l= 3 prim: OBJECT :countryName
+// 45:d=5 hl=2 l= 2 prim: PRINTABLESTRING :AT
+// 49:d=3 hl=3 l= 139 cons: SET
+// 52:d=4 hl=3 l= 136 cons: SEQUENCE
+// 55:d=5 hl=2 l= 3 prim: OBJECT :organizationName
+// 60:d=5 hl=3 l= 128 prim: BMPSTRING :A-Trust Ges. für Sicherheitssysteme im elektr. Datenverkehr GmbH
+//191:d=3 hl=2 l= 24 cons: SET
+//193:d=4 hl=2 l= 22 cons: SEQUENCE
+//195:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName
+//200:d=5 hl=2 l= 15 prim: PRINTABLESTRING :A-Trust-Qual-01
+//217:d=3 hl=2 l= 24 cons: SET
+//219:d=4 hl=2 l= 22 cons: SEQUENCE
+//221:d=5 hl=2 l= 3 prim: OBJECT :commonName
+//226:d=5 hl=2 l= 15 prim: PRINTABLESTRING :A-Trust-Qual-01
+static const uint8_t ATrustQual01DN[] = {
+ 0x30, 0x81, 0xcf, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x41, 0x54, 0x31, 0x81, 0x8b, 0x30, 0x81, 0x88, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x1e, 0x81, 0x80, 0x00, 0x41, 0x00, 0x2d, 0x00, 0x54, 0x00, 0x72, 0x00,
+ 0x75, 0x00, 0x73, 0x00, 0x74, 0x00, 0x20, 0x00, 0x47, 0x00, 0x65, 0x00, 0x73,
+ 0x00, 0x2e, 0x00, 0x20, 0x00, 0x66, 0x00, 0xfc, 0x00, 0x72, 0x00, 0x20, 0x00,
+ 0x53, 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x68,
+ 0x00, 0x65, 0x00, 0x69, 0x00, 0x74, 0x00, 0x73, 0x00, 0x73, 0x00, 0x79, 0x00,
+ 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69,
+ 0x00, 0x6d, 0x00, 0x20, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x6b, 0x00,
+ 0x74, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x44, 0x00, 0x61, 0x00, 0x74,
+ 0x00, 0x65, 0x00, 0x6e, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6b, 0x00,
+ 0x65, 0x00, 0x68, 0x00, 0x72, 0x00, 0x20, 0x00, 0x47, 0x00, 0x6d, 0x00, 0x62,
+ 0x00, 0x48, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f,
+ 0x41, 0x2d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x51, 0x75, 0x61, 0x6c, 0x2d,
+ 0x30, 0x31, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f,
+ 0x41, 0x2d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x51, 0x75, 0x61, 0x6c, 0x2d,
+ 0x30, 0x31, 0x30, 0x1e, 0x17
+};
+
+// 34:d=2 hl=3 l= 180 cons: SEQUENCE
+// 37:d=3 hl=2 l= 20 cons: SET
+// 39:d=4 hl=2 l= 18 cons: SEQUENCE
+// 41:d=5 hl=2 l= 3 prim: OBJECT :organizationName
+// 46:d=5 hl=2 l= 11 prim: PRINTABLESTRING :Entrust.net
+// 59:d=3 hl=2 l= 64 cons: SET
+// 61:d=4 hl=2 l= 62 cons: SEQUENCE
+// 63:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName
+// 68:d=5 hl=2 l= 55 prim: T61STRING :www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)
+//125:d=3 hl=2 l= 37 cons: SET
+//127:d=4 hl=2 l= 35 cons: SEQUENCE
+//129:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName
+//134:d=5 hl=2 l= 28 prim: PRINTABLESTRING :(c) 1999 Entrust.net Limited
+//164:d=3 hl=2 l= 51 cons: SET
+//166:d=4 hl=2 l= 49 cons: SEQUENCE
+//168:d=5 hl=2 l= 3 prim: OBJECT :commonName
+//173:d=5 hl=2 l= 42 prim: PRINTABLESTRING :Entrust.net Certification Authority (2048)
+static const uint8_t EntrustDN[] = {
+ 0x30, 0x81, 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x31,
+ 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, 0x77, 0x77, 0x77,
+ 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f,
+ 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x20, 0x69, 0x6e, 0x63, 0x6f,
+ 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28,
+ 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e, 0x29,
+ 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63,
+ 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64,
+ 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, 0x34, 0x38,
+ 0x29
+};
+
+namespace net {
+
+TEST(X509TypesTest, Matching) {
+ CertPrincipal spamco;
+ spamco.common_name = "SpamCo Dept. Of Certificization";
+ spamco.country_name = "EB";
+ spamco.organization_names.push_back("SpamCo Holding Company, LLC");
+ spamco.organization_names.push_back("SpamCo Evil Masterminds");
+ spamco.organization_unit_names.push_back("Class Z Obfuscation Authority");
+ ASSERT_TRUE(spamco.Matches(spamco));
+
+ CertPrincipal bogus;
+ EXPECT_FALSE(bogus.Matches(spamco));
+ EXPECT_FALSE(spamco.Matches(bogus));
+
+ bogus = spamco;
+ EXPECT_TRUE(bogus.Matches(spamco));
+ EXPECT_TRUE(spamco.Matches(bogus));
+
+ bogus.organization_names.erase(bogus.organization_names.begin(),
+ bogus.organization_names.end());
+ EXPECT_FALSE(bogus.Matches(spamco));
+ EXPECT_FALSE(spamco.Matches(bogus));
+
+ bogus.organization_names.push_back("SpamCo Holding Company, LLC");
+ bogus.organization_names.push_back("SpamCo Evil Masterminds");
+ EXPECT_TRUE(bogus.Matches(spamco));
+ EXPECT_TRUE(spamco.Matches(bogus));
+
+ bogus.locality_name = "Elbosdorf";
+ EXPECT_FALSE(bogus.Matches(spamco));
+ EXPECT_FALSE(spamco.Matches(bogus));
+
+ bogus.locality_name = "";
+ bogus.organization_unit_names.push_back("Q Division");
+ EXPECT_FALSE(bogus.Matches(spamco));
+ EXPECT_FALSE(spamco.Matches(bogus));
+}
+
+#if defined(OS_MACOSX) // ParseDistinguishedName not implemented for Win/Linux
+
+TEST(X509TypesTest, ParseDNVerisign) {
+ CertPrincipal verisign;
+ EXPECT_TRUE(verisign.ParseDistinguishedName(VerisignDN, sizeof(VerisignDN)));
+ EXPECT_EQ("", verisign.common_name);
+ EXPECT_EQ("US", verisign.country_name);
+ ASSERT_EQ(1U, verisign.organization_names.size());
+ EXPECT_EQ("VeriSign, Inc.", verisign.organization_names[0]);
+ ASSERT_EQ(1U, verisign.organization_unit_names.size());
+ EXPECT_EQ("Class 1 Public Primary Certification Authority",
+ verisign.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNStartcom) {
+ CertPrincipal startcom;
+ EXPECT_TRUE(startcom.ParseDistinguishedName(StartComDN, sizeof(StartComDN)));
+ EXPECT_EQ("StartCom Certification Authority", startcom.common_name);
+ EXPECT_EQ("IL", startcom.country_name);
+ ASSERT_EQ(1U, startcom.organization_names.size());
+ EXPECT_EQ("StartCom Ltd.", startcom.organization_names[0]);
+ ASSERT_EQ(1U, startcom.organization_unit_names.size());
+ EXPECT_EQ("Secure Digital Certificate Signing",
+ startcom.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNUserTrust) {
+ CertPrincipal usertrust;
+ EXPECT_TRUE(usertrust.ParseDistinguishedName(UserTrustDN,
+ sizeof(UserTrustDN)));
+ EXPECT_EQ("UTN-USERFirst-Client Authentication and Email",
+ usertrust.common_name);
+ EXPECT_EQ("US", usertrust.country_name);
+ EXPECT_EQ("UT", usertrust.state_or_province_name);
+ EXPECT_EQ("Salt Lake City", usertrust.locality_name);
+ ASSERT_EQ(1U, usertrust.organization_names.size());
+ EXPECT_EQ("The USERTRUST Network", usertrust.organization_names[0]);
+ ASSERT_EQ(1U, usertrust.organization_unit_names.size());
+ EXPECT_EQ("http://www.usertrust.com",
+ usertrust.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNTurkTrust) {
+ // Note: This tests parsing UTF8STRINGs.
+ CertPrincipal turktrust;
+ EXPECT_TRUE(turktrust.ParseDistinguishedName(TurkTrustDN,
+ sizeof(TurkTrustDN)));
+ EXPECT_EQ("TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı",
+ turktrust.common_name);
+ EXPECT_EQ("TR", turktrust.country_name);
+ EXPECT_EQ("Ankara", turktrust.locality_name);
+ ASSERT_EQ(1U, turktrust.organization_names.size());
+ EXPECT_EQ("TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005",
+ turktrust.organization_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNATrust) {
+ // Note: This tests parsing 16-bit BMPSTRINGs.
+ CertPrincipal atrust;
+ EXPECT_TRUE(atrust.ParseDistinguishedName(ATrustQual01DN,
+ sizeof(ATrustQual01DN)));
+ EXPECT_EQ("A-Trust-Qual-01",
+ atrust.common_name);
+ EXPECT_EQ("AT", atrust.country_name);
+ ASSERT_EQ(1U, atrust.organization_names.size());
+ EXPECT_EQ("A-Trust Ges. für Sicherheitssysteme im elektr. Datenverkehr GmbH",
+ atrust.organization_names[0]);
+ ASSERT_EQ(1U, atrust.organization_unit_names.size());
+ EXPECT_EQ("A-Trust-Qual-01",
+ atrust.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNEntrust) {
+ // Note: This tests parsing T61STRINGs and fields with multiple values.
+ CertPrincipal entrust;
+ EXPECT_TRUE(entrust.ParseDistinguishedName(EntrustDN,
+ sizeof(EntrustDN)));
+ EXPECT_EQ("Entrust.net Certification Authority (2048)",
+ entrust.common_name);
+ EXPECT_EQ("", entrust.country_name);
+ ASSERT_EQ(1U, entrust.organization_names.size());
+ EXPECT_EQ("Entrust.net",
+ entrust.organization_names[0]);
+ ASSERT_EQ(2U, entrust.organization_unit_names.size());
+ EXPECT_EQ("www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)",
+ entrust.organization_unit_names[0]);
+ EXPECT_EQ("(c) 1999 Entrust.net Limited",
+ entrust.organization_unit_names[1]);
+}
+#endif
+
+}
diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc
index adf73b9..367afda 100644
--- a/net/base/x509_certificate.cc
+++ b/net/base/x509_certificate.cc
@@ -4,7 +4,9 @@
#include "net/base/x509_certificate.h"
-#if defined(USE_NSS)
+#if defined(OS_MACOSX)
+#include <Security/Security.h>
+#elif defined(USE_NSS)
#include <cert.h>
#endif
@@ -56,8 +58,8 @@ bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
}
bool X509Certificate::FingerprintLessThan::operator()(
- const Fingerprint& lhs,
- const Fingerprint& rhs) const {
+ const SHA1Fingerprint& lhs,
+ const SHA1Fingerprint& rhs) const {
for (size_t i = 0; i < sizeof(lhs.data); ++i) {
if (lhs.data[i] < rhs.data[i])
return true;
@@ -121,47 +123,6 @@ X509Certificate* X509Certificate::Cache::Find(const Fingerprint& fingerprint) {
return pos->second;
};
-X509Certificate::Policy::Judgment X509Certificate::Policy::Check(
- X509Certificate* cert) const {
- // It shouldn't matter which set we check first, but we check denied first
- // in case something strange has happened.
-
- if (denied_.find(cert->fingerprint()) != denied_.end()) {
- // DCHECK that the order didn't matter.
- DCHECK(allowed_.find(cert->fingerprint()) == allowed_.end());
- return DENIED;
- }
-
- if (allowed_.find(cert->fingerprint()) != allowed_.end()) {
- // DCHECK that the order didn't matter.
- DCHECK(denied_.find(cert->fingerprint()) == denied_.end());
- return ALLOWED;
- }
-
- // We don't have a policy for this cert.
- return UNKNOWN;
-}
-
-void X509Certificate::Policy::Allow(X509Certificate* cert) {
- // Put the cert in the allowed set and (maybe) remove it from the denied set.
- denied_.erase(cert->fingerprint());
- allowed_.insert(cert->fingerprint());
-}
-
-void X509Certificate::Policy::Deny(X509Certificate* cert) {
- // Put the cert in the denied set and (maybe) remove it from the allowed set.
- allowed_.erase(cert->fingerprint());
- denied_.insert(cert->fingerprint());
-}
-
-bool X509Certificate::Policy::HasAllowedCert() const {
- return !allowed_.empty();
-}
-
-bool X509Certificate::Policy::HasDeniedCert() const {
- return !denied_.empty();
-}
-
// static
X509Certificate* X509Certificate::CreateFromHandle(
OSCertHandle cert_handle,
diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h
index ec287ce..32c16f1 100644
--- a/net/base/x509_certificate.h
+++ b/net/base/x509_certificate.h
@@ -15,13 +15,15 @@
#include "base/ref_counted.h"
#include "base/singleton.h"
#include "base/time.h"
+#include "net/base/x509_cert_types.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
#if defined(OS_WIN)
#include <windows.h>
#include <wincrypt.h>
#elif defined(OS_MACOSX)
-#include <Security/Security.h>
+#include <CoreFoundation/CFArray.h>
+#include <Security/SecBase.h>
#elif defined(USE_NSS)
// Forward declaration; real one in <cert.h>
struct CERTCertificateStr;
@@ -36,28 +38,6 @@ class CertVerifyResult;
// X509Certificate represents an X.509 certificate used by SSL.
class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
public:
- // SHA-1 fingerprint (160 bits) of a certificate.
- struct Fingerprint {
- bool Equals(const Fingerprint& other) const {
- return memcmp(data, other.data, sizeof(data)) == 0;
- }
-
- unsigned char data[20];
- };
-
- class FingerprintLessThan
- : public std::binary_function<Fingerprint, Fingerprint, bool> {
- public:
- bool operator() (const Fingerprint& lhs, const Fingerprint& rhs) const;
- };
-
- // Predicate functor used in maps when X509Certificate is used as the key.
- class LessThan
- : public std::binary_function<X509Certificate*, X509Certificate*, bool> {
- public:
- bool operator() (X509Certificate* lhs, X509Certificate* rhs) const;
- };
-
// A handle to the certificate object in the underlying crypto library.
// We assume that OSCertHandle is a pointer type on all platforms and
// NULL is an invalid OSCertHandle.
@@ -74,62 +54,18 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
typedef std::vector<OSCertHandle> OSCertHandles;
- // Principal represent an X.509 principal.
- struct Principal {
- Principal() { }
- explicit Principal(const std::string& name) : common_name(name) { }
-
- // The different attributes for a principal. They may be "".
- // Note that some of them can have several values.
-
- std::string common_name;
- std::string locality_name;
- std::string state_or_province_name;
- std::string country_name;
-
- std::vector<std::string> street_addresses;
- std::vector<std::string> organization_names;
- std::vector<std::string> organization_unit_names;
- std::vector<std::string> domain_components;
- };
+ // Legacy names for types now defined in x509_cert_types.h.
+ // TODO(snej): Clean up existing code using these names to use the new names.
+ typedef CertPrincipal Principal;
+ typedef CertPolicy Policy;
+ typedef SHA1Fingerprint Fingerprint;
+ typedef SHA1FingerprintLessThan FingerprintLessThan;
- // This class is useful for maintaining policies about which certificates are
- // permitted or forbidden for a particular purpose.
- class Policy {
+ // Predicate functor used in maps when X509Certificate is used as the key.
+ class LessThan
+ : public std::binary_function<X509Certificate*, X509Certificate*, bool> {
public:
- // The judgments this policy can reach.
- enum Judgment {
- // We don't have policy information for this certificate.
- UNKNOWN,
-
- // This certificate is allowed.
- ALLOWED,
-
- // This certificate is denied.
- DENIED,
- };
-
- // Returns the judgment this policy makes about this certificate.
- Judgment Check(X509Certificate* cert) const;
-
- // Causes the policy to allow this certificate.
- void Allow(X509Certificate* cert);
-
- // Causes the policy to deny this certificate.
- void Deny(X509Certificate* cert);
-
- // Returns true if this policy has allowed at least one certificate.
- bool HasAllowedCert() const;
-
- // Returns true if this policy has denied at least one certificate.
- bool HasDeniedCert() const;
-
- private:
- // The set of fingerprints of allowed certificates.
- std::set<Fingerprint, FingerprintLessThan> allowed_;
-
- // The set of fingerprints of denied certificates.
- std::set<Fingerprint, FingerprintLessThan> denied_;
+ bool operator() (X509Certificate* lhs, X509Certificate* rhs) const;
};
// Where the certificate comes from. The enumeration constants are
@@ -231,6 +167,9 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
// Does this certificate's usage allow SSL client authentication?
bool SupportsSSLClientAuth() const;
+ // Do any of the given issuer names appear in this cert's chain of trust?
+ bool IsIssuedBy(const std::vector<CertPrincipal>& valid_issuers);
+
// Creates a security policy for SSL client certificates.
static OSStatus CreateSSLClientPolicy(SecPolicyRef* outPolicy);
@@ -238,8 +177,11 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
// |server_domain| is a hint for which domain the cert is to be sent to
// (a cert previously specified as the default for that domain will be given
// precedence and returned first in the output vector.)
+ // If valid_issuers is non-empty, only certs that were transitively issued by
+ // one of the given names will be included in the list.
static bool GetSSLClientCertificates(
const std::string& server_domain,
+ const std::vector<CertPrincipal>& valid_issuers,
std::vector<scoped_refptr<X509Certificate> >* certs);
// Creates the chain of certs to use for this client identity cert.
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
index 16e7604..a4c37ba 100644
--- a/net/base/x509_certificate_mac.cc
+++ b/net/base/x509_certificate_mac.cc
@@ -5,6 +5,7 @@
#include "net/base/x509_certificate.h"
#include <CommonCrypto/CommonDigest.h>
+#include <Security/Security.h>
#include <time.h>
#include "base/scoped_cftyperef.h"
@@ -80,11 +81,6 @@ namespace {
typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
CFDictionaryRef*);
-inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) {
- return oid1->Length == oid2->Length &&
- (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0);
-}
-
int NetErrorFromOSStatus(OSStatus status) {
switch (status) {
case noErr:
@@ -173,64 +169,6 @@ bool OverrideHostnameMismatch(const std::string& hostname,
return override_hostname_mismatch;
}
-void ParsePrincipal(const CSSM_X509_NAME* name,
- X509Certificate::Principal* principal) {
- std::vector<std::string> common_names, locality_names, state_names,
- country_names;
-
- // TODO(jcampan): add business_category and serial_number.
- const CSSM_OID* kOIDs[] = { &CSSMOID_CommonName,
- &CSSMOID_LocalityName,
- &CSSMOID_StateProvinceName,
- &CSSMOID_CountryName,
- &CSSMOID_StreetAddress,
- &CSSMOID_OrganizationName,
- &CSSMOID_OrganizationalUnitName,
- &CSSMOID_DNQualifier }; // This should be "DC"
- // but is undoubtedly
- // wrong. TODO(avi):
- // Find the right OID.
-
- std::vector<std::string>* values[] = {
- &common_names, &locality_names,
- &state_names, &country_names,
- &(principal->street_addresses),
- &(principal->organization_names),
- &(principal->organization_unit_names),
- &(principal->domain_components) };
- DCHECK(arraysize(kOIDs) == arraysize(values));
-
- for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) {
- CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn];
- for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) {
- CSSM_X509_TYPE_VALUE_PAIR pair_struct =
- rdn_struct.AttributeTypeAndValue[pair];
- for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
- if (CSSMOIDEqual(&pair_struct.type, kOIDs[oid])) {
- std::string value =
- std::string(reinterpret_cast<std::string::value_type*>
- (pair_struct.value.Data),
- pair_struct.value.Length);
- values[oid]->push_back(value);
- break;
- }
- }
- }
- }
-
- // We don't expect to have more than one CN, L, S, and C.
- std::vector<std::string>* single_value_lists[4] = {
- &common_names, &locality_names, &state_names, &country_names };
- std::string* single_values[4] = {
- &principal->common_name, &principal->locality_name,
- &principal->state_or_province_name, &principal->country_name };
- for (size_t i = 0; i < arraysize(single_value_lists); ++i) {
- DCHECK(single_value_lists[i]->size() <= 1);
- if (single_value_lists[i]->size() > 0)
- *(single_values[i]) = (*(single_value_lists[i]))[0];
- }
-}
-
struct CSSMFields {
CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {}
~CSSMFields() {
@@ -386,17 +324,51 @@ OSStatus CreatePolicy(const CSSM_OID* policy_OID,
return noErr;
}
+// Gets the issuer for a given cert, starting with the cert itself and
+// including the intermediate and finally root certificates (if any).
+// This function calls SecTrust but doesn't actually pay attention to the trust
+// result: it shouldn't be used to determine trust, just to traverse the chain.
+// Caller is responsible for releasing the value stored into *out_cert_chain.
+OSStatus CopyCertChain(SecCertificateRef cert_handle,
+ CFArrayRef* out_cert_chain) {
+ DCHECK(cert_handle && out_cert_chain);
+ // Create an SSL policy ref configured for client cert evaluation.
+ SecPolicyRef ssl_policy;
+ OSStatus result = X509Certificate::CreateSSLClientPolicy(&ssl_policy);
+ if (result)
+ return result;
+ scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
+
+ // Create a SecTrustRef.
+ scoped_cftyperef<CFArrayRef> input_certs(
+ CFArrayCreate(NULL, (const void**)&cert_handle, 1,
+ &kCFTypeArrayCallBacks));
+ SecTrustRef trust_ref = NULL;
+ result = SecTrustCreateWithCertificates(input_certs, ssl_policy, &trust_ref);
+ if (result)
+ return result;
+ scoped_cftyperef<SecTrustRef> trust(trust_ref);
+
+ // Evaluate trust, which creates the cert chain.
+ SecTrustResultType status;
+ CSSM_TP_APPLE_EVIDENCE_INFO* status_chain;
+ result = SecTrustEvaluate(trust, &status);
+ if (result)
+ return result;
+ return SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
+}
+
} // namespace
void X509Certificate::Initialize() {
const CSSM_X509_NAME* name;
OSStatus status = SecCertificateGetSubject(cert_handle_, &name);
if (!status) {
- ParsePrincipal(name, &subject_);
+ subject_.Parse(name);
}
status = SecCertificateGetIssuer(cert_handle_, &name);
if (!status) {
- ParsePrincipal(name, &issuer_);
+ issuer_.Parse(name);
}
GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore,
@@ -742,6 +714,34 @@ bool X509Certificate::SupportsSSLClientAuth() const {
return false;
}
+bool X509Certificate::IsIssuedBy(
+ const std::vector<CertPrincipal>& valid_issuers) {
+ // Get the cert's issuer chain.
+ CFArrayRef cert_chain = NULL;
+ OSStatus result;
+ result = CopyCertChain(os_cert_handle(), &cert_chain);
+ if (result != noErr)
+ return false;
+ scoped_cftyperef<CFArrayRef> scoped_cert_chain(cert_chain);
+
+ // Check all the certs in the chain for a match.
+ int n = CFArrayGetCount(cert_chain);
+ for (int i = 0; i < n; ++i) {
+ SecCertificateRef cert_handle = reinterpret_cast<SecCertificateRef>(
+ const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
+ CFRetain(cert_handle);
+ scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
+ cert_handle,
+ X509Certificate::SOURCE_LONE_CERT_IMPORT,
+ X509Certificate::OSCertHandles());
+ for (unsigned j = 0; j < valid_issuers.size(); j++) {
+ if (cert->subject().Matches(valid_issuers[j]))
+ return true;
+ }
+ }
+ return false;
+}
+
// static
OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) {
CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = {
@@ -759,6 +759,7 @@ OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) {
// static
bool X509Certificate::GetSSLClientCertificates (
const std::string& server_domain,
+ const std::vector<Principal>& valid_issuers,
std::vector<scoped_refptr<X509Certificate> >* certs) {
scoped_cftyperef<SecIdentityRef> preferred_identity;
if (!server_domain.empty()) {
@@ -768,11 +769,12 @@ bool X509Certificate::GetSSLClientCertificates (
SecIdentityRef identity = NULL;
if (SecIdentityCopyPreference(domain_str,
0,
- NULL,
+ NULL, // validIssuers argument is ignored :(
&identity) == noErr)
preferred_identity.reset(identity);
}
+ // Now enumerate the identities in the available keychains.
SecIdentitySearchRef search = nil;
OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
scoped_cftyperef<SecIdentitySearchRef> scoped_search(search);
@@ -805,10 +807,20 @@ bool X509Certificate::GetSSLClientCertificates (
if (i < certs->size())
continue;
+ bool is_preferred = preferred_identity &&
+ CFEqual(preferred_identity, identity);
+
+ // Make sure the issuer matches valid_issuers, if given.
+ // But an explicit cert preference overrides this.
+ if (!is_preferred &&
+ valid_issuers.size() > 0 &&
+ !cert->IsIssuedBy(valid_issuers))
+ continue;
+
// The cert passes, so add it to the vector.
// If it's the preferred identity, add it at the start (so it'll be
// selected by default in the UI.)
- if (preferred_identity && CFEqual(preferred_identity, identity))
+ if (is_preferred)
certs->insert(certs->begin(), cert);
else
certs->push_back(cert);
@@ -834,46 +846,20 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const {
CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
CFArrayAppendValue(chain, identity);
- {
- // Create an SSL policy ref configured for client cert evaluation.
- SecPolicyRef ssl_policy;
- result = CreateSSLClientPolicy(&ssl_policy);
- if (result)
- goto exit;
- scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
-
- // Use a SecTrust object to find the intermediate certs in the trust chain.
- scoped_cftyperef<CFArrayRef> input_certs(
- CFArrayCreate(NULL, (const void**)&cert_handle_, 1,
- &kCFTypeArrayCallBacks));
- SecTrustRef trust_ref = NULL;
- result = SecTrustCreateWithCertificates(input_certs,
- ssl_policy,
- &trust_ref);
- if (result)
- goto exit;
- scoped_cftyperef<SecTrustRef> trust(trust_ref);
-
- SecTrustResultType status;
- CFArrayRef trust_chain = NULL;
- CSSM_TP_APPLE_EVIDENCE_INFO* status_chain;
- result = SecTrustEvaluate(trust, &status);
- if (result)
- goto exit;
- result = SecTrustGetResult(trust, &status, &trust_chain, &status_chain);
- if (result)
- goto exit;
-
- // Append the intermediate certs from SecTrust to the result array:
- if (trust_chain) {
- int chain_count = CFArrayGetCount(trust_chain);
- if (chain_count > 1) {
- CFArrayAppendArray(chain,
- trust_chain,
- CFRangeMake(1, chain_count - 1));
- }
- CFRelease(trust_chain);
+ CFArrayRef cert_chain = NULL;
+ result = CopyCertChain(cert_handle_, &cert_chain);
+ if (result)
+ goto exit;
+
+ // Append the intermediate certs from SecTrust to the result array:
+ if (cert_chain) {
+ int chain_count = CFArrayGetCount(cert_chain);
+ if (chain_count > 1) {
+ CFArrayAppendArray(chain,
+ cert_chain,
+ CFRangeMake(1, chain_count - 1));
}
+ CFRelease(cert_chain);
}
exit:
if (result)
diff --git a/net/net.gyp b/net/net.gyp
index a6ed78d..34e77fc 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -161,6 +161,9 @@
'base/x509_certificate_mac.cc',
'base/x509_certificate_nss.cc',
'base/x509_certificate_win.cc',
+ 'base/x509_cert_types.cc',
+ 'base/x509_cert_types.h',
+ 'base/x509_cert_types_mac.cc',
],
'export_dependent_settings': [
'../base/base.gyp:base',
@@ -633,6 +636,7 @@
'base/test_completion_callback_unittest.cc',
'base/upload_data_stream_unittest.cc',
'base/x509_certificate_unittest.cc',
+ 'base/x509_cert_types_unittest.cc',
'disk_cache/addr_unittest.cc',
'disk_cache/backend_unittest.cc',
'disk_cache/bitmap_unittest.cc',
diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc
index 4cf7722..ad3c747 100644
--- a/net/socket/ssl_client_socket_mac.cc
+++ b/net/socket/ssl_client_socket_mac.cc
@@ -655,19 +655,32 @@ void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) {
void SSLClientSocketMac::GetSSLCertRequestInfo(
SSLCertRequestInfo* cert_request_info) {
// I'm being asked for available client certs (identities).
-
- CFArrayRef allowed_issuer_names = NULL;
- if (SSLCopyDistinguishedNames(ssl_context_, &allowed_issuer_names) == noErr &&
- allowed_issuer_names != NULL) {
- SSL_LOG << "Server has " << CFArrayGetCount(allowed_issuer_names)
- << " allowed issuer names";
- CFRelease(allowed_issuer_names);
- // TODO(snej): Filter GetSSLClientCertificates using this array.
+ // First, get the cert issuer names allowed by the server.
+ std::vector<CertPrincipal> valid_issuers;
+ CFArrayRef valid_issuer_names = NULL;
+ if (SSLCopyDistinguishedNames(ssl_context_, &valid_issuer_names) == noErr &&
+ valid_issuer_names != NULL) {
+ SSL_LOG << "Server has " << CFArrayGetCount(valid_issuer_names)
+ << " valid issuer names";
+ int n = CFArrayGetCount(valid_issuer_names);
+ for (int i = 0; i < n; i++) {
+ // Parse each name into a CertPrincipal object.
+ CFDataRef issuer = reinterpret_cast<CFDataRef>(
+ CFArrayGetValueAtIndex(valid_issuer_names, i));
+ CertPrincipal p;
+ if (p.ParseDistinguishedName(CFDataGetBytePtr(issuer),
+ CFDataGetLength(issuer))) {
+ valid_issuers.push_back(p);
+ }
+ }
+ CFRelease(valid_issuer_names);
}
+ // Now get the available client certs whose issuers are allowed by the server.
cert_request_info->host_and_port = hostname_;
cert_request_info->client_certs.clear();
X509Certificate::GetSSLClientCertificates(hostname_,
+ valid_issuers,
&cert_request_info->client_certs);
SSL_LOG << "Asking user to choose between "
<< cert_request_info->client_certs.size() << " client certs...";