summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-18 01:47:04 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-18 01:47:04 +0000
commit3c5f6db2ef15a4ac179351764f320e16364fa457 (patch)
treec69a4d6684d81d73a622343311c2e962501819e4 /net/base
parent091a5b6f0dede5cedee54ec1aae623a946ab405f (diff)
downloadchromium_src-3c5f6db2ef15a4ac179351764f320e16364fa457.zip
chromium_src-3c5f6db2ef15a4ac179351764f320e16364fa457.tar.gz
chromium_src-3c5f6db2ef15a4ac179351764f320e16364fa457.tar.bz2
Use NSS to generate Origin-Bound Certs on Win and Mac.
The platform RSAPrivateKey is used to generate the private key, which is then imported into NSS to generate the certificate. X509Certificate::CreateOriginBound is moved to x509_util::CreateOriginBoundCert so it can be shared by those platforms, and removes the unnecessary X509Certificate generation step. BUG=88782 TEST=X509UtilNSSTest.CreateOriginBoundCert & manual testing: try on win or mac, check if generated cert has the OBC extension. Review URL: http://codereview.chromium.org/8296014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@105997 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r--net/base/net_error_list.h3
-rw-r--r--net/base/origin_bound_cert_service.cc22
-rw-r--r--net/base/x509_certificate.h12
-rw-r--r--net/base/x509_certificate_mac.cc19
-rw-r--r--net/base/x509_certificate_nss.cc239
-rw-r--r--net/base/x509_certificate_openssl.cc11
-rw-r--r--net/base/x509_certificate_unittest.cc79
-rw-r--r--net/base/x509_certificate_win.cc10
-rw-r--r--net/base/x509_util.h39
-rw-r--r--net/base/x509_util_nss.cc318
-rw-r--r--net/base/x509_util_nss.h37
-rw-r--r--net/base/x509_util_nss_unittest.cc106
-rw-r--r--net/base/x509_util_openssl.cc11
-rw-r--r--net/base/x509_util_openssl_unittest.cc29
14 files changed, 553 insertions, 382 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index cc012e0..3097d31 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -613,9 +613,6 @@ NET_ERROR(ORIGIN_BOUND_CERT_GENERATION_FAILED, -711)
// Failure to export private key.
NET_ERROR(PRIVATE_KEY_EXPORT_FAILED, -712)
-// Failure to get certificate bytes.
-NET_ERROR(GET_CERT_BYTES_FAILED, -713)
-
// DNS error codes.
// DNS resolver received a malformed response.
diff --git a/net/base/origin_bound_cert_service.cc b/net/base/origin_bound_cert_service.cc
index 4b48e61..e5c1e9d 100644
--- a/net/base/origin_bound_cert_service.cc
+++ b/net/base/origin_bound_cert_service.cc
@@ -19,6 +19,7 @@
#include "net/base/net_errors.h"
#include "net/base/origin_bound_cert_store.h"
#include "net/base/x509_certificate.h"
+#include "net/base/x509_util.h"
#if defined(USE_NSS)
#include <private/pprthred.h> // PR_DetachThread
@@ -324,20 +325,13 @@ int OriginBoundCertService::GenerateCert(const std::string& origin,
LOG(WARNING) << "Unable to create key pair for client";
return ERR_KEY_GENERATION_FAILED;
}
-#if defined(USE_NSS)
- scoped_refptr<X509Certificate> x509_cert = X509Certificate::CreateOriginBound(
+ std::string der_cert;
+ if (!x509_util::CreateOriginBoundCert(
key.get(),
origin,
serial_number,
- base::TimeDelta::FromDays(kValidityPeriodInDays));
-#else
- scoped_refptr<X509Certificate> x509_cert = X509Certificate::CreateSelfSigned(
- key.get(),
- "CN=anonymous.invalid",
- serial_number,
- base::TimeDelta::FromDays(kValidityPeriodInDays));
-#endif
- if (!x509_cert) {
+ base::TimeDelta::FromDays(kValidityPeriodInDays),
+ &der_cert)) {
LOG(WARNING) << "Unable to create x509 cert for client";
return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED;
}
@@ -351,12 +345,6 @@ int OriginBoundCertService::GenerateCert(const std::string& origin,
// std::string* to prevent this copying.
std::string key_out(private_key_info.begin(), private_key_info.end());
- std::string der_cert;
- if (!x509_cert->GetDEREncoded(&der_cert)) {
- LOG(WARNING) << "Unable to get DER-encoded cert";
- return ERR_GET_CERT_BYTES_FAILED;
- }
-
private_key->swap(key_out);
cert->swap(der_cert);
return OK;
diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h
index 1a8b233..94a6f4a 100644
--- a/net/base/x509_certificate.h
+++ b/net/base/x509_certificate.h
@@ -187,18 +187,6 @@ class NET_EXPORT X509Certificate
uint32 serial_number,
base::TimeDelta valid_duration);
- // Create an origin bound certificate containing the public key in |key|.
- // Web origin, serial number and validity period are given as
- // parameters. The certificate is signed by the private key in |key|.
- // The hashing algorithm for the signature is SHA-1.
- //
- // See Internet Draft draft-balfanz-tls-obc-00 for more details:
- // http://balfanz.github.com/tls-obc-spec/draft-balfanz-tls-obc-00.html
- static X509Certificate* CreateOriginBound(crypto::RSAPrivateKey* key,
- const std::string& origin,
- uint32 serial_number,
- base::TimeDelta valid_duration);
-
// Appends a representation of this object to the given pickle.
void Persist(Pickle* pickle);
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
index 2c95981..c47d1a5 100644
--- a/net/base/x509_certificate_mac.cc
+++ b/net/base/x509_certificate_mac.cc
@@ -695,25 +695,6 @@ X509Certificate* X509Certificate::CreateSelfSigned(
return CreateFromHandle(scoped_cert, X509Certificate::OSCertHandles());
}
-// static
-X509Certificate* X509Certificate::CreateOriginBound(
- crypto::RSAPrivateKey* key,
- const std::string& origin,
- uint32 serial_number,
- base::TimeDelta valid_duration) {
- // TODO(wtc): this cannot be implemented by creating a CE_DataAndType for
- // the origin-bound extension and adding it to certReq.extensions because
- // it is not one of the supported extensions in the CE_DataType enum type.
- // Using the DT_Other enum constant does not work.
- //
- // The relevant Apple headers are:
- // - CSSM_APPLE_TP_CERT_REQUEST is defined in cssmapple.h.
- // - CE_DataAndType, CE_DataType, and CE_Data are defined in
- // certextensions.h.
- NOTIMPLEMENTED();
- return NULL;
-}
-
void X509Certificate::GetSubjectAltName(
std::vector<std::string>* dns_names,
std::vector<std::string>* ip_addrs) const {
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
index c226132..039edad 100644
--- a/net/base/x509_certificate_nss.cc
+++ b/net/base/x509_certificate_nss.cc
@@ -6,7 +6,6 @@
#include <cert.h>
#include <cryptohi.h>
-#include <keyhi.h>
#include <nss.h>
#include <pk11pub.h>
#include <prerror.h>
@@ -18,7 +17,6 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "base/memory/singleton.h"
#include "base/pickle.h"
#include "base/time.h"
#include "crypto/nss_util.h"
@@ -27,55 +25,12 @@
#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_nss.h"
namespace net {
namespace {
-class ObCertOIDWrapper {
- public:
- static ObCertOIDWrapper* GetInstance() {
- // Instantiated as a leaky singleton to allow the singleton to be
- // constructed on a worker thead that is not joined when a process
- // shuts down.
- return Singleton<ObCertOIDWrapper,
- LeakySingletonTraits<ObCertOIDWrapper> >::get();
- }
-
- SECOidTag ob_cert_oid_tag() const {
- return ob_cert_oid_tag_;
- }
-
- private:
- friend struct DefaultSingletonTraits<ObCertOIDWrapper>;
-
- ObCertOIDWrapper();
-
- SECOidTag ob_cert_oid_tag_;
-
- DISALLOW_COPY_AND_ASSIGN(ObCertOIDWrapper);
-};
-
-ObCertOIDWrapper::ObCertOIDWrapper(): ob_cert_oid_tag_(SEC_OID_UNKNOWN) {
- // 1.3.6.1.4.1.11129.2.1.6
- // (iso.org.dod.internet.private.enterprises.google.googleSecurity.
- // certificateExtensions.originBoundCertificate)
- static const uint8 kObCertOID[] = {
- 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x06
- };
- SECOidData oid_data;
- memset(&oid_data, 0, sizeof(oid_data));
- oid_data.oid.data = const_cast<uint8*>(kObCertOID);
- oid_data.oid.len = sizeof(kObCertOID);
- oid_data.offset = SEC_OID_UNKNOWN;
- oid_data.desc = "Origin Bound Certificate";
- oid_data.mechanism = CKM_INVALID_MECHANISM;
- oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION;
- ob_cert_oid_tag_ = SECOID_AddEntry(&oid_data);
- if (ob_cert_oid_tag_ == SEC_OID_UNKNOWN)
- LOG(ERROR) << "OB_CERT OID tag creation failed";
-}
-
class ScopedCERTCertificatePolicies {
public:
explicit ScopedCERTCertificatePolicies(CERTCertificatePolicies* policies)
@@ -668,125 +623,6 @@ void X509Certificate::Initialize() {
serial_number_ = serial_number_.substr(1, serial_number_.size() - 1);
}
-// Creates a Certificate object that may be passed to the SignCertificate
-// method to generate an X509 certificate.
-// Returns NULL if an error is encountered in the certificate creation
-// process.
-// Caller responsible for freeing returned certificate object.
-static CERTCertificate* CreateCertificate(
- crypto::RSAPrivateKey* key,
- const std::string& subject,
- uint32 serial_number,
- base::TimeDelta valid_duration) {
- // Create info about public key.
- CERTSubjectPublicKeyInfo* spki =
- SECKEY_CreateSubjectPublicKeyInfo(key->public_key());
- if (!spki)
- return NULL;
-
- // Create the certificate request.
- CERTName* subject_name =
- CERT_AsciiToName(const_cast<char*>(subject.c_str()));
- CERTCertificateRequest* cert_request =
- CERT_CreateCertificateRequest(subject_name, spki, NULL);
- SECKEY_DestroySubjectPublicKeyInfo(spki);
-
- if (!cert_request) {
- PRErrorCode prerr = PR_GetError();
- LOG(ERROR) << "Failed to create certificate request: " << prerr;
- CERT_DestroyName(subject_name);
- return NULL;
- }
-
- PRTime now = PR_Now();
- PRTime not_after = now + valid_duration.InMicroseconds();
-
- // Note that the time is now in micro-second unit.
- CERTValidity* validity = CERT_CreateValidity(now, not_after);
- CERTCertificate* cert = CERT_CreateCertificate(serial_number, subject_name,
- validity, cert_request);
- if (!cert) {
- PRErrorCode prerr = PR_GetError();
- LOG(ERROR) << "Failed to create certificate: " << prerr;
- }
-
- // Cleanup for resources used to generate the cert.
- CERT_DestroyName(subject_name);
- CERT_DestroyValidity(validity);
- CERT_DestroyCertificateRequest(cert_request);
-
- return cert;
-}
-
-// Signs a certificate object, with |key| generating a new X509Certificate
-// and destroying the passed certificate object (even when NULL is returned).
-// The logic of this method references SignCert() in NSS utility certutil:
-// http://mxr.mozilla.org/security/ident?i=SignCert.
-// Returns NULL if an error is encountered in the certificate signing
-// process.
-// Caller responsible for freeing returned X509Certificate object.
-//
-// TODO: change this function to return
-// a success/failure status, and not create an X509Certificate
-// object, and not destroy |cert| on failure. Let the caller
-// create the X509Certificate object and destroy |cert|.
-static X509Certificate* SignCertificate(
- CERTCertificate* cert,
- crypto::RSAPrivateKey* key) {
- // |arena| is used to encode the cert.
- PRArenaPool* arena = cert->arena;
- SECOidTag algo_id = SEC_GetSignatureAlgorithmOidTag(key->key()->keyType,
- SEC_OID_SHA1);
- if (algo_id == SEC_OID_UNKNOWN) {
- CERT_DestroyCertificate(cert);
- return NULL;
- }
-
- SECStatus rv = SECOID_SetAlgorithmID(arena, &cert->signature, algo_id, 0);
- if (rv != SECSuccess) {
- CERT_DestroyCertificate(cert);
- return NULL;
- }
-
- // Generate a cert of version 3.
- *(cert->version.data) = 2;
- cert->version.len = 1;
-
- SECItem der;
- der.len = 0;
- der.data = NULL;
-
- // Use ASN1 DER to encode the cert.
- void* encode_result = SEC_ASN1EncodeItem(
- arena, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate));
- if (!encode_result) {
- CERT_DestroyCertificate(cert);
- return NULL;
- }
-
- // Allocate space to contain the signed cert.
- SECItem* result = SECITEM_AllocItem(arena, NULL, 0);
- if (!result) {
- CERT_DestroyCertificate(cert);
- return NULL;
- }
-
- // Sign the ASN1 encoded cert and save it to |result|.
- rv = SEC_DerSignData(arena, result, der.data, der.len, key->key(), algo_id);
- if (rv != SECSuccess) {
- CERT_DestroyCertificate(cert);
- return NULL;
- }
-
- // Save the signed result to the cert.
- cert->derCert = *result;
-
- X509Certificate* x509_cert =
- X509Certificate::CreateFromHandle(cert, X509Certificate::OSCertHandles());
- CERT_DestroyCertificate(cert);
- return x509_cert;
-}
-
// static
X509Certificate* X509Certificate::CreateSelfSigned(
crypto::RSAPrivateKey* key,
@@ -795,76 +631,17 @@ X509Certificate* X509Certificate::CreateSelfSigned(
base::TimeDelta valid_duration) {
DCHECK(key);
- CERTCertificate* cert = CreateCertificate(key,
- subject,
- serial_number,
- valid_duration);
+ CERTCertificate* cert = x509_util::CreateSelfSignedCert(key->public_key(),
+ key->key(),
+ subject,
+ serial_number,
+ valid_duration);
if (!cert)
return NULL;
- X509Certificate* x509_cert = SignCertificate(cert, key);
-
- return x509_cert;
-}
-
-// static
-X509Certificate* X509Certificate::CreateOriginBound(
- crypto::RSAPrivateKey* key,
- const std::string& origin,
- uint32 serial_number,
- base::TimeDelta valid_duration) {
- DCHECK(key);
-
- CERTCertificate* cert = CreateCertificate(key,
- "CN=anonymous.invalid",
- serial_number,
- valid_duration);
-
- if (!cert)
- return NULL;
-
- // Create opaque handle used to add extensions later.
- void* cert_handle;
- if ((cert_handle = CERT_StartCertExtensions(cert)) == NULL) {
- LOG(ERROR) << "Unable to get opaque handle for adding extensions";
- return NULL;
- }
-
- // Create SECItem for IA5String encoding.
- SECItem origin_string_item = {
- siAsciiString,
- (unsigned char*)origin.data(),
- origin.size()
- };
-
- // IA5Encode and arena allocate SECItem
- SECItem* asn1_origin_string = SEC_ASN1EncodeItem(
- cert->arena, NULL, &origin_string_item,
- SEC_ASN1_GET(SEC_IA5StringTemplate));
- if (asn1_origin_string == NULL) {
- LOG(ERROR) << "Unable to get ASN1 encoding for origin in ob_cert extension";
- return NULL;
- }
-
- // Add the extension to the opaque handle
- if (CERT_AddExtension(cert_handle,
- ObCertOIDWrapper::GetInstance()->ob_cert_oid_tag(),
- asn1_origin_string,
- PR_TRUE, PR_TRUE) != SECSuccess){
- LOG(ERROR) << "Unable to add origin bound cert extension to opaque handle";
- return NULL;
- }
-
- // Copy extension into x509 cert
- if (CERT_FinishExtensions(cert_handle) != SECSuccess){
- LOG(ERROR) << "Unable to copy extension to X509 cert";
- return NULL;
- }
-
- X509Certificate* x509_cert = SignCertificate(cert, key);
-
- return x509_cert;
+ return X509Certificate::CreateFromHandle(cert,
+ X509Certificate::OSCertHandles());
}
void X509Certificate::GetSubjectAltName(
diff --git a/net/base/x509_certificate_openssl.cc b/net/base/x509_certificate_openssl.cc
index 0092577..80e2517 100644
--- a/net/base/x509_certificate_openssl.cc
+++ b/net/base/x509_certificate_openssl.cc
@@ -409,17 +409,6 @@ X509Certificate* X509Certificate::CreateSelfSigned(
return NULL;
}
-// static
-X509Certificate* X509Certificate::CreateOriginBound(
- crypto::RSAPrivateKey* key,
- const std::string& origin,
- uint32 serial_number,
- base::TimeDelta valid_duration) {
- // TODO(port): Implement.
- NOTIMPLEMENTED();
- return NULL;
-}
-
void X509Certificate::GetSubjectAltName(
std::vector<std::string>* dns_names,
std::vector<std::string>* ip_addrs) const {
diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc
index f0d3339..9ba1124 100644
--- a/net/base/x509_certificate_unittest.cc
+++ b/net/base/x509_certificate_unittest.cc
@@ -22,7 +22,6 @@
#if defined(USE_NSS)
#include <cert.h>
-#include <secoid.h>
#endif
// Unit tests aren't allowed to access external resources. Unfortunately, to
@@ -1178,84 +1177,6 @@ TEST(X509CertificateTest, GetDEREncoded) {
}
#endif
-#if defined(USE_NSS)
-// This test creates an origin-bound cert from a private key and
-// then verifies the content of the certificate.
-TEST(X509CertificateTest, CreateOriginBound) {
- // Origin Bound Cert OID.
- static const char oid_string[] = "1.3.6.1.4.1.11129.2.1.6";
-
- // Create a sample ASCII weborigin.
- std::string origin = "http://weborigin.com:443";
-
- // Create object neccissary for extension lookup call.
- SECItem extension_object = {
- siAsciiString,
- (unsigned char*)origin.data(),
- origin.size()
- };
-
- scoped_ptr<crypto::RSAPrivateKey> private_key(
- crypto::RSAPrivateKey::Create(1024));
- scoped_refptr<X509Certificate> cert =
- X509Certificate::CreateOriginBound(private_key.get(),
- origin, 1,
- base::TimeDelta::FromDays(1));
-
- EXPECT_EQ("anonymous.invalid", cert->subject().GetDisplayName());
- EXPECT_FALSE(cert->HasExpired());
-
- // IA5Encode and arena allocate SECItem.
- PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- SECItem* expected = SEC_ASN1EncodeItem(arena,
- NULL,
- &extension_object,
- SEC_ASN1_GET(SEC_IA5StringTemplate));
-
- ASSERT_NE(static_cast<SECItem*>(NULL), expected);
-
- // Create OID SECItem.
- SECItem ob_cert_oid = { siDEROID, NULL, 0 };
- SECStatus ok = SEC_StringToOID(arena, &ob_cert_oid,
- oid_string, 0);
-
- ASSERT_EQ(SECSuccess, ok);
-
- SECOidTag ob_cert_oid_tag = SECOID_FindOIDTag(&ob_cert_oid);
-
- ASSERT_NE(SEC_OID_UNKNOWN, ob_cert_oid_tag);
-
- // Lookup Origin Bound Cert extension in generated cert.
- SECItem actual = { siBuffer, NULL, 0 };
- ok = CERT_FindCertExtension(cert->os_cert_handle(),
- ob_cert_oid_tag,
- &actual);
- ASSERT_EQ(SECSuccess, ok);
-
- // Compare expected and actual extension values.
- PRBool result = SECITEM_ItemsAreEqual(expected, &actual);
- ASSERT_TRUE(result);
-
- // Do Cleanup.
- SECITEM_FreeItem(&actual, PR_FALSE);
- PORT_FreeArena(arena, PR_FALSE);
-}
-#else // defined(USE_NSS)
-// On other platforms, X509Certificate::CreateOriginBound() is not implemented
-// and should return NULL. This unit test ensures that a stub implementation
-// is present.
-TEST(X509CertificateTest, CreateOriginBoundNotImplemented) {
- std::string origin = "http://weborigin.com:443";
- scoped_ptr<crypto::RSAPrivateKey> private_key(
- crypto::RSAPrivateKey::Create(1024));
- scoped_refptr<X509Certificate> cert =
- X509Certificate::CreateOriginBound(private_key.get(),
- origin, 2,
- base::TimeDelta::FromDays(1));
- EXPECT_FALSE(cert);
-}
-#endif // defined(USE_NSS)
-
class X509CertificateParseTest
: public testing::TestWithParam<CertificateFormatTestData> {
public:
diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc
index 1336f8c..5c53a15 100644
--- a/net/base/x509_certificate_win.cc
+++ b/net/base/x509_certificate_win.cc
@@ -630,16 +630,6 @@ X509Certificate* X509Certificate::CreateSelfSigned(
return cert;
}
-// static
-X509Certificate* X509Certificate::CreateOriginBound(
- crypto::RSAPrivateKey* key,
- const std::string& origin,
- uint32 serial_number,
- base::TimeDelta valid_duration) {
- NOTIMPLEMENTED();
- return NULL;
-}
-
void X509Certificate::GetSubjectAltName(
std::vector<std::string>* dns_names,
std::vector<std::string>* ip_addrs) const {
diff --git a/net/base/x509_util.h b/net/base/x509_util.h
new file mode 100644
index 0000000..d110c939
--- /dev/null
+++ b/net/base/x509_util.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 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_UTIL_H_
+#define NET_BASE_X509_UTIL_H_
+#pragma once
+
+#include <string>
+
+#include "base/time.h"
+#include "net/base/net_export.h"
+
+namespace crypto {
+class RSAPrivateKey;
+}
+
+namespace net {
+
+namespace x509_util {
+
+// Creates an origin bound certificate containing the public key in |key|.
+// Web origin, serial number and validity period are given as
+// parameters. The certificate is signed by the private key in |key|.
+// The hashing algorithm for the signature is SHA-1.
+//
+// See Internet Draft draft-balfanz-tls-obc-00 for more details:
+// http://tools.ietf.org/html/draft-balfanz-tls-obc-00
+bool NET_EXPORT_PRIVATE CreateOriginBoundCert(crypto::RSAPrivateKey* key,
+ const std::string& origin,
+ uint32 serial_number,
+ base::TimeDelta valid_duration,
+ std::string* der_cert);
+
+} // namespace x509_util
+
+} // namespace net
+
+#endif // NET_BASE_X509_UTIL_H_
diff --git a/net/base/x509_util_nss.cc b/net/base/x509_util_nss.cc
new file mode 100644
index 0000000..fe3fb17
--- /dev/null
+++ b/net/base/x509_util_nss.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2011 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.h"
+#include "net/base/x509_util_nss.h"
+
+#include <cert.h>
+#include <cryptohi.h>
+#include <pk11pub.h>
+#include <prerror.h>
+#include <secmod.h>
+#include <secport.h>
+
+#include "base/debug/leak_annotations.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "crypto/rsa_private_key.h"
+#include "crypto/scoped_nss_types.h"
+
+namespace {
+
+class ObCertOIDWrapper {
+ public:
+ static ObCertOIDWrapper* GetInstance() {
+ // Instantiated as a leaky singleton to allow the singleton to be
+ // constructed on a worker thead that is not joined when a process
+ // shuts down.
+ return Singleton<ObCertOIDWrapper,
+ LeakySingletonTraits<ObCertOIDWrapper> >::get();
+ }
+
+ SECOidTag ob_cert_oid_tag() const {
+ return ob_cert_oid_tag_;
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<ObCertOIDWrapper>;
+
+ ObCertOIDWrapper();
+
+ SECOidTag ob_cert_oid_tag_;
+
+ DISALLOW_COPY_AND_ASSIGN(ObCertOIDWrapper);
+};
+
+ObCertOIDWrapper::ObCertOIDWrapper(): ob_cert_oid_tag_(SEC_OID_UNKNOWN) {
+ // 1.3.6.1.4.1.11129.2.1.6
+ // (iso.org.dod.internet.private.enterprises.google.googleSecurity.
+ // certificateExtensions.originBoundCertificate)
+ static const uint8 kObCertOID[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x06
+ };
+ SECOidData oid_data;
+ memset(&oid_data, 0, sizeof(oid_data));
+ oid_data.oid.data = const_cast<uint8*>(kObCertOID);
+ oid_data.oid.len = sizeof(kObCertOID);
+ oid_data.offset = SEC_OID_UNKNOWN;
+ oid_data.desc = "Origin Bound Certificate";
+ oid_data.mechanism = CKM_INVALID_MECHANISM;
+ oid_data.supportedExtension = SUPPORTED_CERT_EXTENSION;
+ ob_cert_oid_tag_ = SECOID_AddEntry(&oid_data);
+ if (ob_cert_oid_tag_ == SEC_OID_UNKNOWN)
+ LOG(ERROR) << "OB_CERT OID tag creation failed";
+}
+
+// Creates a Certificate object that may be passed to the SignCertificate
+// method to generate an X509 certificate.
+// Returns NULL if an error is encountered in the certificate creation
+// process.
+// Caller responsible for freeing returned certificate object.
+CERTCertificate* CreateCertificate(
+ SECKEYPublicKey* public_key,
+ const std::string& subject,
+ uint32 serial_number,
+ base::TimeDelta valid_duration) {
+ // Create info about public key.
+ CERTSubjectPublicKeyInfo* spki =
+ SECKEY_CreateSubjectPublicKeyInfo(public_key);
+ if (!spki)
+ return NULL;
+
+ // Create the certificate request.
+ CERTName* subject_name =
+ CERT_AsciiToName(const_cast<char*>(subject.c_str()));
+ CERTCertificateRequest* cert_request =
+ CERT_CreateCertificateRequest(subject_name, spki, NULL);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+
+ if (!cert_request) {
+ PRErrorCode prerr = PR_GetError();
+ LOG(ERROR) << "Failed to create certificate request: " << prerr;
+ CERT_DestroyName(subject_name);
+ return NULL;
+ }
+
+ PRTime now = PR_Now();
+ PRTime not_after = now + valid_duration.InMicroseconds();
+
+ // Note that the time is now in micro-second unit.
+ CERTValidity* validity = CERT_CreateValidity(now, not_after);
+ CERTCertificate* cert = CERT_CreateCertificate(serial_number, subject_name,
+ validity, cert_request);
+ if (!cert) {
+ PRErrorCode prerr = PR_GetError();
+ LOG(ERROR) << "Failed to create certificate: " << prerr;
+ }
+
+ // Cleanup for resources used to generate the cert.
+ CERT_DestroyName(subject_name);
+ CERT_DestroyValidity(validity);
+ CERT_DestroyCertificateRequest(cert_request);
+
+ return cert;
+}
+
+// Signs a certificate object, with |key| generating a new X509Certificate
+// and destroying the passed certificate object (even when NULL is returned).
+// The logic of this method references SignCert() in NSS utility certutil:
+// http://mxr.mozilla.org/security/ident?i=SignCert.
+// Returns true on success or false if an error is encountered in the
+// certificate signing process.
+bool SignCertificate(
+ CERTCertificate* cert,
+ SECKEYPrivateKey* key) {
+ // |arena| is used to encode the cert.
+ PLArenaPool* arena = cert->arena;
+ SECOidTag algo_id = SEC_GetSignatureAlgorithmOidTag(key->keyType,
+ SEC_OID_SHA1);
+ if (algo_id == SEC_OID_UNKNOWN)
+ return false;
+
+ SECStatus rv = SECOID_SetAlgorithmID(arena, &cert->signature, algo_id, 0);
+ if (rv != SECSuccess)
+ return false;
+
+ // Generate a cert of version 3.
+ *(cert->version.data) = 2;
+ cert->version.len = 1;
+
+ SECItem der;
+ der.len = 0;
+ der.data = NULL;
+
+ // Use ASN1 DER to encode the cert.
+ void* encode_result = SEC_ASN1EncodeItem(
+ arena, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate));
+ if (!encode_result)
+ return false;
+
+ // Allocate space to contain the signed cert.
+ SECItem* result = SECITEM_AllocItem(arena, NULL, 0);
+ if (!result)
+ return false;
+
+ // Sign the ASN1 encoded cert and save it to |result|.
+ rv = SEC_DerSignData(arena, result, der.data, der.len, key, algo_id);
+ if (rv != SECSuccess)
+ return false;
+
+ // Save the signed result to the cert.
+ cert->derCert = *result;
+
+ return true;
+}
+
+} // namespace
+
+namespace net {
+
+namespace x509_util {
+
+CERTCertificate* CreateSelfSignedCert(
+ SECKEYPublicKey* public_key,
+ SECKEYPrivateKey* private_key,
+ const std::string& subject,
+ uint32 serial_number,
+ base::TimeDelta valid_duration) {
+ CERTCertificate* cert = CreateCertificate(public_key,
+ subject,
+ serial_number,
+ valid_duration);
+ if (!cert)
+ return NULL;
+
+ if (!SignCertificate(cert, private_key)) {
+ CERT_DestroyCertificate(cert);
+ return NULL;
+ }
+
+ return cert;
+}
+
+bool CreateOriginBoundCert(
+ crypto::RSAPrivateKey* key,
+ const std::string& origin,
+ uint32 serial_number,
+ base::TimeDelta valid_duration,
+ std::string* der_cert) {
+ DCHECK(key);
+
+ SECKEYPublicKey* public_key;
+ SECKEYPrivateKey* private_key;
+#if defined(USE_NSS)
+ public_key = key->public_key();
+ private_key = key->key();
+#else
+ crypto::ScopedSECKEYPublicKey scoped_public_key;
+ crypto::ScopedSECKEYPrivateKey scoped_private_key;
+ {
+ // Based on the NSS RSAPrivateKey::CreateFromPrivateKeyInfoWithParams.
+ // This method currently leaks some memory.
+ // See http://crbug.com/34742.
+ ANNOTATE_SCOPED_MEMORY_LEAK;
+ crypto::EnsureNSSInit();
+
+ std::vector<uint8> key_data;
+ key->ExportPrivateKey(&key_data);
+
+ crypto::ScopedPK11Slot slot(crypto::GetPrivateNSSKeySlot());
+ if (!slot.get())
+ return NULL;
+
+ SECItem der_private_key_info;
+ der_private_key_info.data = const_cast<unsigned char*>(&key_data[0]);
+ der_private_key_info.len = key_data.size();
+ // Allow the private key to be used for key unwrapping, data decryption,
+ // and signature generation.
+ const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT |
+ KU_DIGITAL_SIGNATURE;
+ SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ slot.get(), &der_private_key_info, NULL, NULL, PR_FALSE, PR_FALSE,
+ key_usage, &private_key, NULL);
+ scoped_private_key.reset(private_key);
+ if (rv != SECSuccess) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ public_key = SECKEY_ConvertToPublicKey(private_key);
+ if (!public_key) {
+ NOTREACHED();
+ return NULL;
+ }
+ scoped_public_key.reset(public_key);
+ }
+#endif
+
+ CERTCertificate* cert = CreateCertificate(public_key,
+ "CN=anonymous.invalid",
+ serial_number,
+ valid_duration);
+
+ if (!cert)
+ return false;
+
+ // Create opaque handle used to add extensions later.
+ void* cert_handle;
+ if ((cert_handle = CERT_StartCertExtensions(cert)) == NULL) {
+ LOG(ERROR) << "Unable to get opaque handle for adding extensions";
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ // Create SECItem for IA5String encoding.
+ SECItem origin_string_item = {
+ siAsciiString,
+ (unsigned char*)origin.data(),
+ origin.size()
+ };
+
+ // IA5Encode and arena allocate SECItem
+ SECItem* asn1_origin_string = SEC_ASN1EncodeItem(
+ cert->arena, NULL, &origin_string_item,
+ SEC_ASN1_GET(SEC_IA5StringTemplate));
+ if (asn1_origin_string == NULL) {
+ LOG(ERROR) << "Unable to get ASN1 encoding for origin in ob_cert extension";
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ // Add the extension to the opaque handle
+ if (CERT_AddExtension(cert_handle,
+ ObCertOIDWrapper::GetInstance()->ob_cert_oid_tag(),
+ asn1_origin_string,
+ PR_TRUE, PR_TRUE) != SECSuccess){
+ LOG(ERROR) << "Unable to add origin bound cert extension to opaque handle";
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ // Copy extension into x509 cert
+ if (CERT_FinishExtensions(cert_handle) != SECSuccess){
+ LOG(ERROR) << "Unable to copy extension to X509 cert";
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ if (!SignCertificate(cert, private_key)) {
+ CERT_DestroyCertificate(cert);
+ return false;
+ }
+
+ DCHECK(cert->derCert.len);
+ // XXX copied from X509Certificate::GetDEREncoded
+ der_cert->clear();
+ der_cert->append(reinterpret_cast<char*>(cert->derCert.data),
+ cert->derCert.len);
+ CERT_DestroyCertificate(cert);
+ return true;
+}
+
+} // namespace x509_util
+
+} // namespace net
diff --git a/net/base/x509_util_nss.h b/net/base/x509_util_nss.h
new file mode 100644
index 0000000..82dd4f9
--- /dev/null
+++ b/net/base/x509_util_nss.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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_UTIL_NSS_H_
+#define NET_BASE_X509_UTIL_NSS_H_
+#pragma once
+
+#include <string>
+
+#include "base/time.h"
+
+typedef struct CERTCertificateStr CERTCertificate;
+typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey;
+typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
+
+
+namespace net {
+
+namespace x509_util {
+
+// Creates a self-signed certificate containing |public_key|. Subject, serial
+// number and validity period are given as parameters. The certificate is
+// signed by |private_key|. The hashing algorithm for the signature is SHA-1.
+// |subject| is a distinguished name defined in RFC4514.
+CERTCertificate* CreateSelfSignedCert(
+ SECKEYPublicKey* public_key,
+ SECKEYPrivateKey* private_key,
+ const std::string& subject,
+ uint32 serial_number,
+ base::TimeDelta valid_duration);
+
+} // namespace x509_util
+
+} // namespace net
+
+#endif // NET_BASE_X509_UTIL_NSS_H_
diff --git a/net/base/x509_util_nss_unittest.cc b/net/base/x509_util_nss_unittest.cc
new file mode 100644
index 0000000..79146b6
--- /dev/null
+++ b/net/base/x509_util_nss_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2011 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.h"
+#include "net/base/x509_util_nss.h"
+
+#include <cert.h>
+#include <secoid.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/x509_certificate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+CERTCertificate* CreateNSSCertHandleFromBytes(const char* data, size_t length) {
+ 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
+
+namespace net {
+
+// This test creates an origin-bound cert from a private key and
+// then verifies the content of the certificate.
+TEST(X509UtilNSSTest, CreateOriginBoundCert) {
+ // Origin Bound Cert OID.
+ static const char oid_string[] = "1.3.6.1.4.1.11129.2.1.6";
+
+ // Create a sample ASCII weborigin.
+ std::string origin = "http://weborigin.com:443";
+
+ // Create object neccessary for extension lookup call.
+ SECItem extension_object = {
+ siAsciiString,
+ (unsigned char*)origin.data(),
+ origin.size()
+ };
+
+ scoped_ptr<crypto::RSAPrivateKey> private_key(
+ crypto::RSAPrivateKey::Create(1024));
+ std::string der_cert;
+ ASSERT_TRUE(x509_util::CreateOriginBoundCert(private_key.get(),
+ origin, 1,
+ base::TimeDelta::FromDays(1),
+ &der_cert));
+
+ scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromBytes(
+ der_cert.data(), der_cert.size());
+
+ EXPECT_EQ("anonymous.invalid", cert->subject().GetDisplayName());
+ EXPECT_FALSE(cert->HasExpired());
+
+ // IA5Encode and arena allocate SECItem.
+ PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ SECItem* expected = SEC_ASN1EncodeItem(arena,
+ NULL,
+ &extension_object,
+ SEC_ASN1_GET(SEC_IA5StringTemplate));
+
+ ASSERT_NE(static_cast<SECItem*>(NULL), expected);
+
+ // Create OID SECItem.
+ SECItem ob_cert_oid = { siDEROID, NULL, 0 };
+ SECStatus ok = SEC_StringToOID(arena, &ob_cert_oid,
+ oid_string, 0);
+
+ ASSERT_EQ(SECSuccess, ok);
+
+ SECOidTag ob_cert_oid_tag = SECOID_FindOIDTag(&ob_cert_oid);
+
+ ASSERT_NE(SEC_OID_UNKNOWN, ob_cert_oid_tag);
+
+ // This test is run on Mac and Win where X509Certificate::os_cert_handle isn't
+ // an NSS type, so we have to manually create a NSS certificate object so we
+ // can use CERT_FindCertExtension.
+ CERTCertificate* nss_cert = CreateNSSCertHandleFromBytes(
+ der_cert.data(), der_cert.size());
+ // Lookup Origin Bound Cert extension in generated cert.
+ SECItem actual = { siBuffer, NULL, 0 };
+ ok = CERT_FindCertExtension(nss_cert,
+ ob_cert_oid_tag,
+ &actual);
+ CERT_DestroyCertificate(nss_cert);
+ ASSERT_EQ(SECSuccess, ok);
+
+ // Compare expected and actual extension values.
+ PRBool result = SECITEM_ItemsAreEqual(expected, &actual);
+ ASSERT_TRUE(result);
+
+ // Do Cleanup.
+ SECITEM_FreeItem(&actual, PR_FALSE);
+ PORT_FreeArena(arena, PR_FALSE);
+}
+
+} // namespace net
diff --git a/net/base/x509_util_openssl.cc b/net/base/x509_util_openssl.cc
index 1631d6f..8e4fb27 100644
--- a/net/base/x509_util_openssl.cc
+++ b/net/base/x509_util_openssl.cc
@@ -2,6 +2,7 @@
// 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.h"
#include "net/base/x509_util_openssl.h"
#include <algorithm>
@@ -14,6 +15,16 @@ namespace net {
namespace x509_util {
+bool CreateOriginBoundCert(
+ crypto::RSAPrivateKey* key,
+ const std::string& origin,
+ uint32 serial_number,
+ base::TimeDelta valid_duration,
+ std::string* der_cert) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
bool ParsePrincipalKeyAndValueByIndex(X509_NAME* name,
int index,
std::string* key,
diff --git a/net/base/x509_util_openssl_unittest.cc b/net/base/x509_util_openssl_unittest.cc
new file mode 100644
index 0000000..0647a62
--- /dev/null
+++ b/net/base/x509_util_openssl_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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 "base/memory/scoped_ptr.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/x509_util.h"
+#include "net/base/x509_util_openssl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+// For OpenSSL, x509_util::CreateOriginBoundCert() is not yet implemented
+// and should return false. This unit test ensures that a stub implementation
+// is present.
+TEST(X509UtilOpenSSLTest, CreateOriginBoundCertNotImplemented) {
+ std::string origin = "http://weborigin.com:443";
+ scoped_ptr<crypto::RSAPrivateKey> private_key(
+ crypto::RSAPrivateKey::Create(1024));
+ std::string der_cert;
+ EXPECT_FALSE(x509_util::CreateOriginBoundCert(private_key.get(),
+ origin, 1,
+ base::TimeDelta::FromDays(1),
+ &der_cert));
+ EXPECT_TRUE(der_cert.empty());
+
+}
+
+} // namespace net