Index: net/base/x509_certificate_mac.cc |
=================================================================== |
--- net/base/x509_certificate_mac.cc (revision 49024) |
+++ net/base/x509_certificate_mac.cc (working copy) |
@@ -8,6 +8,8 @@ |
#include <Security/Security.h> |
#include <time.h> |
+#include "base/file_path.h" |
+#include "base/file_util.h" |
#include "base/scoped_cftyperef.h" |
#include "base/logging.h" |
#include "base/pickle.h" |
@@ -358,6 +360,80 @@ |
return SecTrustGetResult(trust, &status, out_cert_chain, &status_chain); |
} |
+void AddCertificatesFromData(const char* data, size_t length, |
+ SecExternalFormat format, |
+ X509Certificate::OSCertHandles* output) { |
+ // In order to use SecKeychainItemImport successfully, a destination |
+ // keychain must be specified to receive the created objects. Further, |
+ // the created objects remain valid only for as long as the keychain does. |
+ // In order to match the handling on Windows/NSS, which has the handles |
+ // valid for an arbitrary length of time, a temporary keychain is created, |
+ // the certificates are imported into it, and then free-standing |
+ // certificates are created from the DER representation of the original |
+ // keychain item. |
+ FilePath temp_file; |
+ if (!file_util::CreateTemporaryFile(&temp_file)) |
+ return; |
+ |
+ SecExternalFormat input_format = format; |
+ scoped_cftyperef<CFDataRef> local_data(CFDataCreateWithBytesNoCopy( |
+ kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data), |
+ length, kCFAllocatorDefault); |
+ |
+ scoped_cftyperef<SecKeychainRef> temp_keychain; |
+ SecKeychainRef keychain = NULL; |
+ OSStatus rv = SecKeychainCreate(temp_file.value().c_str(), 0, NULL, FALSE, |
+ NULL, &keychain); |
+ if (rv != noErr) { |
+ DLOG(WARNING) << rv << " Unable to create temporary keychain at " |
+ << temp_file.value(); |
+ return; |
+ } |
+ |
+ temp_keychain.reset(keychain); |
+ keychain = NULL; |
+ |
+ CFArrayRef items = NULL; |
+ rv = SecKeychainItemImport(local_data, NULL, &input_format, NULL, |
+ 0, NULL, NULL, temp_keychain, &items); |
+ if (rv != noErr) { |
+ scoped_cftyperef<CFArrayRef> scoped_items(items); |
+ CFTypeID certTypeID = SecCertificateGetTypeID(); |
+ |
+ for (CFIndex i = 0; i < CFArrayGetCount(items); ++i) { |
+ SecKeychainItemRef item = CFArrayGetValueAtIndex(items, i); |
+ |
+ // While inputFormat implies only certificates will be imported, if/when |
+ // other formats (eg: PKCS#12) are supported, this may also include |
+ // private keys or other items types, so filter appropriately. |
+ if (CFGetTypeID(item) == certTypeID) { |
+ // Create a freestanding SecCertificateRef from the keychain item. |
+ // Unfortunately, this invokes a double-parse of the DER data, which |
+ // is less than optimally efficient. |
+ scoped_cftyperef<CFDataRef> scoped_cert_data = |
+ SecCertificateCopyData(reinterpret_cast<SecCertificateRef>(item)); |
+ if (scoped_cert_data == NULL) |
+ continue; |
+ |
+ SecCertificateRef cert = SecCertificateCreateWithData(NULL, |
+ scoped_cert_data); |
+ if (cert == NULL) |
+ continue; |
+ |
+ output->push_back(cert); |
+ } |
+ } else { |
+ DLOG(WARNING) << rv << " Unable to import items from data of length " |
+ << length; |
+ } |
+ |
+ rv = SecKeychainDelete(temp_keychain); |
+ if (rv != noErr) { |
+ DLOG(WARNING) << rv << " Unable to remove temporary keychain at " |
+ << temp_file.value(); |
+ } |
+} |
+ |
} // namespace |
void X509Certificate::Initialize() { |
@@ -387,7 +463,7 @@ |
if (!pickle.ReadData(pickle_iter, &data, &length)) |
return NULL; |
- return CreateFromBytes(data, length); |
+ return CreateFromBytes(data, length, X509Certificate::FORMAT_DER); |
} |
void X509Certificate::Persist(Pickle* pickle) { |
@@ -647,7 +723,7 @@ |
// static |
X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( |
- const char* data, int length) { |
+ const char* data, size_t length) { |
CSSM_DATA cert_data; |
cert_data.Data = const_cast<uint8*>(reinterpret_cast<const uint8*>(data)); |
cert_data.Length = length; |
@@ -664,6 +740,34 @@ |
} |
// static |
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( |
+ const char* data, size_t length, CertificateFormat format) { |
+ OSCertHandles results; |
+ |
+ switch (format) { |
+ case FORMAT_DER: |
+ { |
+ OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); |
+ if (handle) |
+ results.push_back(handle); |
+ } |
+ break; |
+ case FORMAT_PKCS7: |
+ AddCertificatesFromData(data, length, kSecFormatPKCS7, &results); |
+ break; |
+ case FORMAT_LEGACY_NETSCAPE: |
+ AddCertificatesFromData(data, length, kSecFormatNetscapeCertSequence, |
+ &results); |
+ break; |
+ default: |
+ NOTREACHED() << "Certificate format " << format << " unimplemented"; |
+ break; |
+ } |
+ |
+ return results; |
+} |
+ |
+// static |
X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( |
OSCertHandle handle) { |
if (!handle) |
@@ -763,7 +867,7 @@ |
} |
// static |
-bool X509Certificate::GetSSLClientCertificates ( |
+bool X509Certificate::GetSSLClientCertificates( |
const std::string& server_domain, |
const std::vector<Principal>& valid_issuers, |
std::vector<scoped_refptr<X509Certificate> >* certs) { |
@@ -775,7 +879,7 @@ |
SecIdentityRef identity = NULL; |
if (SecIdentityCopyPreference(domain_str, |
0, |
- NULL, // validIssuers argument is ignored :( |
+ NULL, // validIssuers argument is ignored :( |
&identity) == noErr) |
preferred_identity.reset(identity); |
} |
@@ -867,7 +971,7 @@ |
} |
CFRelease(cert_chain); |
} |
-exit: |
+ exit: |
if (result) |
LOG(ERROR) << "CreateIdentityCertificateChain error " << result; |
return chain.release(); |