Index: net/base/x509_certificate_mac.cc |
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc |
index 7c5cc643c650d0320b8ca227646c159f79d93235..c565cfa55196216fd8861db3515dab2714321d46 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 <map> |
+ |
+#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,77 @@ void AddCertificatesFromBytes(const char* data, size_t length, |
} |
} |
+struct CSSMOIDCompare { |
+ bool operator()(const CSSM_OID* a, const CSSM_OID* b) const { |
+ return a < b; |
+ } |
+}; |
+ |
+typedef std::map<const CSSM_OID*, std::string, CSSMOIDCompare> CSSMOIDMap; |
+ |
+bool CERTNameToCSSMOIDMap(CERTName* name, CSSMOIDMap* out_values) { |
Ryan Sleevi
2011/02/05 00:23:37
BUG: I could be reading wrong, but I don't think t
dmac
2011/02/08 01:23:45
Done.
|
+ 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; |
+ } |
+ 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); |
+ (*out_values)[kOIDs[oid].cssm_OID_] = value; |
+ 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_; |
+}; |
+ |
} // namespace |
void X509Certificate::Initialize() { |
@@ -406,8 +483,124 @@ X509Certificate* X509Certificate::CreateSelfSigned( |
const std::string& subject, |
uint32 serial_number, |
base::TimeDelta valid_duration) { |
- // TODO(port): Implement. |
- return NULL; |
+ DCHECK(key); |
+ DCHECK(subject.length() > 0); |
Ryan Sleevi
2011/02/05 00:23:37
nit: DCHECK(!subject.empty()) or DCHECK_GT(subject
|
+ |
+ // 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(); |
+ |
+ CSSMOIDMap subject_name_oids; |
+ ScopedCertName subject_name( |
+ CERT_AsciiToName(const_cast<char*>(subject.c_str()))); |
+ if (!CERTNameToCSSMOIDMap(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(CSSMOIDMap::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->first; |
+ cssm_subject_name.string = iter->second.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.challengeString = NULL; |
+ certReq.cspHand = base::GetSharedCSPHandle(); |
+ certReq.clHand = base::GetSharedCLHandle(); |
+ certReq.numSubjectNames = cssm_subject_names.size(); |
+ certReq.subjectNames = &cssm_subject_names[0]; |
+ // See comment about serial numbers above. |
+ certReq.serialNumber = serial_number & 0x7f000000; |
+ certReq.numIssuerNames = 0; // Root. |
+ certReq.issuerNames = NULL; |
+ certReq.issuerNameX509 = NULL; |
+ certReq.certPublicKey = key->public_key(); |
+ certReq.issuerPrivateKey = key->key(); |
+ // This is the Apple defaults. |
Ryan Sleevi
2011/02/05 00:23:37
world's smallest nit: defaults -> default or this
dmac
2011/02/08 01:23:45
Done.
|
+ certReq.signatureAlg = CSSM_ALGID_SHA1WithRSA; |
+ certReq.signatureOid = CSSMOID_SHA1WithRSA; |
+ certReq.notBefore = 0; |
+ certReq.notAfter = valid_duration.InSeconds(); |
Ryan Sleevi
2011/02/05 00:23:37
nit: InSeconds() returns an Int64, but notAfter ex
dmac
2011/02/08 01:23:45
Done.
|
+ certReq.numExtensions = 0; |
+ certReq.extensions = 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(); |
+ base::ScopedCSSMData 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; |
+ base::ScopedCSSMTPtr<CSSM_TP_RESULT_SET> resultSet; |
+ crtn = CSSM_TP_RetrieveCredResult(tp_handle, refId, NULL, &estTime, |
+ &confirmRequired, &resultSet.receive()); |
+ if (crtn) { |
+ DLOG(ERROR) << "CSSM_TP_RetrieveCredResult failed " << crtn; |
+ return NULL; |
+ } |
+ |
+ if (confirmRequired) { |
Ryan Sleevi
2011/02/05 00:23:37
BUG/nit? If confirmRequired is true, it's still po
dmac
2011/02/08 01:23:45
Done.
|
+ DLOG(ERROR) << "CSSM_TP_RetrieveCredResult required confirmation"; |
+ return NULL; |
+ } |
+ |
Ryan Sleevi
2011/02/05 00:23:37
BUG: Check that resultSet->NumberOfResults == 1 be
dmac
2011/02/08 01:23:45
Done.
|
+ CSSM_ENCODED_CERT* encCert = |
+ reinterpret_cast<CSSM_ENCODED_CERT*>(resultSet->Results); |
+ base::mac::ScopedCFTypeRef<SecCertificateRef> scoped_cert; |
+ { |
+ base::AutoLock sec_lock(base::GetMacSecurityServicesLock()); |
Ryan Sleevi
2011/02/05 00:23:37
You shouldn't need to lock here. It's only needed
dmac
2011/02/08 01:23:45
Done.
|
+ 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); |
+ } |
Ryan Sleevi
2011/02/05 00:23:37
BUG: You also need to free
resultSet->Results
and
dmac
2011/02/08 01:23:45
Done.
|
+ return CreateFromHandle( |
+ scoped_cert, X509Certificate::SOURCE_LONE_CERT_IMPORT, |
+ X509Certificate::OSCertHandles()); |
} |
void X509Certificate::Persist(Pickle* pickle) { |
@@ -934,6 +1127,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 +1141,6 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const { |
cert_chain, |
CFRangeMake(1, chain_count - 1)); |
} |
- CFRelease(cert_chain); |
} |
return chain.release(); |