Index: net/base/x509_certificate.cc |
=================================================================== |
--- net/base/x509_certificate.cc (revision 49024) |
+++ net/base/x509_certificate.cc (working copy) |
@@ -12,7 +12,8 @@ |
#include "base/histogram.h" |
#include "base/logging.h" |
-#include "base/time.h" |
+#include "base/string_piece.h" |
+#include "net/base/pem_tokenizer.h" |
namespace net { |
@@ -158,14 +159,82 @@ |
// static |
X509Certificate* X509Certificate::CreateFromBytes(const char* data, |
- int length) { |
- OSCertHandle cert_handle = CreateOSCertHandleFromBytes(data, length); |
- if (!cert_handle) |
+ size_t length, |
+ int format) { |
+ OSCertHandles certificates; |
+ |
+ // Indicates the order to use when trying to decode binary data, which is |
+ // based on (speculation) as to what will be most common -> least common |
+ // TODO(rsleevi): Calculate metrics as to which formats end up being |
+ // encountered and re-arrange this based on actual data. |
+ static CertificateFormat formats[] = { FORMAT_DER, FORMAT_PKCS7, |
+ FORMAT_LEGACY_NETSCAPE }; |
+ |
+ // If it wasn't PEM encoded, try each of the binary formats in order of |
+ // their probability until one successfully decodes. |
+ for (size_t i = 0; certificates.empty() && i < arraysize(formats); |
+ ++i) { |
+ if (format & formats[i]) |
+ certificates = CreateOSCertHandlesFromBytes(data, length, formats[i]); |
+ } |
+ |
+ // No certs were read. Check to see if this is perhaps a PEM-wrapped version. |
+ if (certificates.empty()) { |
+ base::StringPiece data_string(data, length); |
+ std::vector<std::string> pem_headers; |
+ |
+ // To maintain compatibility with NSS/Firefox, CERTIFICATE is a universally |
+ // valid PEM block header. |
+ pem_headers.push_back("CERTIFICATE"); |
+ if (format & FORMAT_PKCS7) |
+ pem_headers.push_back("PKCS7"); |
+ |
+ PEMTokenizer pem_tok(&data_string, pem_headers); |
+ while (pem_tok.GetNext()) { |
+ std::string decoded(pem_tok.data()); |
+ |
+ // Try a single cert decode first. If this succeeds, then the data is |
+ // likely a PEM cert sequence and thus all PEM blocks should be parsed |
+ // as certificates. If it doesn't parse as a single certificate, then |
+ // this is potentially one of the other formats, in which case only the |
+ // first PEM block is consulted. |
+ OSCertHandle handle = NULL; |
+ if (format & FORMAT_PEM) |
+ handle = CreateOSCertHandleFromBytes(decoded.c_str(), decoded.size()); |
+ if (handle != NULL) { |
+ format = FORMAT_PEM; |
+ certificates.push_back(handle); |
+ } else { |
+ // If this is the first block, and other formats are specified, try to |
+ // decode using the binary contents from the PEM block |
+ if (format & ~FORMAT_PEM) |
+ return CreateFromBytes(decoded.c_str(), decoded.size(), format); |
+ |
+ // When parsing PEM, the first bad block encountered terminates |
+ // parsing |
+ break; |
+ } |
+ } |
+ } |
+ |
+ // No certificates parsed. |
+ if (certificates.empty()) |
return NULL; |
- return CreateFromHandle(cert_handle, |
- SOURCE_LONE_CERT_IMPORT, |
- OSCertHandles()); |
+ X509Certificate* result = CreateFromHandle(certificates.front(), |
+ SOURCE_LONE_CERT_IMPORT, |
+ certificates.size() == 1 ? OSCertHandles() : |
+ OSCertHandles(++certificates.begin(), certificates.end())); |
+ |
+ // CreateFromHandle duplicates the handles to the intermediates, but does |
+ // not do so for the primary certificate. Ensure that every intermediate is |
+ // freed |
+ for (OSCertHandles::iterator it = ++certificates.begin(); |
+ it != certificates.end(); ++it) { |
+ FreeOSCertHandle(*it); |
+ } |
+ |
+ return result; |
} |
X509Certificate::X509Certificate(OSCertHandle cert_handle, |
@@ -173,11 +242,9 @@ |
const OSCertHandles& intermediates) |
: cert_handle_(cert_handle), |
source_(source) { |
-#if defined(OS_MACOSX) || defined(OS_WIN) |
// Copy/retain the intermediate cert handles. |
for (size_t i = 0; i < intermediates.size(); ++i) |
intermediate_ca_certs_.push_back(DupOSCertHandle(intermediates[i])); |
-#endif |
// Platform-specific initialization. |
Initialize(); |
} |
@@ -200,10 +267,8 @@ |
X509Certificate::Cache::GetInstance()->Remove(this); |
if (cert_handle_) |
FreeOSCertHandle(cert_handle_); |
-#if defined(OS_MACOSX) || defined(OS_WIN) |
for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) |
FreeOSCertHandle(intermediate_ca_certs_[i]); |
-#endif |
} |
bool X509Certificate::HasExpired() const { |
@@ -211,15 +276,11 @@ |
} |
bool X509Certificate::HasIntermediateCertificate(OSCertHandle cert) { |
-#if defined(OS_MACOSX) || defined(OS_WIN) |
for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { |
if (IsSameOSCert(cert, intermediate_ca_certs_[i])) |
return true; |
} |
return false; |
-#else |
- return true; |
-#endif |
} |
bool X509Certificate::HasIntermediateCertificates(const OSCertHandles& certs) { |