summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authordroger@chromium.org <droger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-18 09:04:33 +0000
committerdroger@chromium.org <droger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-18 09:04:33 +0000
commitd4158950608f079e527a1e9cb085440ab98aff08 (patch)
treedd21bacc22a9fda3d18a4a8f100a24ea1effa640 /net
parent47263cb1a4253ff51e157749499b293c8399dd48 (diff)
downloadchromium_src-d4158950608f079e527a1e9cb085440ab98aff08.zip
chromium_src-d4158950608f079e527a1e9cb085440ab98aff08.tar.gz
chromium_src-d4158950608f079e527a1e9cb085440ab98aff08.tar.bz2
Support x509 certificate on iOS.
The iOS implementation uses both NSS and system API. This CL creates x509_certificate_nss_util to allow iOS to share code with the NSS implementation. The iOS specific code is in x509_certificate_ios. BUG=145954 Review URL: https://chromiumcodereview.appspot.com/10928107 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@157325 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/ev_root_ca_metadata.cc6
-rw-r--r--net/base/ev_root_ca_metadata.h8
-rw-r--r--net/base/x509_certificate_ios.cc230
-rw-r--r--net/base/x509_certificate_nss.cc209
-rw-r--r--net/base/x509_certificate_unittest.cc5
-rw-r--r--net/base/x509_util_ios.cc77
-rw-r--r--net/base/x509_util_ios.h43
-rw-r--r--net/base/x509_util_nss.cc224
-rw-r--r--net/base/x509_util_nss.h44
-rw-r--r--net/net.gyp67
10 files changed, 684 insertions, 229 deletions
diff --git a/net/base/ev_root_ca_metadata.cc b/net/base/ev_root_ca_metadata.cc
index 9901ff3..23378ca 100644
--- a/net/base/ev_root_ca_metadata.cc
+++ b/net/base/ev_root_ca_metadata.cc
@@ -4,7 +4,7 @@
#include "net/base/ev_root_ca_metadata.h"
-#if defined(USE_NSS)
+#if defined(USE_NSS) || defined(OS_IOS)
#include <cert.h>
#include <pkcs11n.h>
#include <secerr.h>
@@ -15,7 +15,7 @@
#include "base/lazy_instance.h"
#include "base/logging.h"
-#if defined(USE_NSS)
+#if defined(USE_NSS) || defined(OS_IOS)
#include "crypto/nss_util.h"
#endif
@@ -319,7 +319,7 @@ EVRootCAMetadata* EVRootCAMetadata::GetInstance() {
return g_ev_root_ca_metadata.Pointer();
}
-#if defined(USE_NSS)
+#if defined(USE_NSS) || defined(OS_IOS)
bool EVRootCAMetadata::IsEVPolicyOID(PolicyOID policy_oid) const {
return policy_oids_.find(policy_oid) != policy_oids_.end();
}
diff --git a/net/base/ev_root_ca_metadata.h b/net/base/ev_root_ca_metadata.h
index 57ebe80..292c5ea 100644
--- a/net/base/ev_root_ca_metadata.h
+++ b/net/base/ev_root_ca_metadata.h
@@ -7,7 +7,7 @@
#include "build/build_config.h"
-#if defined(USE_NSS)
+#if defined(USE_NSS) || defined(OS_IOS)
#include <secoidt.h>
#endif
@@ -30,7 +30,7 @@ namespace net {
// extended-validation (EV) certificates.
class NET_EXPORT_PRIVATE EVRootCAMetadata {
public:
-#if defined(USE_NSS)
+#if defined(USE_NSS) || defined(OS_IOS)
typedef SECOidTag PolicyOID;
#elif defined(OS_WIN)
typedef const char* PolicyOID;
@@ -38,7 +38,7 @@ class NET_EXPORT_PRIVATE EVRootCAMetadata {
static EVRootCAMetadata* GetInstance();
-#if defined(USE_NSS) || defined(OS_WIN)
+#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_IOS)
// Returns true if policy_oid is an EV policy OID of some root CA.
bool IsEVPolicyOID(PolicyOID policy_oid) const;
@@ -63,7 +63,7 @@ class NET_EXPORT_PRIVATE EVRootCAMetadata {
EVRootCAMetadata();
~EVRootCAMetadata();
-#if defined(USE_NSS)
+#if defined(USE_NSS) || defined(OS_IOS)
typedef std::map<SHA1HashValue, std::vector<PolicyOID>,
SHA1HashValueLessThan> PolicyOIDMap;
diff --git a/net/base/x509_certificate_ios.cc b/net/base/x509_certificate_ios.cc
new file mode 100644
index 0000000..0286203
--- /dev/null
+++ b/net/base/x509_certificate_ios.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2012 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_certificate.h"
+
+#include <CommonCrypto/CommonDigest.h>
+#include <Security/Security.h>
+#include <vector>
+
+#include <cryptohi.h>
+#include <cert.h>
+#include <keyhi.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <prerror.h>
+#include <prtime.h>
+#include <prtypes.h>
+#include <secder.h>
+#include <secerr.h>
+#include <sslerr.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/time.h"
+#include "crypto/nss_util.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/base/asn1_util.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/cert_verify_result.h"
+#include "net/base/ev_root_ca_metadata.h"
+#include "net/base/net_errors.h"
+#include "net/base/x509_util_ios.h"
+#include "net/base/x509_util_nss.h"
+
+using base::mac::ScopedCFTypeRef;
+
+namespace net {
+namespace {
+// Returns true if a given |cert_handle| is actually a valid X.509 certificate
+// handle.
+//
+// SecCertificateCreateFromData() does not always force the immediate parsing of
+// the certificate, and as such, may return a SecCertificateRef for an
+// invalid/unparsable certificate. Force parsing to occur to ensure that the
+// SecCertificateRef is correct. On later versions where
+// SecCertificateCreateFromData() immediately parses, rather than lazily, this
+// call is cheap, as the subject is cached.
+bool IsValidOSCertHandle(SecCertificateRef cert_handle) {
+ ScopedCFTypeRef<CFStringRef> sanity_check(
+ SecCertificateCopySubjectSummary(cert_handle));
+ return sanity_check != NULL;
+}
+} // namespace
+
+void X509Certificate::Initialize() {
+ x509_util_ios::NSSCertificate nss_cert(cert_handle_);
+ CERTCertificate* cert_handle = nss_cert.cert_handle();
+ if (cert_handle) {
+ x509_util::ParsePrincipal(&cert_handle->subject, &subject_);
+ x509_util::ParsePrincipal(&cert_handle->issuer, &issuer_);
+ x509_util::ParseDate(&cert_handle->validity.notBefore, &valid_start_);
+ x509_util::ParseDate(&cert_handle->validity.notAfter, &valid_expiry_);
+ serial_number_ = x509_util::ParseSerialNumber(cert_handle);
+ }
+ fingerprint_ = CalculateFingerprint(cert_handle_);
+ ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
+}
+
+// static
+X509Certificate* X509Certificate::CreateSelfSigned(
+ crypto::RSAPrivateKey* key,
+ const std::string& subject,
+ uint32 serial_number,
+ base::TimeDelta valid_duration) {
+ DCHECK(key);
+ DCHECK(!subject.empty());
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+void X509Certificate::GetSubjectAltName(
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) const {
+ x509_util_ios::NSSCertificate nss_cert(cert_handle_);
+ CERTCertificate* cert_handle = nss_cert.cert_handle();
+ if (!cert_handle) {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+ return;
+ }
+ x509_util::GetSubjectAltName(cert_handle, dns_names, ip_addrs);
+}
+
+// static
+bool X509Certificate::GetDEREncoded(OSCertHandle cert_handle,
+ std::string* encoded) {
+ ScopedCFTypeRef<CFDataRef> der_data(SecCertificateCopyData(cert_handle));
+ if (!der_data)
+ return false;
+ encoded->assign(reinterpret_cast<const char*>(CFDataGetBytePtr(der_data)),
+ CFDataGetLength(der_data));
+ return true;
+}
+
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+ X509Certificate::OSCertHandle b) {
+ DCHECK(a && b);
+ if (a == b)
+ return true;
+ if (CFEqual(a, b))
+ return true;
+ ScopedCFTypeRef<CFDataRef> a_data(SecCertificateCopyData(a));
+ ScopedCFTypeRef<CFDataRef> b_data(SecCertificateCopyData(b));
+ return a_data && b_data &&
+ CFDataGetLength(a_data) == CFDataGetLength(b_data) &&
+ memcmp(CFDataGetBytePtr(a_data), CFDataGetBytePtr(b_data),
+ CFDataGetLength(a_data)) == 0;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
+ const char* data, int length) {
+ ScopedCFTypeRef<CFDataRef> cert_data(CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(data), length,
+ kCFAllocatorNull));
+ if (!cert_data)
+ return NULL;
+ OSCertHandle cert_handle = SecCertificateCreateWithData(NULL, cert_data);
+ if (!cert_handle)
+ return NULL;
+ if (!IsValidOSCertHandle(cert_handle)) {
+ CFRelease(cert_handle);
+ return NULL;
+ }
+ return cert_handle;
+}
+
+// static
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
+ const char* data,
+ int length,
+ Format format) {
+ return x509_util::CreateOSCertHandlesFromBytes(data, length, format);
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+ OSCertHandle handle) {
+ if (!handle)
+ return NULL;
+ return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle)));
+}
+
+// static
+void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
+ CFRelease(cert_handle);
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateFingerprint(
+ OSCertHandle cert) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert));
+ if (!cert_data)
+ return sha1;
+ DCHECK(CFDataGetBytePtr(cert_data));
+ DCHECK_NE(0, CFDataGetLength(cert_data));
+ CC_SHA1(CFDataGetBytePtr(cert_data), CFDataGetLength(cert_data), sha1.data);
+
+ return sha1;
+}
+
+// static
+SHA1HashValue X509Certificate::CalculateCAFingerprint(
+ const OSCertHandles& intermediates) {
+ SHA1HashValue sha1;
+ memset(sha1.data, 0, sizeof(sha1.data));
+
+ // The CC_SHA(3cc) man page says all CC_SHA1_xxx routines return 1, so
+ // we don't check their return values.
+ CC_SHA1_CTX sha1_ctx;
+ CC_SHA1_Init(&sha1_ctx);
+ for (size_t i = 0; i < intermediates.size(); ++i) {
+ ScopedCFTypeRef<CFDataRef>
+ cert_data(SecCertificateCopyData(intermediates[i]));
+ if (!cert_data)
+ return sha1;
+ CC_SHA1_Update(&sha1_ctx,
+ CFDataGetBytePtr(cert_data),
+ CFDataGetLength(cert_data));
+ }
+ CC_SHA1_Final(sha1.data, &sha1_ctx);
+ return sha1;
+}
+
+// static
+X509Certificate::OSCertHandle
+X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
+ return x509_util::ReadOSCertHandleFromPickle(pickle_iter);
+}
+
+// static
+bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
+ Pickle* pickle) {
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert_handle));
+ if (!cert_data)
+ return false;
+
+ return pickle->WriteData(
+ reinterpret_cast<const char*>(CFDataGetBytePtr(cert_data)),
+ CFDataGetLength(cert_data));
+}
+
+// static
+void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
+ size_t* size_bits,
+ PublicKeyType* type) {
+ x509_util_ios::NSSCertificate nss_cert(cert_handle);
+ x509_util::GetPublicKeyInfo(nss_cert.cert_handle(), size_bits, type);
+}
+
+} // namespace net
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
index 060f4f7..54d2197 100644
--- a/net/base/x509_certificate_nss.cc
+++ b/net/base/x509_certificate_nss.cc
@@ -19,110 +19,21 @@
#include "base/time.h"
#include "crypto/nss_util.h"
#include "crypto/rsa_private_key.h"
-#include "crypto/scoped_nss_types.h"
#include "net/base/x509_util_nss.h"
namespace net {
-namespace {
-
-void ParsePrincipal(CERTName* name,
- CertPrincipal* principal) {
- typedef char* (*CERTGetNameFunc)(CERTName* name);
-
- // TODO(jcampan): add business_category and serial_number.
- // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and
- // CERT_GetDomainComponentName functions, but they return only the most
- // general (the first) RDN. NSS doesn't have a function for the street
- // address.
- static const SECOidTag kOIDs[] = {
- SEC_OID_AVA_STREET_ADDRESS,
- SEC_OID_AVA_ORGANIZATION_NAME,
- SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME,
- SEC_OID_AVA_DC };
-
- std::vector<std::string>* values[] = {
- &principal->street_addresses,
- &principal->organization_names,
- &principal->organization_unit_names,
- &principal->domain_components };
- DCHECK(arraysize(kOIDs) == arraysize(values));
-
- CERTRDN** rdns = name->rdns;
- for (size_t rdn = 0; rdns[rdn]; ++rdn) {
- CERTAVA** avas = rdns[rdn]->avas;
- for (size_t pair = 0; avas[pair] != 0; ++pair) {
- SECOidTag tag = CERT_GetAVATag(avas[pair]);
- for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
- if (kOIDs[oid] == tag) {
- SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value);
- if (!decode_item)
- break;
- // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote.
- std::string value(reinterpret_cast<char*>(decode_item->data),
- decode_item->len);
- values[oid]->push_back(value);
- SECITEM_FreeItem(decode_item, PR_TRUE);
- break;
- }
- }
- }
- }
-
- // Get CN, L, S, and C.
- CERTGetNameFunc get_name_funcs[4] = {
- CERT_GetCommonName, CERT_GetLocalityName,
- CERT_GetStateName, CERT_GetCountryName };
- 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(get_name_funcs); ++i) {
- char* value = get_name_funcs[i](name);
- if (value) {
- single_values[i]->assign(value);
- PORT_Free(value);
- }
- }
-}
-
-void ParseDate(SECItem* der_date, base::Time* result) {
- PRTime prtime;
- SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date);
- DCHECK_EQ(SECSuccess, rv);
- *result = crypto::PRTimeToBaseTime(prtime);
-}
-
-SECStatus PR_CALLBACK
-CollectCertsCallback(void* arg, SECItem** certs, int num_certs) {
- X509Certificate::OSCertHandles* results =
- reinterpret_cast<X509Certificate::OSCertHandles*>(arg);
-
- for (int i = 0; i < num_certs; ++i) {
- X509Certificate::OSCertHandle handle =
- X509Certificate::CreateOSCertHandleFromBytes(
- reinterpret_cast<char*>(certs[i]->data), certs[i]->len);
- if (handle)
- results->push_back(handle);
- }
-
- return SECSuccess;
-}
-
-} // namespace
-
void X509Certificate::Initialize() {
- ParsePrincipal(&cert_handle_->subject, &subject_);
- ParsePrincipal(&cert_handle_->issuer, &issuer_);
+ x509_util::ParsePrincipal(&cert_handle_->subject, &subject_);
+ x509_util::ParsePrincipal(&cert_handle_->issuer, &issuer_);
- ParseDate(&cert_handle_->validity.notBefore, &valid_start_);
- ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_);
+ x509_util::ParseDate(&cert_handle_->validity.notBefore, &valid_start_);
+ x509_util::ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_);
fingerprint_ = CalculateFingerprint(cert_handle_);
ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
- serial_number_ = std::string(
- reinterpret_cast<char*>(cert_handle_->serialNumber.data),
- cert_handle_->serialNumber.len);
+ serial_number_ = x509_util::ParseSerialNumber(cert_handle_);
}
// static
@@ -216,7 +127,6 @@ X509Certificate* X509Certificate::CreateSelfSigned(
uint32 serial_number,
base::TimeDelta valid_duration) {
DCHECK(key);
-
base::Time not_valid_before = base::Time::Now();
base::Time not_valid_after = not_valid_before + valid_duration;
CERTCertificate* cert = x509_util::CreateSelfSignedCert(key->public_key(),
@@ -225,7 +135,6 @@ X509Certificate* X509Certificate::CreateSelfSigned(
serial_number,
not_valid_before,
not_valid_after);
-
if (!cert)
return NULL;
@@ -238,44 +147,7 @@ X509Certificate* X509Certificate::CreateSelfSigned(
void X509Certificate::GetSubjectAltName(
std::vector<std::string>* dns_names,
std::vector<std::string>* ip_addrs) const {
- if (dns_names)
- dns_names->clear();
- if (ip_addrs)
- ip_addrs->clear();
-
- SECItem alt_name;
- SECStatus rv = CERT_FindCertExtension(cert_handle_,
- SEC_OID_X509_SUBJECT_ALT_NAME,
- &alt_name);
- if (rv != SECSuccess)
- return;
-
- PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- DCHECK(arena != NULL);
-
- CERTGeneralName* alt_name_list;
- alt_name_list = CERT_DecodeAltNameExtension(arena, &alt_name);
- SECITEM_FreeItem(&alt_name, PR_FALSE);
-
- CERTGeneralName* name = alt_name_list;
- while (name) {
- // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs
- // respectively, both of which can be byte copied from
- // SECItemType::data into the appropriate output vector.
- if (dns_names && name->type == certDNSName) {
- dns_names->push_back(std::string(
- reinterpret_cast<char*>(name->name.other.data),
- name->name.other.len));
- } else if (ip_addrs && name->type == certIPAddress) {
- ip_addrs->push_back(std::string(
- reinterpret_cast<char*>(name->name.other.data),
- name->name.other.len));
- }
- name = CERT_GetNextGeneralName(name);
- if (name == alt_name_list)
- break;
- }
- PORT_FreeArena(arena, PR_FALSE);
+ x509_util::GetSubjectAltName(cert_handle_, dns_names, ip_addrs);
}
bool X509Certificate::VerifyNameMatch(const std::string& hostname) const {
@@ -338,38 +210,7 @@ X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
const char* data,
int length,
Format format) {
- OSCertHandles results;
- if (length < 0)
- return results;
-
- crypto::EnsureNSSInit();
-
- if (!NSS_IsInitialized())
- return results;
-
- switch (format) {
- case FORMAT_SINGLE_CERTIFICATE: {
- OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
- if (handle)
- results.push_back(handle);
- break;
- }
- case FORMAT_PKCS7: {
- // Make a copy since CERT_DecodeCertPackage may modify it
- std::vector<char> data_copy(data, data + length);
-
- SECStatus result = CERT_DecodeCertPackage(&data_copy[0],
- length, CollectCertsCallback, &results);
- if (result != SECSuccess)
- results.clear();
- break;
- }
- default:
- NOTREACHED() << "Certificate format " << format << " unimplemented";
- break;
- }
-
- return results;
+ return x509_util::CreateOSCertHandlesFromBytes(data, length, format);
}
// static
@@ -423,12 +264,7 @@ SHA1HashValue X509Certificate::CalculateCAFingerprint(
// static
X509Certificate::OSCertHandle
X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
- const char* data;
- int length;
- if (!pickle_iter->ReadData(&data, &length))
- return NULL;
-
- return CreateOSCertHandleFromBytes(data, length);
+ return x509_util::ReadOSCertHandleFromPickle(pickle_iter);
}
// static
@@ -443,34 +279,7 @@ bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
size_t* size_bits,
PublicKeyType* type) {
- // Since we might fail, set the output parameters to default values first.
- *type = kPublicKeyTypeUnknown;
- *size_bits = 0;
-
- crypto::ScopedSECKEYPublicKey key(CERT_ExtractPublicKey(cert_handle));
- if (!key.get())
- return;
-
- *size_bits = SECKEY_PublicKeyStrengthInBits(key.get());
-
- switch (key->keyType) {
- case rsaKey:
- *type = kPublicKeyTypeRSA;
- break;
- case dsaKey:
- *type = kPublicKeyTypeDSA;
- break;
- case dhKey:
- *type = kPublicKeyTypeDH;
- break;
- case ecKey:
- *type = kPublicKeyTypeECDSA;
- break;
- default:
- *type = kPublicKeyTypeUnknown;
- *size_bits = 0;
- break;
- }
+ x509_util::GetPublicKeyInfo(cert_handle, size_bits, type);
}
} // namespace net
diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc
index f8f43fc..c16ad87 100644
--- a/net/base/x509_certificate_unittest.cc
+++ b/net/base/x509_certificate_unittest.cc
@@ -680,6 +680,8 @@ TEST(X509CertificateTest, IntermediateCertificates) {
X509Certificate::FreeOSCertHandle(google_handle);
}
+#if !defined(OS_IOS)
+// TODO(ios): Not yet implemented on iOS.
#if defined(OS_MACOSX)
TEST(X509CertificateTest, IsIssuedBy) {
FilePath certs_dir = GetTestCertsDirectory();
@@ -726,7 +728,9 @@ TEST(X509CertificateTest, IsIssuedBy) {
EXPECT_FALSE(mit_davidben_cert->IsIssuedBy(foaf_issuers));
}
#endif // defined(OS_MACOSX)
+#endif // !defined(OS_IOS)
+#if !defined(OS_IOS) // TODO(ios): Unable to create certificates.
#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX)
// This test creates a self-signed cert from a private key and then verify the
// content of the certificate.
@@ -850,6 +854,7 @@ TEST(X509CertificateTest, GetDEREncoded) {
EXPECT_FALSE(der_cert.empty());
}
#endif
+#endif // !defined(OS_IOS)
class X509CertificateParseTest
: public testing::TestWithParam<CertificateFormatTestData> {
diff --git a/net/base/x509_util_ios.cc b/net/base/x509_util_ios.cc
new file mode 100644
index 0000000..a5be412
--- /dev/null
+++ b/net/base/x509_util_ios.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 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_util_ios.h"
+
+#include <cert.h>
+#include <nss.h>
+#include <prtypes.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "crypto/nss_util.h"
+#include "net/base/x509_certificate.h"
+
+using base::mac::ScopedCFTypeRef;
+
+namespace net {
+namespace x509_util_ios {
+
+namespace {
+
+// Creates an NSS certificate handle from |data|, which is |length| bytes in
+// size.
+CERTCertificate* CreateNSSCertHandleFromBytes(const char* data,
+ int length) {
+ if (length < 0)
+ return NULL;
+
+ crypto::EnsureNSSInit();
+
+ if (!NSS_IsInitialized())
+ return NULL;
+
+ SECItem der_cert;
+ der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
+ der_cert.len = length;
+ der_cert.type = siDERCertBuffer;
+
+ // Parse into a certificate structure.
+ return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL,
+ PR_FALSE, PR_TRUE);
+}
+
+} // namespace
+
+CERTCertificate* CreateNSSCertHandleFromOSHandle(
+ SecCertificateRef cert_handle) {
+ ScopedCFTypeRef<CFDataRef> cert_data(SecCertificateCopyData(cert_handle));
+ return CreateNSSCertHandleFromBytes(
+ reinterpret_cast<const char*>(CFDataGetBytePtr(cert_data)),
+ CFDataGetLength(cert_data));
+}
+
+SecCertificateRef CreateOSCertHandleFromNSSHandle(
+ CERTCertificate* nss_cert_handle) {
+ return X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<const char*>(nss_cert_handle->derCert.data),
+ nss_cert_handle->derCert.len);
+}
+
+NSSCertificate::NSSCertificate(SecCertificateRef cert_handle) {
+ nss_cert_handle_ = CreateNSSCertHandleFromOSHandle(cert_handle);
+ DLOG_IF(INFO, cert_handle && !nss_cert_handle_)
+ << "Could not convert SecCertificateRef to CERTCertificate*";
+}
+
+NSSCertificate::~NSSCertificate() {
+ CERT_DestroyCertificate(nss_cert_handle_);
+}
+
+CERTCertificate* NSSCertificate::cert_handle() {
+ return nss_cert_handle_;
+}
+
+} // namespace x509_util_ios
+} // namespace net
+
diff --git a/net/base/x509_util_ios.h b/net/base/x509_util_ios.h
new file mode 100644
index 0000000..641ddba
--- /dev/null
+++ b/net/base/x509_util_ios.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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.
+
+// This file contains functions for iOS to glue NSS and Security.framework
+// together.
+
+#ifndef NET_BASE_X509_UTIL_IOS_H_
+#define NET_BASE_X509_UTIL_IOS_H_
+
+#include <Security/Security.h>
+
+// Forward declaration; real one in <cert.h>
+typedef struct CERTCertificateStr CERTCertificate;
+
+namespace net {
+namespace x509_util_ios {
+
+// Converts a Security.framework certificate handle (SecCertificateRef) into
+// an NSS certificate handle (CERTCertificate*).
+CERTCertificate* CreateNSSCertHandleFromOSHandle(SecCertificateRef cert_handle);
+
+// Converts an NSS certificate handle (CERTCertificate*) into a
+// Security.framework handle (SecCertificateRef)
+SecCertificateRef CreateOSCertHandleFromNSSHandle(
+ CERTCertificate* nss_cert_handle);
+
+// This is a wrapper class around the native NSS certificate handle.
+// The constructor copies the certificate data from |cert_handle| and
+// uses the NSS library to parse it.
+class NSSCertificate {
+ public:
+ explicit NSSCertificate(SecCertificateRef cert_handle);
+ ~NSSCertificate();
+ CERTCertificate* cert_handle();
+ private:
+ CERTCertificate* nss_cert_handle_;
+};
+
+} // namespace x509_util_ios
+} // namespace net
+
+#endif // NET_BASE_X509_UTIL_IOS_H_
diff --git a/net/base/x509_util_nss.cc b/net/base/x509_util_nss.cc
index 1234d94..a4eaf6f 100644
--- a/net/base/x509_util_nss.cc
+++ b/net/base/x509_util_nss.cc
@@ -7,8 +7,10 @@
#include <cert.h>
#include <cryptohi.h>
+#include <nss.h>
#include <pk11pub.h>
#include <prerror.h>
+#include <secder.h>
#include <secmod.h>
#include <secport.h>
@@ -16,11 +18,15 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
+#include "base/pickle.h"
#include "crypto/ec_private_key.h"
#include "crypto/nss_util.h"
#include "crypto/nss_util_internal.h"
#include "crypto/scoped_nss_types.h"
#include "crypto/third_party/nss/chromium-nss.h"
+#include "net/base/x509_certificate.h"
+
+namespace net {
namespace {
@@ -248,9 +254,26 @@ bool CreateDomainBoundCertInternal(
return true;
}
-} // namespace
+// Callback for CERT_DecodeCertPackage(), used in
+// CreateOSCertHandlesFromBytes().
+SECStatus PR_CALLBACK CollectCertsCallback(void* arg,
+ SECItem** certs,
+ int num_certs) {
+ X509Certificate::OSCertHandles* results =
+ reinterpret_cast<X509Certificate::OSCertHandles*>(arg);
+
+ for (int i = 0; i < num_certs; ++i) {
+ X509Certificate::OSCertHandle handle =
+ X509Certificate::CreateOSCertHandleFromBytes(
+ reinterpret_cast<char*>(certs[i]->data), certs[i]->len);
+ if (handle)
+ results->push_back(handle);
+ }
-namespace net {
+ return SECSuccess;
+}
+
+} // namespace
namespace x509_util {
@@ -307,6 +330,203 @@ bool CreateDomainBoundCertEC(
der_cert);
}
+#if defined(USE_NSS) || defined(OS_IOS)
+void ParsePrincipal(CERTName* name, CertPrincipal* principal) {
+ typedef char* (*CERTGetNameFunc)(CERTName* name);
+
+ // TODO(jcampan): add business_category and serial_number.
+ // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and
+ // CERT_GetDomainComponentName functions, but they return only the most
+ // general (the first) RDN. NSS doesn't have a function for the street
+ // address.
+ static const SECOidTag kOIDs[] = {
+ SEC_OID_AVA_STREET_ADDRESS,
+ SEC_OID_AVA_ORGANIZATION_NAME,
+ SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME,
+ SEC_OID_AVA_DC };
+
+ std::vector<std::string>* values[] = {
+ &principal->street_addresses,
+ &principal->organization_names,
+ &principal->organization_unit_names,
+ &principal->domain_components };
+ DCHECK_EQ(arraysize(kOIDs), arraysize(values));
+
+ CERTRDN** rdns = name->rdns;
+ for (size_t rdn = 0; rdns[rdn]; ++rdn) {
+ CERTAVA** avas = rdns[rdn]->avas;
+ for (size_t pair = 0; avas[pair] != 0; ++pair) {
+ SECOidTag tag = CERT_GetAVATag(avas[pair]);
+ for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
+ if (kOIDs[oid] == tag) {
+ SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value);
+ if (!decode_item)
+ break;
+ // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote.
+ std::string value(reinterpret_cast<char*>(decode_item->data),
+ decode_item->len);
+ values[oid]->push_back(value);
+ SECITEM_FreeItem(decode_item, PR_TRUE);
+ break;
+ }
+ }
+ }
+ }
+
+ // Get CN, L, S, and C.
+ CERTGetNameFunc get_name_funcs[4] = {
+ CERT_GetCommonName, CERT_GetLocalityName,
+ CERT_GetStateName, CERT_GetCountryName };
+ 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(get_name_funcs); ++i) {
+ char* value = get_name_funcs[i](name);
+ if (value) {
+ single_values[i]->assign(value);
+ PORT_Free(value);
+ }
+ }
+}
+
+void ParseDate(const SECItem* der_date, base::Time* result) {
+ PRTime prtime;
+ SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date);
+ DCHECK_EQ(SECSuccess, rv);
+ *result = crypto::PRTimeToBaseTime(prtime);
+}
+
+std::string ParseSerialNumber(const CERTCertificate* certificate) {
+ return std::string(reinterpret_cast<char*>(certificate->serialNumber.data),
+ certificate->serialNumber.len);
+}
+
+void GetSubjectAltName(CERTCertificate* cert_handle,
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs) {
+ if (dns_names)
+ dns_names->clear();
+ if (ip_addrs)
+ ip_addrs->clear();
+
+ SECItem alt_name;
+ SECStatus rv = CERT_FindCertExtension(cert_handle,
+ SEC_OID_X509_SUBJECT_ALT_NAME,
+ &alt_name);
+ if (rv != SECSuccess)
+ return;
+
+ PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ DCHECK(arena != NULL);
+
+ CERTGeneralName* alt_name_list;
+ alt_name_list = CERT_DecodeAltNameExtension(arena, &alt_name);
+ SECITEM_FreeItem(&alt_name, PR_FALSE);
+
+ CERTGeneralName* name = alt_name_list;
+ while (name) {
+ // DNSName and IPAddress are encoded as IA5String and OCTET STRINGs
+ // respectively, both of which can be byte copied from
+ // SECItemType::data into the appropriate output vector.
+ if (dns_names && name->type == certDNSName) {
+ dns_names->push_back(std::string(
+ reinterpret_cast<char*>(name->name.other.data),
+ name->name.other.len));
+ } else if (ip_addrs && name->type == certIPAddress) {
+ ip_addrs->push_back(std::string(
+ reinterpret_cast<char*>(name->name.other.data),
+ name->name.other.len));
+ }
+ name = CERT_GetNextGeneralName(name);
+ if (name == alt_name_list)
+ break;
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+}
+
+X509Certificate::OSCertHandles CreateOSCertHandlesFromBytes(
+ const char* data,
+ int length,
+ X509Certificate::Format format) {
+ X509Certificate::OSCertHandles results;
+ if (length < 0)
+ return results;
+
+ crypto::EnsureNSSInit();
+
+ if (!NSS_IsInitialized())
+ return results;
+
+ switch (format) {
+ case X509Certificate::FORMAT_SINGLE_CERTIFICATE: {
+ X509Certificate::OSCertHandle handle =
+ X509Certificate::CreateOSCertHandleFromBytes(data, length);
+ if (handle)
+ results.push_back(handle);
+ break;
+ }
+ case X509Certificate::FORMAT_PKCS7: {
+ // Make a copy since CERT_DecodeCertPackage may modify it
+ std::vector<char> data_copy(data, data + length);
+
+ SECStatus result = CERT_DecodeCertPackage(&data_copy[0],
+ length, CollectCertsCallback, &results);
+ if (result != SECSuccess)
+ results.clear();
+ break;
+ }
+ default:
+ NOTREACHED() << "Certificate format " << format << " unimplemented";
+ break;
+ }
+
+ return results;
+}
+
+X509Certificate::OSCertHandle ReadOSCertHandleFromPickle(
+ PickleIterator* pickle_iter) {
+ const char* data;
+ int length;
+ if (!pickle_iter->ReadData(&data, &length))
+ return NULL;
+
+ return X509Certificate::CreateOSCertHandleFromBytes(data, length);
+}
+
+void GetPublicKeyInfo(CERTCertificate* handle,
+ size_t* size_bits,
+ X509Certificate::PublicKeyType* type) {
+ // Since we might fail, set the output parameters to default values first.
+ *type = X509Certificate::kPublicKeyTypeUnknown;
+ *size_bits = 0;
+
+ crypto::ScopedSECKEYPublicKey key(CERT_ExtractPublicKey(handle));
+ if (!key.get())
+ return;
+
+ *size_bits = SECKEY_PublicKeyStrengthInBits(key.get());
+
+ switch (key->keyType) {
+ case rsaKey:
+ *type = X509Certificate::kPublicKeyTypeRSA;
+ break;
+ case dsaKey:
+ *type = X509Certificate::kPublicKeyTypeDSA;
+ break;
+ case dhKey:
+ *type = X509Certificate::kPublicKeyTypeDH;
+ break;
+ case ecKey:
+ *type = X509Certificate::kPublicKeyTypeECDSA;
+ break;
+ default:
+ *type = X509Certificate::kPublicKeyTypeUnknown;
+ *size_bits = 0;
+ break;
+ }
+}
+#endif // defined(USE_NSS) || defined(OS_IOS)
+
} // namespace x509_util
} // namespace net
diff --git a/net/base/x509_util_nss.h b/net/base/x509_util_nss.h
index 7685167..f00c4ab 100644
--- a/net/base/x509_util_nss.h
+++ b/net/base/x509_util_nss.h
@@ -6,14 +6,19 @@
#define NET_BASE_X509_UTIL_NSS_H_
#include <string>
+#include <vector>
#include "base/time.h"
+#include "net/base/x509_certificate.h"
+
+class PickleIterator;
typedef struct CERTCertificateStr CERTCertificate;
+typedef struct CERTNameStr CERTName;
typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey;
+typedef struct SECItemStr SECItem;
typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
-
namespace net {
namespace x509_util {
@@ -30,6 +35,43 @@ CERTCertificate* CreateSelfSignedCert(
base::Time not_valid_before,
base::Time not_valid_after);
+#if defined(USE_NSS) || defined(OS_IOS)
+// Parses the Principal attribute from |name| and outputs the result in
+// |principal|.
+void ParsePrincipal(CERTName* name,
+ CertPrincipal* principal);
+
+// Parses the date from |der_date| and outputs the result in |result|.
+void ParseDate(const SECItem* der_date, base::Time* result);
+
+// Parses the serial number from |certificate|.
+std::string ParseSerialNumber(const CERTCertificate* certificate);
+
+// Gets the subjectAltName extension field from the certificate, if any.
+void GetSubjectAltName(CERTCertificate* cert_handle,
+ std::vector<std::string>* dns_names,
+ std::vector<std::string>* ip_addrs);
+
+// Creates all possible OS certificate handles from |data| encoded in a specific
+// |format|. Returns an empty collection on failure.
+X509Certificate::OSCertHandles CreateOSCertHandlesFromBytes(
+ const char* data,
+ int length,
+ X509Certificate::Format format);
+
+// Reads a single certificate from |pickle_iter| and returns a platform-specific
+// certificate handle. Returns an invalid handle, NULL, on failure.
+X509Certificate::OSCertHandle ReadOSCertHandleFromPickle(
+ PickleIterator* pickle_iter);
+
+// Sets |*size_bits| to be the length of the public key in bits, and sets
+// |*type| to one of the |PublicKeyType| values. In case of
+// |kPublicKeyTypeUnknown|, |*size_bits| will be set to 0.
+void GetPublicKeyInfo(CERTCertificate* handle,
+ size_t* size_bits,
+ X509Certificate::PublicKeyType* type);
+#endif // defined(USE_NSS) || defined(OS_IOS)
+
} // namespace x509_util
} // namespace net
diff --git a/net/net.gyp b/net/net.gyp
index ee97bba..2857c1a 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -299,6 +299,7 @@
'base/x509_cert_types_win.cc',
'base/x509_certificate.cc',
'base/x509_certificate.h',
+ 'base/x509_certificate_ios.cc',
'base/x509_certificate_mac.cc',
'base/x509_certificate_net_log_param.cc',
'base/x509_certificate_net_log_param.h',
@@ -306,6 +307,8 @@
'base/x509_certificate_openssl.cc',
'base/x509_certificate_win.cc',
'base/x509_util.h',
+ 'base/x509_util_ios.cc',
+ 'base/x509_util_ios.h',
'base/x509_util_mac.cc',
'base/x509_util_mac.h',
'base/x509_util_nss.cc',
@@ -1078,10 +1081,15 @@
},
],
[ 'OS == "ios"', {
+ 'dependencies': [
+ '../third_party/nss/nss.gyp:nss',
+ 'third_party/nss/ssl.gyp:libssl',
+ ],
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/CFNetwork.framework',
'$(SDKROOT)/System/Library/Frameworks/MobileCoreServices.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Security.framework',
'$(SDKROOT)/System/Library/Frameworks/SystemConfiguration.framework',
'$(SDKROOT)/usr/lib/libresolv.dylib',
],
@@ -1091,8 +1099,10 @@
# compiled on iOS, just enough to bring up the dependencies needed
# by the ui target.
['exclude', '.*'],
+ ['include', '^base/asn1_util\\.'],
['include', '^base/dns_util\\.'],
['include', '^base/escape\\.'],
+ ['include', '^base/ev_root_ca_metadata\\.'],
['include', '^base/ip_endpoint\\.'],
['include', '^base/mime_util\\.'],
['include', '^base/net_errors\\.'],
@@ -1104,7 +1114,13 @@
['include', '^base/net_util\\.'],
['include', '^base/net_util_posix\\.cc$'],
['include', '^base/platform_mime_util\\.h$'],
+ ['include', '^base/pem_tokenizer\\.cc$'],
+ ['include', '^base/pem_tokenizer\\.h$'],
['include', '^base/registry_controlled_domains/registry_controlled_domain\\.'],
+ ['include', '^base/x509_certificate\\.'],
+ ['include', '^base/x509_certificate_ios\\.'],
+ ['include', '^base/x509_cert_types\\.'],
+ ['include', '^base/x509_util_ios\\.'],
['include', '^http/http_byte_range\\.'],
['include', '^http/http_content_disposition\\.'],
['include', '^http/http_util\\.'],
@@ -1167,6 +1183,11 @@
['include', 'base/network_config_watcher_mac\\.cc$'],
['include', 'base/platform_mime_util_mac\\.mm$'],
['include', 'proxy/proxy_resolver_mac\\.cc$'],
+ # The iOS implementation only partially uses NSS and thus does not
+ # defines |use_nss|. In particular the |USE_NSS| preprocessor
+ # definition is not used. The following files are needed though:
+ ['include', 'base/x509_util_nss\\.cc$'],
+ ['include', 'base/x509_util_nss\\.h$'],
],
}],
],
@@ -1536,25 +1557,28 @@
],
},
],
- ['OS == "ios"', {
- # TODO: For now this only tests the subset of code that is enabled in
- # the net target.
- 'dependencies': [
- '../testing/gtest.gyp:gtest_main',
- ],
- 'sources/': [
- ['exclude', '.*'],
- ['include', '^base/dns_util_unittest\\.cc$'],
- ['include', '^base/escape_unittest\\.cc$'],
- ['include', '^base/ip_endpoint_unittest\\.cc$'],
- ['include', '^base/mime_util_unittest\\.cc$'],
- ['include', '^base/net_log_unittest\\.cc$'],
- ['include', '^base/registry_controlled_domains/registry_controlled_domain_unittest\\.cc$'],
- ['include', '^http/http_byte_range_unittest\\.cc$'],
- ['include', '^http/http_content_disposition_unittest\\.cc$'],
- ['include', '^http/http_util_unittest\\.cc$'],
- ['include', '^proxy/proxy_config_service_common_unittest\\.cc$'],
- ],
+ [ 'OS == "ios"', {
+ # TODO: For now this only tests the subset of code that is enabled
+ # in the net target.
+ 'dependencies': [
+ '../third_party/nss/nss.gyp:nss',
+ '../testing/gtest.gyp:gtest_main',
+ ],
+ 'sources/': [
+ ['exclude', '.*'],
+ ['include', '^base/dns_util_unittest\\.cc$'],
+ ['include', '^base/escape_unittest\\.cc$'],
+ ['include', '^base/ip_endpoint_unittest\\.cc$'],
+ ['include', '^base/mime_util_unittest\\.cc$'],
+ ['include', '^base/net_log_unittest\\.cc$'],
+ ['include', '^base/pem_tokenizer_unittest\\.cc$'],
+ ['include', '^base/registry_controlled_domains/registry_controlled_domain_unittest\\.cc$'],
+ ['include', '^base/x509_certificate_unittest\\.cc$'],
+ ['include', '^http/http_byte_range_unittest\\.cc$'],
+ ['include', '^http/http_content_disposition_unittest\\.cc$'],
+ ['include', '^http/http_util_unittest\\.cc$'],
+ ['include', '^proxy/proxy_config_service_common_unittest\\.cc$'],
+ ],
}],
[ 'OS == "linux"', {
'dependencies': [
@@ -1740,6 +1764,11 @@
'test/spawner_communicator.h',
],
}],
+ ['OS == "ios"', {
+ 'dependencies': [
+ '../third_party/nss/nss.gyp:nss',
+ ],
+ }],
[ 'use_v8_in_net==1', {
'dependencies': [
'net_with_v8',