Index: net/base/x509_certificate_mac.cc |
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc |
index ed46adc3789cee966583214b9f1d2d07fcf21034..1b464f97a271bbfe118a84787ba5beaa5310b178 100644 |
--- a/net/base/x509_certificate_mac.cc |
+++ b/net/base/x509_certificate_mac.cc |
@@ -8,9 +8,9 @@ |
#include <Security/Security.h> |
#include <time.h> |
-#include "base/scoped_cftyperef.h" |
#include "base/logging.h" |
#include "base/pickle.h" |
+#include "base/scoped_cftyperef.h" |
#include "base/sys_string_conversions.h" |
#include "net/base/cert_status_flags.h" |
#include "net/base/cert_verify_result.h" |
@@ -372,6 +372,44 @@ bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage, |
return false; |
} |
+// Parses |data| of length |length|, attempting to decode it as the specified |
+// |format|. If |data| is in the specified format, any certificates contained |
+// within are stored into |output|. |
+void AddCertificatesFromBytes(const char* data, size_t length, |
+ SecExternalFormat format, |
+ X509Certificate::OSCertHandles* output) { |
+ SecExternalFormat input_format = format; |
+ scoped_cftyperef<CFDataRef> local_data(CFDataCreateWithBytesNoCopy( |
+ kCFAllocatorDefault, reinterpret_cast<const UInt8*>(data), |
+ length, kCFAllocatorNull)); |
+ |
+ CFArrayRef items = NULL; |
+ OSStatus status = SecKeychainItemImport(local_data, NULL, &input_format, |
+ NULL, 0, NULL, NULL, &items); |
+ if (status) { |
+ DLOG(WARNING) << status << " Unable to import items from data of length " |
+ << length; |
+ return; |
+ } |
+ |
+ scoped_cftyperef<CFArrayRef> scoped_items(items); |
+ CFTypeID cert_type_id = SecCertificateGetTypeID(); |
+ |
+ for (CFIndex i = 0; i < CFArrayGetCount(items); ++i) { |
+ SecKeychainItemRef item = reinterpret_cast<SecKeychainItemRef>( |
+ const_cast<void*>(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) == cert_type_id) { |
+ SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(item); |
+ CFRetain(cert); |
+ output->push_back(cert); |
+ } |
+ } |
+} |
+ |
} // namespace |
void X509Certificate::Initialize() { |
@@ -669,15 +707,53 @@ X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( |
OSCertHandle cert_handle = NULL; |
OSStatus status = SecCertificateCreateFromData(&cert_data, |
CSSM_CERT_X_509v3, |
- CSSM_CERT_ENCODING_BER, |
+ CSSM_CERT_ENCODING_DER, |
&cert_handle); |
if (status) |
return NULL; |
+ // SecCertificateCreateFromData() unfortunately will not return any |
+ // errors, as long as simply all pointers are present. The actual decoding |
+ // of the certificate does not happen until an API that requires a CDSA |
+ // handle is called. While SecCertificateGetCLHandle is the most likely |
+ // candidate, as it initializes the parsing, it does not check whether the |
+ // parsing was successful. Instead, SecCertificateGetSubject is used |
+ // (supported since 10.3), as a means to double-check that the parsed |
+ // certificate is valid. |
+ const CSSM_X509_NAME* sanity_check = NULL; |
+ status = SecCertificateGetSubject(cert_handle, &sanity_check); |
+ if (status || !sanity_check) { |
+ CFRelease(cert_handle); |
+ return NULL; |
+ } |
+ |
return cert_handle; |
} |
// static |
+X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( |
+ const char* data, int length, Format format) { |
+ OSCertHandles results; |
+ |
+ switch (format) { |
+ case FORMAT_SINGLE_CERTIFICATE: { |
+ OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); |
+ if (handle) |
+ results.push_back(handle); |
+ break; |
+ } |
+ case FORMAT_PKCS7: |
+ AddCertificatesFromBytes(data, length, kSecFormatPKCS7, &results); |
+ break; |
+ default: |
+ NOTREACHED() << "Certificate format " << format << " unimplemented"; |
+ break; |
+ } |
+ |
+ return results; |
+} |
+ |
+// static |
X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( |
OSCertHandle handle) { |
if (!handle) |