diff options
author | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-08 14:28:29 +0000 |
---|---|---|
committer | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-08 14:28:29 +0000 |
commit | eed0011868749ef759ab95cb906fc0fe8e23cde1 (patch) | |
tree | 88c449642e823cb60826b424c01fa4ceac2d7b7e | |
parent | 13f50ec8d5db4bc3b6dd5f2b062f96cd9f5cc031 (diff) | |
download | chromium_src-eed0011868749ef759ab95cb906fc0fe8e23cde1.zip chromium_src-eed0011868749ef759ab95cb906fc0fe8e23cde1.tar.gz chromium_src-eed0011868749ef759ab95cb906fc0fe8e23cde1.tar.bz2 |
Add ability to create self signed certs to mac.
BUG=67929
TEST=BUILD
Review URL: http://codereview.chromium.org/6312157
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74115 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/crypto/cssm_init.cc | 134 | ||||
-rw-r--r-- | base/crypto/cssm_init.h | 31 | ||||
-rw-r--r-- | base/crypto/rsa_private_key.cc | 32 | ||||
-rw-r--r-- | base/crypto/rsa_private_key.h | 6 | ||||
-rw-r--r-- | base/crypto/rsa_private_key_mac.cc | 51 | ||||
-rw-r--r-- | base/crypto/signature_creator_mac.cc | 10 | ||||
-rw-r--r-- | net/DEPS | 1 | ||||
-rw-r--r-- | net/base/x509_certificate_mac.cc | 235 | ||||
-rw-r--r-- | net/base/x509_certificate_unittest.cc | 98 |
9 files changed, 537 insertions, 61 deletions
diff --git a/base/crypto/cssm_init.cc b/base/crypto/cssm_init.cc index eea9d1b..570dcc3 100644 --- a/base/crypto/cssm_init.cc +++ b/base/crypto/cssm_init.cc @@ -7,6 +7,7 @@ #include <Security/SecBase.h> #include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" #include "base/singleton.h" #include "base/synchronization/lock.h" #include "base/sys_string_conversions.h" @@ -20,6 +21,22 @@ namespace { +void* CSSMMalloc(CSSM_SIZE size, void* alloc_ref) { + return malloc(size); +} + +void CSSMFree(void* mem_ptr, void* alloc_ref) { + free(mem_ptr); +} + +void* CSSMRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) { + return realloc(ptr, size); +} + +void* CSSMCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) { + return calloc(num, size); +} + class CSSMInitSingleton { public: static CSSMInitSingleton* GetInstance() { @@ -27,10 +44,15 @@ class CSSMInitSingleton { LeakySingletonTraits<CSSMInitSingleton> >::get(); } - CSSM_CSP_HANDLE csp_handle() const { return csp_handle_; } + CSSM_CSP_HANDLE csp_handle() const { return csp_handle_; } + CSSM_CL_HANDLE cl_handle() const { return cl_handle_; } + CSSM_TP_HANDLE tp_handle() const { return tp_handle_; } private: - CSSMInitSingleton() : inited_(false), loaded_(false), csp_handle_(NULL) { + CSSMInitSingleton() + : inited_(false), csp_loaded_(false), cl_loaded_(false), + tp_loaded_(false), csp_handle_(NULL), cl_handle_(NULL), + tp_handle_(NULL) { static CSSM_VERSION version = {2, 0}; // TODO(wtc): what should our caller GUID be? static const CSSM_GUID test_guid = { @@ -51,13 +73,42 @@ class CSSMInitSingleton { NOTREACHED(); return; } - loaded_ = true; + csp_loaded_ = true; + crtn = CSSM_ModuleLoad( + &gGuidAppleX509CL, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + cl_loaded_ = true; + crtn = CSSM_ModuleLoad( + &gGuidAppleX509TP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + tp_loaded_ = true; + + const CSSM_API_MEMORY_FUNCS cssmMemoryFunctions = { + CSSMMalloc, + CSSMFree, + CSSMRealloc, + CSSMCalloc, + NULL + }; - crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, - &base::kCssmMemoryFunctions, 0, + crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, &cssmMemoryFunctions, 0, CSSM_SERVICE_CSP, 0, CSSM_KEY_HIERARCHY_NONE, NULL, 0, NULL, &csp_handle_); DCHECK(crtn == CSSM_OK); + crtn = CSSM_ModuleAttach(&gGuidAppleX509CL, &version, &cssmMemoryFunctions, + 0, CSSM_SERVICE_CL, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &cl_handle_); + DCHECK(crtn == CSSM_OK); + crtn = CSSM_ModuleAttach(&gGuidAppleX509TP, &version, &cssmMemoryFunctions, + 0, CSSM_SERVICE_TP, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &tp_handle_); + DCHECK(crtn == CSSM_OK); } ~CSSMInitSingleton() { @@ -66,10 +117,26 @@ class CSSMInitSingleton { CSSM_RETURN crtn = CSSM_ModuleDetach(csp_handle_); DCHECK(crtn == CSSM_OK); } - if (loaded_) { + if (cl_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(cl_handle_); + DCHECK(crtn == CSSM_OK); + } + if (tp_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(tp_handle_); + DCHECK(crtn == CSSM_OK); + } + if (csp_loaded_) { crtn = CSSM_ModuleUnload(&gGuidAppleCSP, NULL, NULL); DCHECK(crtn == CSSM_OK); } + if (cl_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleX509CL, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } + if (tp_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleX509TP, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } if (inited_) { crtn = CSSM_Terminate(); DCHECK(crtn == CSSM_OK); @@ -77,8 +144,12 @@ class CSSMInitSingleton { } bool inited_; // True if CSSM_Init has been called successfully. - bool loaded_; // True if CSSM_ModuleLoad has been called successfully. + bool csp_loaded_; // True if gGuidAppleCSP has been loaded + bool cl_loaded_; // True if gGuidAppleX509CL has been loaded. + bool tp_loaded_; // True if gGuidAppleX509TP has been loaded. CSSM_CSP_HANDLE csp_handle_; + CSSM_CL_HANDLE cl_handle_; + CSSM_TP_HANDLE tp_handle_; friend struct DefaultSingletonTraits<CSSMInitSingleton>; }; @@ -117,45 +188,44 @@ CSSM_CSP_HANDLE GetSharedCSPHandle() { return CSSMInitSingleton::GetInstance()->csp_handle(); } -void* CSSMMalloc(CSSM_SIZE size, void *alloc_ref) { - return malloc(size); +CSSM_CL_HANDLE GetSharedCLHandle() { + return CSSMInitSingleton::GetInstance()->cl_handle(); } -void CSSMFree(void* mem_ptr, void* alloc_ref) { - free(mem_ptr); +CSSM_TP_HANDLE GetSharedTPHandle() { + return CSSMInitSingleton::GetInstance()->tp_handle(); } -void* CSSMRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) { - return realloc(ptr, size); +void* CSSMMalloc(CSSM_SIZE size) { + return ::CSSMMalloc(size, NULL); } -void* CSSMCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) { - return calloc(num, size); +void CSSMFree(void* ptr) { + ::CSSMFree(ptr, NULL); } -const CSSM_API_MEMORY_FUNCS kCssmMemoryFunctions = { - CSSMMalloc, - CSSMFree, - CSSMRealloc, - CSSMCalloc, - NULL -}; - -void LogCSSMError(const char *fn_name, CSSM_RETURN err) { +void LogCSSMError(const char* fn_name, CSSM_RETURN err) { if (!err) return; - CFStringRef cfstr = SecCopyErrorMessageString(err, NULL); - if (cfstr) { - std::string err_name = SysCFStringRefToUTF8(cfstr); - CFRelease(cfstr); - LOG(ERROR) << fn_name << " returned " << err << " (" << err_name << ")"; - } else { - LOG(ERROR) << fn_name << " returned " << err; - } + base::mac::ScopedCFTypeRef<CFStringRef> cfstr( + SecCopyErrorMessageString(err, NULL)); + LOG(ERROR) << fn_name << " returned " << err + << " (" << SysCFStringRefToUTF8(cfstr) << ")"; } base::Lock& GetMacSecurityServicesLock() { return SecurityServicesSingleton::GetInstance()->lock(); } +ScopedCSSMData::ScopedCSSMData() { + memset(&data_, 0, sizeof(data_)); +} + +ScopedCSSMData::~ScopedCSSMData() { + if (data_.Data) { + CSSMFree(data_.Data); + data_.Data = NULL; + } +} + } // namespace base diff --git a/base/crypto/cssm_init.h b/base/crypto/cssm_init.h index 5644d7e..b51a3b5 100644 --- a/base/crypto/cssm_init.h +++ b/base/crypto/cssm_init.h @@ -8,7 +8,7 @@ #include <Security/cssm.h> -#include "base/scoped_ptr.h" +#include "base/basictypes.h" namespace base { @@ -22,12 +22,22 @@ void EnsureCSSMInit(); // Returns the shared CSP handle used by CSSM functions. CSSM_CSP_HANDLE GetSharedCSPHandle(); +// Returns the shared CL handle used by CSSM functions. +CSSM_CL_HANDLE GetSharedCLHandle(); + +// Returns the shared TP handle used by CSSM functions. +CSSM_TP_HANDLE GetSharedTPHandle(); + // Set of pointers to memory function wrappers that are required for CSSM extern const CSSM_API_MEMORY_FUNCS kCssmMemoryFunctions; // Utility function to log an error message including the error name. void LogCSSMError(const char *function_name, CSSM_RETURN err); +// Utility functions to allocate and release CSSM memory. +void* CSSMMalloc(CSSM_SIZE size); +void CSSMFree(void* ptr); + // The OS X certificate and key management wrappers over CSSM are not // thread-safe. In particular, code that accesses the CSSM database is // problematic. @@ -35,6 +45,25 @@ void LogCSSMError(const char *function_name, CSSM_RETURN err); // http://developer.apple.com/mac/library/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html Lock& GetMacSecurityServicesLock(); +// Wrapper class for CSSM_DATA type. This should only be used when using the +// CL/TP/CSP handles from above, since that's the only time we're guaranteed (or +// supposed to be guaranteed) that our memory management functions will be used. +// Apple's Sec* APIs manage their own memory so it shouldn't be used for those. +// The constructor initializes data_ to zero and the destructor releases the +// data properly. +class ScopedCSSMData { + public: + ScopedCSSMData(); + ~ScopedCSSMData(); + operator CSSM_DATA*() { return &data_; } + CSSM_DATA* operator ->() { return &data_; } + + private: + CSSM_DATA data_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCSSMData); +}; + } // namespace base #endif // BASE_CRYPTO_CSSM_INIT_H_ diff --git a/base/crypto/rsa_private_key.cc b/base/crypto/rsa_private_key.cc index 2ab0c14..024f741 100644 --- a/base/crypto/rsa_private_key.cc +++ b/base/crypto/rsa_private_key.cc @@ -89,19 +89,12 @@ bool PrivateKeyInfoCodec::Export(std::vector<uint8>* output) { bool PrivateKeyInfoCodec::ExportPublicKeyInfo(std::vector<uint8>* output) { // Create a sequence with the modulus (n) and public exponent (e). - std::list<uint8> content; - PrependInteger(&public_exponent_[0], - static_cast<int>(public_exponent_.size()), - &content); - PrependInteger(&modulus_[0], static_cast<int>(modulus_.size()), &content); - PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); - - // Copy the sequence with n and e into a buffer. std::vector<uint8> bit_string; - for (std::list<uint8>::iterator i = content.begin(); i != content.end(); ++i) - bit_string.push_back(*i); - content.clear(); + if (!ExportPublicKey(&bit_string)) + return false; + // Add the sequence as the contents of a bit string. + std::list<uint8> content; PrependBitString(&bit_string[0], static_cast<int>(bit_string.size()), &content); @@ -120,6 +113,23 @@ bool PrivateKeyInfoCodec::ExportPublicKeyInfo(std::vector<uint8>* output) { return true; } +bool PrivateKeyInfoCodec::ExportPublicKey(std::vector<uint8>* output) { + // Create a sequence with the modulus (n) and public exponent (e). + std::list<uint8> content; + PrependInteger(&public_exponent_[0], + static_cast<int>(public_exponent_.size()), + &content); + PrependInteger(&modulus_[0], static_cast<int>(modulus_.size()), &content); + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + + // Copy everything into the output. + output->reserve(content.size()); + for (std::list<uint8>::iterator i = content.begin(); i != content.end(); ++i) + output->push_back(*i); + + return true; +} + bool PrivateKeyInfoCodec::Import(const std::vector<uint8>& input) { if (input.empty()) { return false; diff --git a/base/crypto/rsa_private_key.h b/base/crypto/rsa_private_key.h index 9b8b4fd..5357adc 100644 --- a/base/crypto/rsa_private_key.h +++ b/base/crypto/rsa_private_key.h @@ -64,6 +64,10 @@ class PrivateKeyInfoCodec { // of the PublicKeyInfo structure to |output|. bool ExportPublicKeyInfo(std::vector<uint8>* output); + // Exports the contents of the integer components to the ASN.1 DER encoding + // of the RSAPublicKey structure to |output|. + bool ExportPublicKey(std::vector<uint8>* output); + // Parses the ASN.1 DER encoding of the PrivateKeyInfo structure in |input| // and populates the integer components with |big_endian_| byte-significance. // IMPORTANT NOTE: This is currently *not* security-approved for importing @@ -215,6 +219,7 @@ class RSAPrivateKey { HCRYPTKEY key() { return key_; } #elif defined(OS_MACOSX) CSSM_KEY_PTR key() { return &key_; } + CSSM_KEY_PTR public_key() { return &public_key_; } #endif // Exports the private key to a PKCS #1 PrivateKey block. @@ -257,6 +262,7 @@ class RSAPrivateKey { ScopedHCRYPTKEY key_; #elif defined(OS_MACOSX) CSSM_KEY key_; + CSSM_KEY public_key_; #endif DISALLOW_COPY_AND_ASSIGN(RSAPrivateKey); diff --git a/base/crypto/rsa_private_key_mac.cc b/base/crypto/rsa_private_key_mac.cc index e46e93e..ede8014 100644 --- a/base/crypto/rsa_private_key_mac.cc +++ b/base/crypto/rsa_private_key_mac.cc @@ -26,14 +26,12 @@ RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { return NULL; } - CSSM_KEY public_key; - memset(&public_key, 0, sizeof(CSSM_KEY)); CSSM_DATA label = { 9, const_cast<uint8*>(reinterpret_cast<const uint8*>("temp_key")) }; crtn = CSSM_GenerateKeyPair(cc_handle, CSSM_KEYUSE_VERIFY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, - &public_key, CSSM_KEYUSE_SIGN, + result->public_key(), CSSM_KEYUSE_SIGN, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, NULL, result->key()); CSSM_DeleteContext(cc_handle); @@ -42,9 +40,6 @@ RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { return NULL; } - // Public key is not needed. - CSSM_FreeKey(GetSharedCSPHandle(), NULL, &public_key, CSSM_FALSE); - return result.release(); } @@ -106,6 +101,46 @@ RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( return NULL; } + // Extract a public key from the private key. + // Apple doesn't accept CSSM_KEYBLOB_RAW_FORMAT_X509 as a valid key + // format when attempting to generate certs, so use PKCS1 instead. + PrivateKeyInfoCodec codec(true); + std::vector<uint8> private_key_data; + private_key_data.assign(key.KeyData.Data, + key.KeyData.Data + key.KeyData.Length); + if (!codec.Import(private_key_data)) { + return NULL; + } + std::vector<uint8> public_key_data; + if (!codec.ExportPublicKey(&public_key_data)) { + return NULL; + } + + CSSM_KEY* public_key = result->public_key(); + size_t size = public_key_data.size(); + public_key->KeyData.Data = reinterpret_cast<uint8*>(CSSMMalloc(size)); + if (!public_key->KeyData.Data) { + NOTREACHED() << "CSSMMalloc failed"; + return NULL; + } + memcpy(public_key->KeyData.Data, &public_key_data.front(), size); + public_key->KeyData.Length = size; + public_key->KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; + public_key->KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; + public_key->KeyHeader.BlobType = CSSM_KEYBLOB_RAW; + public_key->KeyHeader.AlgorithmId = CSSM_ALGID_RSA; + public_key->KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; + public_key->KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; + public_key->KeyHeader.KeyUsage = CSSM_KEYUSE_ANY; + + crtn = CSSM_QueryKeySizeInBits( + base::GetSharedCSPHandle(), NULL, public_key, &key_size); + if (crtn) { + DLOG(ERROR) << "CSSM_QueryKeySizeInBits failed " << crtn; + return NULL; + } + public_key->KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; + return result.release(); } @@ -125,6 +160,7 @@ RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( RSAPrivateKey::RSAPrivateKey() { memset(&key_, 0, sizeof(key_)); + memset(&public_key_, 0, sizeof(public_key_)); EnsureCSSMInit(); } @@ -133,6 +169,9 @@ RSAPrivateKey::~RSAPrivateKey() { if (key_.KeyData.Data) { CSSM_FreeKey(GetSharedCSPHandle(), NULL, &key_, CSSM_FALSE); } + if (public_key_.KeyData.Data) { + CSSM_FreeKey(GetSharedCSPHandle(), NULL, &public_key_, CSSM_FALSE); + } } bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) { diff --git a/base/crypto/signature_creator_mac.cc b/base/crypto/signature_creator_mac.cc index d10d54c..1001c64 100644 --- a/base/crypto/signature_creator_mac.cc +++ b/base/crypto/signature_creator_mac.cc @@ -59,19 +59,15 @@ bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { } bool SignatureCreator::Final(std::vector<uint8>* signature) { - CSSM_DATA sig; - memset(&sig, 0, sizeof(CSSM_DATA)); // Allow CSSM allocate memory; - CSSM_RETURN crtn = CSSM_SignDataFinal(sig_handle_, &sig); + ScopedCSSMData sig; + CSSM_RETURN crtn = CSSM_SignDataFinal(sig_handle_, sig); if (crtn) { NOTREACHED(); return false; } - signature->assign(sig.Data, sig.Data + sig.Length); - kCssmMemoryFunctions.free_func(sig.Data, NULL); // Release data alloc'd - // by CSSM - + signature->assign(sig->Data, sig->Data + sig->Length); return true; } @@ -1,5 +1,6 @@ include_rules = [ "+third_party/libevent", + "+third_party/nss", "+third_party/zlib", "+sdch/open_vcdiff", ] diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc index 7c5cc64..3cc1e08 100644 --- a/net/base/x509_certificate_mac.cc +++ b/net/base/x509_certificate_mac.cc @@ -8,8 +8,13 @@ #include <Security/Security.h> #include <time.h> +#include <vector> + +#include "base/crypto/cssm_init.h" +#include "base/crypto/rsa_private_key.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/nss_util.h" #include "base/pickle.h" #include "base/singleton.h" #include "base/mac/scoped_cftyperef.h" @@ -18,6 +23,7 @@ #include "net/base/cert_verify_result.h" #include "net/base/net_errors.h" #include "net/base/test_root_certs.h" +#include "third_party/nss/mozilla/security/nss/lib/certdb/cert.h" using base::mac::ScopedCFTypeRef; using base::Time; @@ -369,6 +375,99 @@ void AddCertificatesFromBytes(const char* data, size_t length, } } +struct CSSMOIDString { + const CSSM_OID* oid_; + std::string string_; +}; + +typedef std::vector<CSSMOIDString> CSSMOIDStringVector; + +bool CERTNameToCSSMOIDVector(CERTName* name, CSSMOIDStringVector* out_values) { + struct OIDCSSMMap { + SECOidTag sec_OID_; + const CSSM_OID* cssm_OID_; + }; + + const OIDCSSMMap kOIDs[] = { + { SEC_OID_AVA_COMMON_NAME, &CSSMOID_CommonName }, + { SEC_OID_AVA_COUNTRY_NAME, &CSSMOID_CountryName }, + { SEC_OID_AVA_LOCALITY, &CSSMOID_LocalityName }, + { SEC_OID_AVA_STATE_OR_PROVINCE, &CSSMOID_StateProvinceName }, + { SEC_OID_AVA_STREET_ADDRESS, &CSSMOID_StreetAddress }, + { SEC_OID_AVA_ORGANIZATION_NAME, &CSSMOID_OrganizationName }, + { SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, &CSSMOID_OrganizationalUnitName }, + { SEC_OID_AVA_DN_QUALIFIER, &CSSMOID_DNQualifier }, + { SEC_OID_RFC1274_UID, &CSSMOID_UniqueIdentifier }, + { SEC_OID_PKCS9_EMAIL_ADDRESS, &CSSMOID_EmailAddress }, + }; + + 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]); + if (tag == SEC_OID_UNKNOWN) { + return false; + } + CSSMOIDString oidString; + bool found_oid = false; + for (size_t oid = 0; oid < ARRAYSIZE_UNSAFE(kOIDs); ++oid) { + if (kOIDs[oid].sec_OID_ == tag) { + SECItem* decode_item = CERT_DecodeAVAValue(&avas[pair]->value); + if (!decode_item) + return false; + + // TODO(wtc): Pass decode_item to CERT_RFC1485_EscapeAndQuote. + std::string value(reinterpret_cast<char*>(decode_item->data), + decode_item->len); + oidString.oid_ = kOIDs[oid].cssm_OID_; + oidString.string_ = value; + out_values->push_back(oidString); + SECITEM_FreeItem(decode_item, PR_TRUE); + found_oid = true; + break; + } + } + if (!found_oid) { + DLOG(ERROR) << "Unrecognized OID: " << tag; + } + } + } + return true; +} + +class ScopedCertName { + public: + explicit ScopedCertName(CERTName* name) : name_(name) { } + ~ScopedCertName() { + if (name_) CERT_DestroyName(name_); + } + operator CERTName*() { return name_; } + + private: + CERTName* name_; +}; + +class ScopedEncodedCertResults { + public: + explicit ScopedEncodedCertResults(CSSM_TP_RESULT_SET* results) + : results_(results) { } + ~ScopedEncodedCertResults() { + if (results_) { + CSSM_ENCODED_CERT* encCert = + reinterpret_cast<CSSM_ENCODED_CERT*>(results_->Results); + for (uint32 i = 0; i < results_->NumberOfResults; i++) { + base::CSSMFree(encCert[i].CertBlob.Data); + } + } + base::CSSMFree(results_->Results); + base::CSSMFree(results_); + } + +private: + CSSM_TP_RESULT_SET* results_; +}; + } // namespace void X509Certificate::Initialize() { @@ -406,8 +505,138 @@ X509Certificate* X509Certificate::CreateSelfSigned( const std::string& subject, uint32 serial_number, base::TimeDelta valid_duration) { - // TODO(port): Implement. - return NULL; + DCHECK(key); + DCHECK(!subject.empty()); + + if (valid_duration.InSeconds() > UINT32_MAX) { + LOG(ERROR) << "valid_duration too big" << valid_duration.InSeconds(); + valid_duration = base::TimeDelta::FromSeconds(UINT32_MAX); + } + + // There is a comment in + // http://www.opensource.apple.com/source/security_certtool/security_certtool-31828/src/CertTool.cpp + // that serial_numbers being passed into CSSM_TP_SubmitCredRequest can't have + // their high bit set. We will continue though and mask it out below. + if (serial_number & 0x80000000) + LOG(ERROR) << "serial_number has high bit set " << serial_number; + + // NSS is used to parse the subject string into a set of + // CSSM_OID/string pairs. There doesn't appear to be a system routine for + // parsing Distinguished Name strings. + base::EnsureNSSInit(); + + CSSMOIDStringVector subject_name_oids; + ScopedCertName subject_name( + CERT_AsciiToName(const_cast<char*>(subject.c_str()))); + if (!CERTNameToCSSMOIDVector(subject_name, &subject_name_oids)) { + DLOG(ERROR) << "Unable to generate CSSMOIDMap from " << subject; + return NULL; + } + + // Convert the map of oid/string pairs into an array of + // CSSM_APPLE_TP_NAME_OIDs. + std::vector<CSSM_APPLE_TP_NAME_OID> cssm_subject_names; + for(CSSMOIDStringVector::iterator iter = subject_name_oids.begin(); + iter != subject_name_oids.end(); ++iter) { + CSSM_APPLE_TP_NAME_OID cssm_subject_name; + cssm_subject_name.oid = iter->oid_; + cssm_subject_name.string = iter->string_.c_str(); + cssm_subject_names.push_back(cssm_subject_name); + } + + if (cssm_subject_names.size() == 0) { + DLOG(ERROR) << "cssm_subject_names.size() == 0. Input: " << subject; + return NULL; + } + + // Set up a certificate request. + CSSM_APPLE_TP_CERT_REQUEST certReq; + memset(&certReq, 0, sizeof(certReq)); + certReq.cspHand = base::GetSharedCSPHandle(); + certReq.clHand = base::GetSharedCLHandle(); + // See comment about serial numbers above. + certReq.serialNumber = serial_number & 0x7fffffff; + certReq.numSubjectNames = cssm_subject_names.size(); + certReq.subjectNames = &cssm_subject_names[0]; + certReq.numIssuerNames = 0; // Root. + certReq.issuerNames = NULL; + certReq.issuerNameX509 = NULL; + certReq.certPublicKey = key->public_key(); + certReq.issuerPrivateKey = key->key(); + // These are the Apple defaults. + certReq.signatureAlg = CSSM_ALGID_SHA1WithRSA; + certReq.signatureOid = CSSMOID_SHA1WithRSA; + certReq.notBefore = 0; + certReq.notAfter = static_cast<uint32>(valid_duration.InSeconds()); + certReq.numExtensions = 0; + certReq.extensions = NULL; + certReq.challengeString = NULL; + + CSSM_TP_REQUEST_SET reqSet; + reqSet.NumberOfRequests = 1; + reqSet.Requests = &certReq; + + CSSM_FIELD policyId; + memset(&policyId, 0, sizeof(policyId)); + policyId.FieldOid = CSSMOID_APPLE_TP_LOCAL_CERT_GEN; + + CSSM_TP_CALLERAUTH_CONTEXT callerAuthContext; + memset(&callerAuthContext, 0, sizeof(callerAuthContext)); + callerAuthContext.Policy.NumberOfPolicyIds = 1; + callerAuthContext.Policy.PolicyIds = &policyId; + + CSSM_TP_HANDLE tp_handle = base::GetSharedTPHandle(); + CSSM_DATA refId; + memset(&refId, 0, sizeof(refId)); + sint32 estTime; + CSSM_RETURN crtn = CSSM_TP_SubmitCredRequest(tp_handle, NULL, + CSSM_TP_AUTHORITY_REQUEST_CERTISSUE, &reqSet, &callerAuthContext, + &estTime, &refId); + if(crtn) { + DLOG(ERROR) << "CSSM_TP_SubmitCredRequest failed " << crtn; + return NULL; + } + + CSSM_BOOL confirmRequired; + CSSM_TP_RESULT_SET *resultSet = NULL; + crtn = CSSM_TP_RetrieveCredResult(tp_handle, &refId, NULL, &estTime, + &confirmRequired, &resultSet); + ScopedEncodedCertResults scopedResults(resultSet); + base::CSSMFree(refId.Data); + if (crtn) { + DLOG(ERROR) << "CSSM_TP_RetrieveCredResult failed " << crtn; + return NULL; + } + + if (confirmRequired) { + // Potential leak here of resultSet. |confirmRequired| should never be + // true. + DLOG(ERROR) << "CSSM_TP_RetrieveCredResult required confirmation"; + return NULL; + } + + if (resultSet->NumberOfResults != 1) { + DLOG(ERROR) << "Unexpected number of results: " + << resultSet->NumberOfResults; + return NULL; + } + + CSSM_ENCODED_CERT* encCert = + reinterpret_cast<CSSM_ENCODED_CERT*>(resultSet->Results); + base::mac::ScopedCFTypeRef<SecCertificateRef> scoped_cert; + SecCertificateRef certificate_ref = NULL; + OSStatus os_status = + SecCertificateCreateFromData(&encCert->CertBlob, encCert->CertType, + encCert->CertEncoding, &certificate_ref); + if (os_status != 0) { + DLOG(ERROR) << "SecCertificateCreateFromData failed: " << os_status; + return NULL; + } + scoped_cert.reset(certificate_ref); + + return CreateFromHandle( + scoped_cert, X509Certificate::SOURCE_LONE_CERT_IMPORT, + X509Certificate::OSCertHandles()); } void X509Certificate::Persist(Pickle* pickle) { @@ -934,6 +1163,7 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const { CFArrayRef cert_chain = NULL; result = CopyCertChain(cert_handle_, &cert_chain); + ScopedCFTypeRef<CFArrayRef> scoped_cert_chain(cert_chain); if (result) { LOG(ERROR) << "CreateIdentityCertificateChain error " << result; return chain.release(); @@ -947,7 +1177,6 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const { cert_chain, CFRangeMake(1, chain_count - 1)); } - CFRelease(cert_chain); } return chain.release(); diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc index 7bd17fb..54af784 100644 --- a/net/base/x509_certificate_unittest.cc +++ b/net/base/x509_certificate_unittest.cc @@ -659,7 +659,7 @@ TEST(X509CertificateTest, IsIssuedBy) { } #endif // defined(OS_MACOSX) -#if defined(USE_NSS) || defined(OS_WIN) +#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(X509CertificateTest, CreateSelfSigned) { @@ -671,6 +671,102 @@ TEST(X509CertificateTest, CreateSelfSigned) { EXPECT_EQ("subject", cert->subject().GetDisplayName()); EXPECT_FALSE(cert->HasExpired()); + + const uint8 private_key_info[] = { + 0x30, 0x82, 0x02, 0x78, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x02, 0x62, 0x30, 0x82, 0x02, 0x5e, 0x02, 0x01, + 0x00, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, + 0x20, 0xdc, 0x7c, 0x9b, 0x0c, 0xdc, 0x51, 0x61, + 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08, + 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, + 0x85, 0x7b, 0x0c, 0x04, 0x13, 0x3f, 0x8d, 0xf4, + 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a, + 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, + 0x9e, 0x36, 0x74, 0x30, 0xda, 0x8a, 0x31, 0x4f, + 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17, + 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, + 0xb2, 0x65, 0x7a, 0x89, 0x4e, 0xb6, 0x47, 0xff, + 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85, + 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, + 0x96, 0xd0, 0xd6, 0x14, 0x6f, 0x13, 0x8d, 0xc5, + 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18, + 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, + 0xe7, 0x1f, 0x0f, 0xe6, 0x0f, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x02, 0x81, 0x80, 0x03, 0x61, 0x89, + 0x37, 0xcb, 0xf2, 0x98, 0xa0, 0xce, 0xb4, 0xcb, + 0x16, 0x13, 0xf0, 0xe6, 0xaf, 0x5c, 0xc5, 0xa7, + 0x69, 0x71, 0xca, 0xba, 0x8d, 0xe0, 0x4d, 0xdd, + 0xed, 0xb8, 0x48, 0x8b, 0x16, 0x93, 0x36, 0x95, + 0xc2, 0x91, 0x40, 0x65, 0x17, 0xbd, 0x7f, 0xd6, + 0xad, 0x9e, 0x30, 0x28, 0x46, 0xe4, 0x3e, 0xcc, + 0x43, 0x78, 0xf9, 0xfe, 0x1f, 0x33, 0x23, 0x1e, + 0x31, 0x12, 0x9d, 0x3c, 0xa7, 0x08, 0x82, 0x7b, + 0x7d, 0x25, 0x4e, 0x5e, 0x19, 0xa8, 0x9b, 0xed, + 0x86, 0xb2, 0xcb, 0x3c, 0xfe, 0x4e, 0xa1, 0xfa, + 0x62, 0x87, 0x3a, 0x17, 0xf7, 0x60, 0xec, 0x38, + 0x29, 0xe8, 0x4f, 0x34, 0x9f, 0x76, 0x9d, 0xee, + 0xa3, 0xf6, 0x85, 0x6b, 0x84, 0x43, 0xc9, 0x1e, + 0x01, 0xff, 0xfd, 0xd0, 0x29, 0x4c, 0xfa, 0x8e, + 0x57, 0x0c, 0xc0, 0x71, 0xa5, 0xbb, 0x88, 0x46, + 0x29, 0x5c, 0xc0, 0x4f, 0x01, 0x02, 0x41, 0x00, + 0xf5, 0x83, 0xa4, 0x64, 0x4a, 0xf2, 0xdd, 0x8c, + 0x2c, 0xed, 0xa8, 0xd5, 0x60, 0x5a, 0xe4, 0xc7, + 0xcc, 0x61, 0xcd, 0x38, 0x42, 0x20, 0xd3, 0x82, + 0x18, 0xf2, 0x35, 0x00, 0x72, 0x2d, 0xf7, 0x89, + 0x80, 0x67, 0xb5, 0x93, 0x05, 0x5f, 0xdd, 0x42, + 0xba, 0x16, 0x1a, 0xea, 0x15, 0xc6, 0xf0, 0xb8, + 0x8c, 0xbc, 0xbf, 0x54, 0x9e, 0xf1, 0xc1, 0xb2, + 0xb3, 0x8b, 0xb6, 0x26, 0x02, 0x30, 0xc4, 0x81, + 0x02, 0x41, 0x00, 0xc0, 0x60, 0x62, 0x80, 0xe1, + 0x22, 0x78, 0xf6, 0x9d, 0x83, 0x18, 0xeb, 0x72, + 0x45, 0xd7, 0xc8, 0x01, 0x7f, 0xa9, 0xca, 0x8f, + 0x7d, 0xd6, 0xb8, 0x31, 0x2b, 0x84, 0x7f, 0x62, + 0xd9, 0xa9, 0x22, 0x17, 0x7d, 0x06, 0x35, 0x6c, + 0xf3, 0xc1, 0x94, 0x17, 0x85, 0x5a, 0xaf, 0x9c, + 0x5c, 0x09, 0x3c, 0xcf, 0x2f, 0x44, 0x9d, 0xb6, + 0x52, 0x68, 0x5f, 0xf9, 0x59, 0xc8, 0x84, 0x2b, + 0x39, 0x22, 0x8f, 0x02, 0x41, 0x00, 0xb2, 0x04, + 0xe2, 0x0e, 0x56, 0xca, 0x03, 0x1a, 0xc0, 0xf9, + 0x12, 0x92, 0xa5, 0x6b, 0x42, 0xb8, 0x1c, 0xda, + 0x4d, 0x93, 0x9d, 0x5f, 0x6f, 0xfd, 0xc5, 0x58, + 0xda, 0x55, 0x98, 0x74, 0xfc, 0x28, 0x17, 0x93, + 0x1b, 0x75, 0x9f, 0x50, 0x03, 0x7f, 0x7e, 0xae, + 0xc8, 0x95, 0x33, 0x75, 0x2c, 0xd6, 0xa4, 0x35, + 0xb8, 0x06, 0x03, 0xba, 0x08, 0x59, 0x2b, 0x17, + 0x02, 0xdc, 0x4c, 0x7a, 0x50, 0x01, 0x02, 0x41, + 0x00, 0x9d, 0xdb, 0x39, 0x59, 0x09, 0xe4, 0x30, + 0xa0, 0x24, 0xf5, 0xdb, 0x2f, 0xf0, 0x2f, 0xf1, + 0x75, 0x74, 0x0d, 0x5e, 0xb5, 0x11, 0x73, 0xb0, + 0x0a, 0xaa, 0x86, 0x4c, 0x0d, 0xff, 0x7e, 0x1d, + 0xb4, 0x14, 0xd4, 0x09, 0x91, 0x33, 0x5a, 0xfd, + 0xa0, 0x58, 0x80, 0x9b, 0xbe, 0x78, 0x2e, 0x69, + 0x82, 0x15, 0x7c, 0x72, 0xf0, 0x7b, 0x18, 0x39, + 0xff, 0x6e, 0xeb, 0xc6, 0x86, 0xf5, 0xb4, 0xc7, + 0x6f, 0x02, 0x41, 0x00, 0x8d, 0x1a, 0x37, 0x0f, + 0x76, 0xc4, 0x82, 0xfa, 0x5c, 0xc3, 0x79, 0x35, + 0x3e, 0x70, 0x8a, 0xbf, 0x27, 0x49, 0xb0, 0x99, + 0x63, 0xcb, 0x77, 0x5f, 0xa8, 0x82, 0x65, 0xf6, + 0x03, 0x52, 0x51, 0xf1, 0xae, 0x2e, 0x05, 0xb3, + 0xc6, 0xa4, 0x92, 0xd1, 0xce, 0x6c, 0x72, 0xfb, + 0x21, 0xb3, 0x02, 0x87, 0xe4, 0xfd, 0x61, 0xca, + 0x00, 0x42, 0x19, 0xf0, 0xda, 0x5a, 0x53, 0xe3, + 0xb1, 0xc5, 0x15, 0xf3 + }; + + std::vector<uint8> input; + input.resize(sizeof(private_key_info)); + memcpy(&input.front(), private_key_info, sizeof(private_key_info)); + + private_key.reset(base::RSAPrivateKey::CreateFromPrivateKeyInfo(input)); + ASSERT_TRUE(private_key.get()); + + cert = net::X509Certificate::CreateSelfSigned( + private_key.get(), "CN=subject", 1, base::TimeDelta::FromDays(1)); + + EXPECT_EQ("subject", cert->subject().GetDisplayName()); + EXPECT_FALSE(cert->HasExpired()); } TEST(X509CertificateTest, GetDEREncoded) { |