diff options
author | digit@chromium.org <digit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-18 03:49:47 +0000 |
---|---|---|
committer | digit@chromium.org <digit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-18 03:49:47 +0000 |
commit | 5d30729bbea9ae61320fc879642393fb051f2ab5 (patch) | |
tree | 252a24305397d5785d161854848b9d99816ab337 /net | |
parent | 96a0c067d816576735856452608a03bd7e914504 (diff) | |
download | chromium_src-5d30729bbea9ae61320fc879642393fb051f2ab5.zip chromium_src-5d30729bbea9ae61320fc879642393fb051f2ab5.tar.gz chromium_src-5d30729bbea9ae61320fc879642393fb051f2ab5.tar.bz2 |
Implement x509_util::CreateSelfSignedCert() for OpenSSL.
This requires a parser for RFC 2253 strings, so add one in
"net/cert/x509_util_openssl.h", and an appropriate unit test.
BUG=306176
R=rsleevi@chromium.org,phajdan.jr@chromium.org
TESTING
build/android/test_runner.py gtest -s net_unittests
build/android/test_runner.py gtest -s content_unittests
Review URL: https://codereview.chromium.org/27096005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229305 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/cert/x509_util.h | 7 | ||||
-rw-r--r-- | net/cert/x509_util_nss.cc | 1 | ||||
-rw-r--r-- | net/cert/x509_util_openssl.cc | 95 | ||||
-rw-r--r-- | net/cert/x509_util_unittest.cc | 2 |
4 files changed, 97 insertions, 8 deletions
diff --git a/net/cert/x509_util.h b/net/cert/x509_util.h index 8a6bae2..513878d 100644 --- a/net/cert/x509_util.h +++ b/net/cert/x509_util.h @@ -50,10 +50,9 @@ NET_EXPORT_PRIVATE bool CreateDomainBoundCertEC( // The certificate is signed by the private key in |key|. The hashing // algorithm for the signature is SHA-1. // -// |subject| is a distinguished name defined in RFC4514. -// -// An example: -// CN=Michael Wong,O=FooBar Corporation,DC=foobar,DC=com +// |subject| is a distinguished name defined in RFC4514 with _only_ a CN +// component, as in: +// CN=Michael Wong // // SECURITY WARNING // diff --git a/net/cert/x509_util_nss.cc b/net/cert/x509_util_nss.cc index f8fbd6f..de09b95 100644 --- a/net/cert/x509_util_nss.cc +++ b/net/cert/x509_util_nss.cc @@ -246,6 +246,7 @@ bool CreateSelfSignedCert(crypto::RSAPrivateKey* key, base::Time not_valid_after, std::string* der_cert) { DCHECK(key); + DCHECK(!strncmp(subject.c_str(), "CN=", 3U)); CERTCertificate* cert = CreateCertificate(key->public_key(), subject, serial_number, diff --git a/net/cert/x509_util_openssl.cc b/net/cert/x509_util_openssl.cc index e4dec99..89c5394 100644 --- a/net/cert/x509_util_openssl.cc +++ b/net/cert/x509_util_openssl.cc @@ -9,6 +9,8 @@ #include "base/logging.h" #include "base/strings/string_piece.h" +#include "crypto/openssl_util.h" +#include "crypto/rsa_private_key.h" #include "net/cert/x509_cert_types.h" namespace net { @@ -65,8 +67,97 @@ bool CreateSelfSignedCert(crypto::RSAPrivateKey* key, base::Time not_valid_before, base::Time not_valid_after, std::string* der_encoded) { - NOTIMPLEMENTED(); - return false; + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + static const char kCommonNamePrefix[] = "CN="; + const size_t kCommonNamePrefixLen = sizeof(kCommonNamePrefix) - 1; + + // Put the serial number into an OpenSSL-friendly object. + crypto::ScopedOpenSSL<ASN1_INTEGER, ASN1_INTEGER_free> asn1_serial( + ASN1_INTEGER_new()); + if (!asn1_serial.get() || + !ASN1_INTEGER_set(asn1_serial.get(), static_cast<long>(serial_number))) { + LOG(ERROR) << "Invalid serial number " << serial_number; + return false; + } + + // Do the same for the time stamps. + crypto::ScopedOpenSSL<ASN1_TIME, ASN1_TIME_free> asn1_not_before_time( + ASN1_TIME_set(NULL, not_valid_before.ToTimeT())); + if (!asn1_not_before_time.get()) { + LOG(ERROR) << "Invalid not_valid_before time: " + << not_valid_before.ToTimeT(); + return false; + } + + crypto::ScopedOpenSSL<ASN1_TIME, ASN1_TIME_free> asn1_not_after_time( + ASN1_TIME_set(NULL, not_valid_after.ToTimeT())); + if (!asn1_not_after_time.get()) { + LOG(ERROR) << "Invalid not_valid_after time: " << not_valid_after.ToTimeT(); + return false; + } + + // Because |common_name| only contains a common name and starts with 'CN=', + // there is no need for a full RFC 2253 parser here. Do some sanity checks + // though. + if (common_name.size() < kCommonNamePrefixLen || + strncmp(common_name.c_str(), kCommonNamePrefix, kCommonNamePrefixLen)) { + LOG(ERROR) << "Common name must begin with " << kCommonNamePrefix; + return false; + } + if (common_name.size() > INT_MAX) { + LOG(ERROR) << "Common name too long"; + return false; + } + unsigned char* common_name_str = + reinterpret_cast<unsigned char*>(const_cast<char*>(common_name.data())) + + kCommonNamePrefixLen; + int common_name_len = + static_cast<int>(common_name.size()) - kCommonNamePrefixLen; + + crypto::ScopedOpenSSL<X509_NAME, X509_NAME_free> name(X509_NAME_new()); + if (!name.get() || !X509_NAME_add_entry_by_NID(name.get(), + NID_commonName, + MBSTRING_ASC, + common_name_str, + common_name_len, + -1, + 0)) { + LOG(ERROR) << "Can't parse common name: " << common_name.c_str(); + return false; + } + + // Now create certificate and populate it. + crypto::ScopedOpenSSL<X509, X509_free> cert(X509_new()); + if (!cert.get() || !X509_set_version(cert.get(), 2L) /* i.e. version 3 */ || + !X509_set_pubkey(cert.get(), key->key()) || + !X509_set_serialNumber(cert.get(), asn1_serial.get()) || + !X509_set_notBefore(cert.get(), asn1_not_before_time.get()) || + !X509_set_notAfter(cert.get(), asn1_not_after_time.get()) || + !X509_set_subject_name(cert.get(), name.get()) || + !X509_set_issuer_name(cert.get(), name.get())) { + LOG(ERROR) << "Could not create certificate"; + return false; + } + + // Sign it with the private key. + if (!X509_sign(cert.get(), key->key(), EVP_sha1())) { + LOG(ERROR) << "Could not sign certificate with key."; + return false; + } + + // Convert it into a DER-encoded string copied to |der_encoded|. + int der_data_length = i2d_X509(cert.get(), NULL); + if (der_data_length < 0) + return false; + + der_encoded->resize(static_cast<size_t>(der_data_length)); + unsigned char* der_data = + reinterpret_cast<unsigned char*>(&(*der_encoded)[0]); + + if (i2d_X509(cert.get(), &der_data) < 0) + return false; + + return true; } bool ParsePrincipalKeyAndValueByIndex(X509_NAME* name, diff --git a/net/cert/x509_util_unittest.cc b/net/cert/x509_util_unittest.cc index cc13d96..8fa24d2 100644 --- a/net/cert/x509_util_unittest.cc +++ b/net/cert/x509_util_unittest.cc @@ -52,7 +52,6 @@ TEST(X509UtilTest, SortClientCertificates) { ASSERT_FALSE(certs[5].get()); } -#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. TEST(X509UtilTest, CreateSelfSigned) { @@ -183,7 +182,6 @@ TEST(X509UtilTest, CreateSelfSigned) { EXPECT_EQ("subject", cert->subject().GetDisplayName()); EXPECT_FALSE(cert->HasExpired()); } -#endif } // namespace x509_util |