diff options
-rw-r--r-- | net/base/pem_tokenizer.cc | 95 | ||||
-rw-r--r-- | net/base/pem_tokenizer.h | 79 | ||||
-rw-r--r-- | net/base/pem_tokenizer_unittest.cc | 169 | ||||
-rw-r--r-- | net/base/x509_certificate.cc | 89 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 38 | ||||
-rw-r--r-- | net/base/x509_certificate_mac.cc | 80 | ||||
-rw-r--r-- | net/base/x509_certificate_nss.cc | 71 | ||||
-rw-r--r-- | net/base/x509_certificate_unittest.cc | 162 | ||||
-rw-r--r-- | net/base/x509_certificate_win.cc | 69 | ||||
-rw-r--r-- | net/data/ssl/certificates/google.binary.p7b | bin | 0 -> 1661 bytes | |||
-rw-r--r-- | net/data/ssl/certificates/google.chain.pem | 38 | ||||
-rw-r--r-- | net/data/ssl/certificates/google.pem_cert.p7b | 37 | ||||
-rw-r--r-- | net/data/ssl/certificates/google.pem_pkcs7.p7b | 37 | ||||
-rw-r--r-- | net/data/ssl/certificates/google.single.der | bin | 0 -> 805 bytes | |||
-rw-r--r-- | net/data/ssl/certificates/google.single.pem | 19 | ||||
-rw-r--r-- | net/data/ssl/certificates/thawte.single.pem | 19 | ||||
-rw-r--r-- | net/net.gyp | 3 |
17 files changed, 986 insertions, 19 deletions
diff --git a/net/base/pem_tokenizer.cc b/net/base/pem_tokenizer.cc new file mode 100644 index 0000000..0abe5db --- /dev/null +++ b/net/base/pem_tokenizer.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/pem_tokenizer.h" + +#include "base/base64.h" +#include "base/string_util.h" + +namespace { + +const char kPEMSearchBlock[] = "-----BEGIN "; +const char kPEMBeginBlock[] = "-----BEGIN %s-----"; +const char kPEMEndBlock[] = "-----END %s-----"; + +} // namespace + +namespace net { + +using base::StringPiece; + +PEMTokenizer::PEMTokenizer( + const StringPiece& str, + const std::vector<std::string>& allowed_block_types) { + Init(str, allowed_block_types); +} + +bool PEMTokenizer::GetNext() { + while (pos_ != StringPiece::npos) { + // Scan for the beginning of the next PEM encoded block. + pos_ = str_.find(kPEMSearchBlock, pos_); + if (pos_ == StringPiece::npos) + return false; // No more PEM blocks + + std::vector<PEMType>::const_iterator it; + // Check to see if it is of an acceptable block type. + for (it = block_types_.begin(); it != block_types_.end(); ++it) { + if (!str_.substr(pos_).starts_with(it->header)) + continue; + + // Look for a footer matching the header. If none is found, then all + // data following this point is invalid and should not be parsed. + StringPiece::size_type footer_pos = str_.find(it->footer, pos_); + if (footer_pos == StringPiece::npos) { + pos_ = StringPiece::npos; + return false; + } + + // Chop off the header and footer and parse the data in between. + StringPiece::size_type data_begin = pos_ + it->header.size(); + pos_ = footer_pos + it->footer.size(); + block_type_ = it->type; + + StringPiece encoded = str_.substr(data_begin, + footer_pos - data_begin); + if (!base::Base64Decode(CollapseWhitespaceASCII(encoded.as_string(), + true), &data_)) { + // The most likely cause for a decode failure is a datatype that + // includes PEM headers, which are not supported. + break; + } + + return true; + } + + // If the block did not match any acceptable type, move past it and + // continue the search. Otherwise, |pos_| has been updated to the most + // appropriate search position to continue searching from and should not + // be adjusted. + if (it == block_types_.end()) + pos_ += sizeof(kPEMSearchBlock); + } + + return false; +} + +void PEMTokenizer::Init( + const StringPiece& str, + const std::vector<std::string>& allowed_block_types) { + str_ = str; + pos_ = 0; + + // Construct PEM header/footer strings for all the accepted types, to + // reduce parsing later. + for (std::vector<std::string>::const_iterator it = + allowed_block_types.begin(); it != allowed_block_types.end(); ++it) { + PEMType allowed_type; + allowed_type.type = *it; + allowed_type.header = StringPrintf(kPEMBeginBlock, it->c_str()); + allowed_type.footer = StringPrintf(kPEMEndBlock, it->c_str()); + block_types_.push_back(allowed_type); + } +} + +} // namespace net diff --git a/net/base/pem_tokenizer.h b/net/base/pem_tokenizer.h new file mode 100644 index 0000000..eebba2d --- /dev/null +++ b/net/base/pem_tokenizer.h @@ -0,0 +1,79 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_PEM_TOKENIZER_H_ +#define NET_BASE_PEM_TOKENIZER_H_ + +#include <string> +#include <vector> + +#include "base/string_piece.h" + +namespace net { + +// PEMTokenizer is a utility class for the parsing of data encapsulated +// using RFC 1421, Privacy Enhancement for Internet Electronic Mail. It +// does not implement the full specification, most notably it does not +// support the Encapsulated Header Portion described in Section 4.4. +class PEMTokenizer { + public: + // Create a new PEMTokenizer that iterates through |str| searching for + // instances of PEM encoded blocks that are of the |allowed_block_types|. + // |str| must remain valid for the duration of the PEMTokenizer. + PEMTokenizer(const base::StringPiece& str, + const std::vector<std::string>& allowed_block_types); + + // Attempts to decode the next PEM block in the string. Returns false if no + // PEM blocks can be decoded. The decoded PEM block will be available via + // data(). + bool GetNext(); + + // Returns the PEM block type (eg: CERTIFICATE) of the last successfully + // decoded PEM block. + // GetNext() must have returned true before calling this method. + const std::string& block_type() const { return block_type_; } + + // Returns the raw, Base64-decoded data of the last successfully decoded + // PEM block. + // GetNext() must have returned true before calling this method. + const std::string& data() const { return data_; } + + private: + void Init(const base::StringPiece& str, + const std::vector<std::string>& allowed_block_types); + + // A simple cache of the allowed PEM header and footer for a given PEM + // block type, so that it is only computed once. + struct PEMType { + std::string type; + std::string header; + std::string footer; + }; + + // The string to search, which must remain valid for as long as this class + // is around. + base::StringPiece str_; + + // The current position within |str_| that searching should begin from, + // or StringPiece::npos if iteration is complete + base::StringPiece::size_type pos_; + + // The type of data that was encoded, as indicated in the PEM + // Pre-Encapsulation Boundary (eg: CERTIFICATE, PKCS7, or + // PRIVACY-ENHANCED MESSAGE). + std::string block_type_; + + // The types of PEM blocks that are allowed. PEM blocks that are not of + // one of these types will be skipped. + std::vector<PEMType> block_types_; + + // The raw (Base64-decoded) data of the last successfully decoded block. + std::string data_; + + DISALLOW_COPY_AND_ASSIGN(PEMTokenizer); +}; + +} // namespace net + +#endif // NET_BASE_PEM_TOKENIZER_H_ diff --git a/net/base/pem_tokenizer_unittest.cc b/net/base/pem_tokenizer_unittest.cc new file mode 100644 index 0000000..af2446c --- /dev/null +++ b/net/base/pem_tokenizer_unittest.cc @@ -0,0 +1,169 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/pem_tokenizer.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +TEST(PEMTokenizerTest, BasicParsing) { + const char data[] = + "-----BEGIN EXPECTED-BLOCK-----\n" + "TWF0Y2hlc0FjY2VwdGVkQmxvY2tUeXBl\n" + "-----END EXPECTED-BLOCK-----\n"; + base::StringPiece string_piece(data); + std::vector<std::string> accepted_types; + accepted_types.push_back("EXPECTED-BLOCK"); + + PEMTokenizer tokenizer(string_piece, accepted_types); + EXPECT_TRUE(tokenizer.GetNext()); + + EXPECT_EQ("EXPECTED-BLOCK", tokenizer.block_type()); + EXPECT_EQ("MatchesAcceptedBlockType", tokenizer.data()); + + EXPECT_FALSE(tokenizer.GetNext()); +} + +TEST(PEMTokenizerTest, CarriageReturnLineFeeds) { + const char data[] = + "-----BEGIN EXPECTED-BLOCK-----\r\n" + "TWF0Y2hlc0FjY2VwdGVkQmxvY2tUeXBl\r\n" + "-----END EXPECTED-BLOCK-----\r\n"; + base::StringPiece string_piece(data); + std::vector<std::string> accepted_types; + accepted_types.push_back("EXPECTED-BLOCK"); + + PEMTokenizer tokenizer(string_piece, accepted_types); + EXPECT_TRUE(tokenizer.GetNext()); + + EXPECT_EQ("EXPECTED-BLOCK", tokenizer.block_type()); + EXPECT_EQ("MatchesAcceptedBlockType", tokenizer.data()); + + EXPECT_FALSE(tokenizer.GetNext()); +} + +TEST(PEMTokenizerTest, NoAcceptedBlockTypes) { + const char data[] = + "-----BEGIN UNEXPECTED-BLOCK-----\n" + "SWdub3Jlc1JlamVjdGVkQmxvY2tUeXBl\n" + "-----END UNEXPECTED-BLOCK-----\n"; + base::StringPiece string_piece(data); + std::vector<std::string> accepted_types; + accepted_types.push_back("EXPECTED-BLOCK"); + + PEMTokenizer tokenizer(string_piece, accepted_types); + EXPECT_FALSE(tokenizer.GetNext()); +} + +TEST(PEMTokenizerTest, MultipleAcceptedBlockTypes) { + const char data[] = + "-----BEGIN BLOCK-ONE-----\n" + "RW5jb2RlZERhdGFPbmU=\n" + "-----END BLOCK-ONE-----\n" + "-----BEGIN BLOCK-TWO-----\n" + "RW5jb2RlZERhdGFUd28=\n" + "-----END BLOCK-TWO-----\n"; + base::StringPiece string_piece(data); + std::vector<std::string> accepted_types; + accepted_types.push_back("BLOCK-ONE"); + accepted_types.push_back("BLOCK-TWO"); + + PEMTokenizer tokenizer(string_piece, accepted_types); + EXPECT_TRUE(tokenizer.GetNext()); + + EXPECT_EQ("BLOCK-ONE", tokenizer.block_type()); + EXPECT_EQ("EncodedDataOne", tokenizer.data()); + + EXPECT_TRUE(tokenizer.GetNext()); + + EXPECT_EQ("BLOCK-TWO", tokenizer.block_type()); + EXPECT_EQ("EncodedDataTwo", tokenizer.data()); + + EXPECT_FALSE(tokenizer.GetNext()); +} + +TEST(PEMTokenizerTest, MissingFooter) { + const char data[] = + "-----BEGIN MISSING-FOOTER-----\n" + "RW5jb2RlZERhdGFPbmU=\n" + "-----END MISSING-FOOTER-----\n" + "-----BEGIN MISSING-FOOTER-----\n" + "RW5jb2RlZERhdGFUd28=\n"; + base::StringPiece string_piece(data); + std::vector<std::string> accepted_types; + accepted_types.push_back("MISSING-FOOTER"); + + PEMTokenizer tokenizer(string_piece, accepted_types); + EXPECT_TRUE(tokenizer.GetNext()); + + EXPECT_EQ("MISSING-FOOTER", tokenizer.block_type()); + EXPECT_EQ("EncodedDataOne", tokenizer.data()); + + EXPECT_FALSE(tokenizer.GetNext()); +} + +TEST(PEMTokenizerTest, NestedEncoding) { + const char data[] = + "-----BEGIN BLOCK-ONE-----\n" + "RW5jb2RlZERhdGFPbmU=\n" + "-----BEGIN BLOCK-TWO-----\n" + "RW5jb2RlZERhdGFUd28=\n" + "-----END BLOCK-TWO-----\n" + "-----END BLOCK-ONE-----\n" + "-----BEGIN BLOCK-ONE-----\n" + "RW5jb2RlZERhdGFUaHJlZQ==\n" + "-----END BLOCK-ONE-----\n"; + base::StringPiece string_piece(data); + std::vector<std::string> accepted_types; + accepted_types.push_back("BLOCK-ONE"); + + PEMTokenizer tokenizer(string_piece, accepted_types); + EXPECT_TRUE(tokenizer.GetNext()); + + EXPECT_EQ("BLOCK-ONE", tokenizer.block_type()); + EXPECT_EQ("EncodedDataThree", tokenizer.data()); + + EXPECT_FALSE(tokenizer.GetNext()); +} + +TEST(PEMTokenizerTest, EmptyAcceptedTypes) { + const char data[] = + "-----BEGIN BLOCK-ONE-----\n" + "RW5jb2RlZERhdGFPbmU=\n" + "-----END BLOCK-ONE-----\n"; + base::StringPiece string_piece(data); + std::vector<std::string> accepted_types; + + PEMTokenizer tokenizer(string_piece, accepted_types); + EXPECT_FALSE(tokenizer.GetNext()); +} + +TEST(PEMTokenizerTest, BlockWithHeader) { + const char data[] = + "-----BEGIN BLOCK-ONE-----\n" + "Header-One: Data data data\n" + "Header-Two: \n" + " continuation\n" + "Header-Three: Mix-And,Match\n" + "\n" + "RW5jb2RlZERhdGFPbmU=\n" + "-----END BLOCK-ONE-----\n" + "-----BEGIN BLOCK-ONE-----\n" + "RW5jb2RlZERhdGFUd28=\n" + "-----END BLOCK-ONE-----\n"; + base::StringPiece string_piece(data); + std::vector<std::string> accepted_types; + accepted_types.push_back("BLOCK-ONE"); + + PEMTokenizer tokenizer(string_piece, accepted_types); + EXPECT_TRUE(tokenizer.GetNext()); + + EXPECT_EQ("BLOCK-ONE", tokenizer.block_type()); + EXPECT_EQ("EncodedDataTwo", tokenizer.data()); + + EXPECT_FALSE(tokenizer.GetNext()); +} + +} // namespace net diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc index f5b28a6..1230f27 100644 --- a/net/base/x509_certificate.cc +++ b/net/base/x509_certificate.cc @@ -15,7 +15,9 @@ #include "base/histogram.h" #include "base/logging.h" #include "base/singleton.h" +#include "base/string_piece.h" #include "base/time.h" +#include "net/base/pem_tokenizer.h" namespace net { @@ -31,6 +33,18 @@ bool IsNullFingerprint(const SHA1Fingerprint& fingerprint) { return true; } +// 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 +const X509Certificate::Format kFormatDecodePriority[] = { + X509Certificate::FORMAT_DER, + X509Certificate::FORMAT_PKCS7 +}; + +// The PEM block header used for DER certificates +const char kCertificateHeader[] = "CERTIFICATE"; +// The PEM block header used for PKCS#7 data +const char kPKCS7Header[] = "PKCS7"; + } // namespace // static @@ -186,6 +200,81 @@ X509Certificate* X509Certificate::CreateFromBytes(const char* data, return cert; } +CertificateList X509Certificate::CreateCertificateListFromBytes( + const char* data, int length, int format) { + OSCertHandles certificates; + + // Try each of the formats, in order of parse preference, to see if |data| + // contains the binary representation of a Format. + for (size_t i = 0; certificates.empty() && + i < arraysize(kFormatDecodePriority); ++i) { + if (format & kFormatDecodePriority[i]) + certificates = CreateOSCertHandlesFromBytes(data, length, + kFormatDecodePriority[i]); + } + + // No certs were read. Check to see if it is in a PEM-encoded form. + 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 for any format. + pem_headers.push_back(kCertificateHeader); + if (format & FORMAT_PKCS7) + pem_headers.push_back(kPKCS7Header); + + PEMTokenizer pem_tok(data_string, pem_headers); + while (pem_tok.GetNext()) { + std::string decoded(pem_tok.data()); + + OSCertHandle handle = NULL; + if (format & FORMAT_PEM) + handle = CreateOSCertHandleFromBytes(decoded.c_str(), decoded.size()); + if (handle != NULL) { + // Parsed a DER encoded certificate. All PEM blocks that follow must + // also be DER encoded certificates wrapped inside of PEM blocks. + format = FORMAT_PEM; + certificates.push_back(handle); + continue; + } + + // If the first block failed to parse as a DER certificate, and + // formats other than PEM are acceptable, check to see if the decoded + // data is one of the accepted formats. + if (format & ~FORMAT_PEM) { + for (size_t i = 0; certificates.empty() && + i < arraysize(kFormatDecodePriority); ++i) { + if (format & kFormatDecodePriority[i]) { + certificates = CreateOSCertHandlesFromBytes(decoded.c_str(), + decoded.size(), kFormatDecodePriority[i]); + } + } + } + + // Stop parsing after the first block for any format but a sequence of + // PEM-encoded DER certificates. The case of FORMAT_PEM is handled + // above, and continues processing until a certificate fails to parse. + break; + } + } + + CertificateList results; + // No certificates parsed. + if (certificates.empty()) + return results; + + for (OSCertHandles::iterator it = certificates.begin(); + it != certificates.end(); ++it) { + X509Certificate* result = CreateFromHandle(*it, SOURCE_LONE_CERT_IMPORT, + OSCertHandles()); + results.push_back(scoped_refptr<X509Certificate>(result)); + FreeOSCertHandle(*it); + } + + return results; +} + X509Certificate::X509Certificate(OSCertHandle cert_handle, Source source, const OSCertHandles& intermediates) diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index d6b3447..284d2fb 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -32,6 +32,8 @@ namespace net { class CertVerifyResult; +typedef std::vector<scoped_refptr<X509Certificate> > CertificateList; + // X509Certificate represents an X.509 certificate used by SSL. class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { public: @@ -72,6 +74,27 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { VERIFY_EV_CERT = 1 << 1, }; + enum Format { + // The data contains a single DER-encoded certificate, or a PEM-encoded + // DER certificate with the PEM encoding block name of "CERTIFICATE". + // Any subsequent blocks will be ignored. + FORMAT_DER = 1 << 0, + + // The data contains a sequence of one or more PEM-encoded, DER + // certificates, with the PEM encoding block name of "CERTIFICATE". + // All PEM blocks will be parsed, until the first error is encountered. + FORMAT_PEM = 1 << 1, + + // The data contains a PKCS#7 SignedData structure, whose certificates + // member is to be used to initialize the certificate and intermediates. + // The data my further be encoding using PEM, specifying block names of + // either "PKCS7" or "CERTIFICATE". + FORMAT_PKCS7 = 1 << 2, + + // Automatically detect the format. + FORMAT_AUTO = FORMAT_DER | FORMAT_PEM | FORMAT_PKCS7, + }; + // Create an X509Certificate from a handle to the certificate object in the // underlying crypto library. |source| specifies where |cert_handle| comes // from. Given two certificate handles for the same certificate, our @@ -84,7 +107,7 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { Source source, const OSCertHandles& intermediates); - // Create an X509Certificate from the BER-encoded representation. + // Create an X509Certificate from the DER-encoded representation. // Returns NULL on failure. // // The returned pointer must be stored in a scoped_refptr<X509Certificate>. @@ -99,6 +122,14 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { static X509Certificate* CreateFromPickle(const Pickle& pickle, void** pickle_iter); + // Parses all of the certificates possible from |data|. |format| is a + // bit-wise OR of Format, indicating the possible formats the + // certificates may have been serialized as. If an error occurs, an empty + // collection will be returned. + static CertificateList CreateCertificateListFromBytes(const char* data, + int length, + int format); + // Creates a X509Certificate from the ground up. Used by tests that simulate // SSL connections. X509Certificate(const std::string& subject, const std::string& issuer, @@ -203,6 +234,11 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { static OSCertHandle CreateOSCertHandleFromBytes(const char* data, int length); + // Creates all possible OS certificate handles from |data| encoded in a + // specific |format|. Returns an empty collection on failure. + static OSCertHandles CreateOSCertHandlesFromBytes( + const char* data, int length, Format format); + // Duplicates (or adds a reference to) an OS certificate handle. static OSCertHandle DupOSCertHandle(OSCertHandle cert_handle); diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc index ed46adc..727fde9 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 + // 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_DER: { + 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) diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc index 8eb337f..dbc4d18 100644 --- a/net/base/x509_certificate_nss.cc +++ b/net/base/x509_certificate_nss.cc @@ -16,6 +16,7 @@ #include "base/logging.h" #include "base/pickle.h" +#include "base/scoped_ptr.h" #include "base/time.h" #include "base/nss_util.h" #include "net/base/cert_status_flags.h" @@ -571,6 +572,22 @@ bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle, return false; } +SECStatus PR_CALLBACK +CollectCertsCallback(void* arg, SECItem** certs, int num_certs) { + X509Certificate::OSCertHandles* results = + reinterpret_cast<X509Certificate::OSCertHandles*>(arg); + + for (int i = 0; i < num_certs; ++i) { + X509Certificate::OSCertHandle handle = + X509Certificate::CreateOSCertHandleFromBytes( + reinterpret_cast<char*>(certs[i]->data), certs[i]->len); + if (handle) + results->push_back(handle); + } + + return SECSuccess; +} + } // namespace void X509Certificate::Initialize() { @@ -721,21 +738,59 @@ bool X509Certificate::VerifyEV() const { // static X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( const char* data, int length) { + if (length < 0) + return NULL; + base::EnsureNSSInit(); if (!NSS_IsInitialized()) return NULL; - // Make a copy of |data| since CERT_DecodeCertPackage might modify it. - char* data_copy = new char[length]; - memcpy(data_copy, data, length); + SECItem der_cert; + der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data)); + der_cert.len = length; + der_cert.type = siDERCertBuffer; // Parse into a certificate structure. - CERTCertificate* cert = CERT_DecodeCertFromPackage(data_copy, length); - delete [] data_copy; - if (!cert) - LOG(ERROR) << "Couldn't parse a certificate from " << length << " bytes"; - return cert; + return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL, + PR_FALSE, PR_TRUE); +} + +// static +X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( + const char* data, int length, Format format) { + OSCertHandles results; + if (length < 0) + return results; + + base::EnsureNSSInit(); + + if (!NSS_IsInitialized()) + return results; + + switch (format) { + case FORMAT_DER: { + OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); + if (handle) + results.push_back(handle); + break; + } + case FORMAT_PKCS7: { + // Make a copy since CERT_DecodeCertPackage may modify it + std::vector<char> data_copy(data, data + length); + + SECStatus result = CERT_DecodeCertPackage(&data_copy[0], + length, CollectCertsCallback, &results); + if (result != SECSuccess) + results.clear(); + break; + } + default: + NOTREACHED() << "Certificate format " << format << " unimplemented"; + break; + } + + return results; } // static diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc index 63eec15..6becea0 100644 --- a/net/base/x509_certificate_unittest.cc +++ b/net/base/x509_certificate_unittest.cc @@ -76,6 +76,93 @@ unsigned char unosoft_hu_fingerprint[] = { 0x25, 0x66, 0xf2, 0xec, 0x8b, 0x0f, 0xbf, 0xd8 }; +// The fingerprint of the Google certificate used in the parsing tests, +// which is newer than the one included in the x509_certificate_data.h +unsigned char google_parse_fingerprint[] = { + 0x40, 0x50, 0x62, 0xe5, 0xbe, 0xfd, 0xe4, 0xaf, 0x97, 0xe9, 0x38, 0x2a, + 0xf1, 0x6c, 0xc8, 0x7c, 0x8f, 0xb7, 0xc4, 0xe2 +}; + +// The fingerprint for the Thawte SGC certificate +unsigned char thawte_parse_fingerprint[] = { + 0xec, 0x07, 0x10, 0x03, 0xd8, 0xf5, 0xa3, 0x7f, 0x42, 0xc4, 0x55, 0x7f, + 0x65, 0x6a, 0xae, 0x86, 0x65, 0xfa, 0x4b, 0x02 +}; + +// Dec 18 00:00:00 2009 GMT +const double kGoogleParseValidFrom = 1261094400; +// Dec 18 23:59:59 2011 GMT +const double kGoogleParseValidTo = 1324252799; + +struct CertificateFormatTestData { + const char* file_name; + X509Certificate::Format format; + unsigned char* chain_fingerprints[3]; +}; + +const CertificateFormatTestData FormatTestData[] = { + // DER Parsing - single certificate, DER encoded + { "google.single.der", X509Certificate::FORMAT_DER, + { google_parse_fingerprint, + NULL, } }, + // DER parsing - single certificate, PEM encoded + { "google.single.pem", X509Certificate::FORMAT_DER, + { google_parse_fingerprint, + NULL, } }, + // PEM parsing - single certificate, PEM encoded with a PEB of + // "CERTIFICATE" + { "google.single.pem", X509Certificate::FORMAT_PEM, + { google_parse_fingerprint, + NULL, } }, + // PEM parsing - sequence of certificates, PEM encoded with a PEB of + // "CERTIFICATE" + { "google.chain.pem", X509Certificate::FORMAT_PEM, + { google_parse_fingerprint, + thawte_parse_fingerprint, + NULL, } }, + // PKCS#7 parsing - "degenerate" SignedData collection of certificates, DER + // encoding + { "google.binary.p7b", X509Certificate::FORMAT_PKCS7, + { google_parse_fingerprint, + thawte_parse_fingerprint, + NULL, } }, + // PKCS#7 parsing - "degenerate" SignedData collection of certificates, PEM + // encoded with a PEM PEB of "CERTIFICATE" + { "google.pem_cert.p7b", X509Certificate::FORMAT_PKCS7, + { google_parse_fingerprint, + thawte_parse_fingerprint, + NULL, } }, + // PKCS#7 parsing - "degenerate" SignedData collection of certificates, PEM + // encoded with a PEM PEB of "PKCS7" + { "google.pem_pkcs7.p7b", X509Certificate::FORMAT_PKCS7, + { google_parse_fingerprint, + thawte_parse_fingerprint, + NULL, } }, + // All of the above, this time using auto-detection + { "google.single.der", X509Certificate::FORMAT_AUTO, + { google_parse_fingerprint, + NULL, } }, + { "google.single.pem", X509Certificate::FORMAT_AUTO, + { google_parse_fingerprint, + NULL, } }, + { "google.chain.pem", X509Certificate::FORMAT_AUTO, + { google_parse_fingerprint, + thawte_parse_fingerprint, + NULL, } }, + { "google.binary.p7b", X509Certificate::FORMAT_AUTO, + { google_parse_fingerprint, + thawte_parse_fingerprint, + NULL, } }, + { "google.pem_cert.p7b", X509Certificate::FORMAT_AUTO, + { google_parse_fingerprint, + thawte_parse_fingerprint, + NULL, } }, + { "google.pem_pkcs7.p7b", X509Certificate::FORMAT_AUTO, + { google_parse_fingerprint, + thawte_parse_fingerprint, + NULL, } }, +}; + // Returns a FilePath object representing the src/net/data/ssl/certificates // directory in the source tree. FilePath GetTestCertsDirectory() { @@ -100,12 +187,22 @@ X509Certificate* ImportCertFromFile(const FilePath& certs_dir, return X509Certificate::CreateFromBytes(cert_data.data(), cert_data.size()); } -} // namespace - -TEST(X509CertificateTest, GoogleCertParsing) { - scoped_refptr<X509Certificate> google_cert = X509Certificate::CreateFromBytes( - reinterpret_cast<const char*>(google_der), sizeof(google_der)); +CertificateList CreateCertificateListFromFile( + const FilePath& certs_dir, + const std::string& cert_file, + int format) { + FilePath cert_path = certs_dir.AppendASCII(cert_file); + std::string cert_data; + if (!file_util::ReadFileToString(cert_path, &cert_data)) + return CertificateList(); + return X509Certificate::CreateCertificateListFromBytes(cert_data.data(), + cert_data.size(), + format); +} +void CheckGoogleCert(const scoped_refptr<X509Certificate>& google_cert, + unsigned char* expected_fingerprint, + double valid_from, double valid_to) { ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert); const CertPrincipal& subject = google_cert->subject(); @@ -132,14 +229,14 @@ TEST(X509CertificateTest, GoogleCertParsing) { // Use DoubleT because its epoch is the same on all platforms const Time& valid_start = google_cert->valid_start(); - EXPECT_EQ(1238192407, valid_start.ToDoubleT()); // Mar 27 22:20:07 2009 GMT + EXPECT_EQ(valid_from, valid_start.ToDoubleT()); const Time& valid_expiry = google_cert->valid_expiry(); - EXPECT_EQ(1269728407, valid_expiry.ToDoubleT()); // Mar 27 22:20:07 2010 GMT + EXPECT_EQ(valid_to, valid_expiry.ToDoubleT()); const SHA1Fingerprint& fingerprint = google_cert->fingerprint(); for (size_t i = 0; i < 20; ++i) - EXPECT_EQ(google_fingerprint[i], fingerprint.data[i]); + EXPECT_EQ(expected_fingerprint[i], fingerprint.data[i]); std::vector<std::string> dns_names; google_cert->GetDNSNames(&dns_names); @@ -156,6 +253,18 @@ TEST(X509CertificateTest, GoogleCertParsing) { #endif } +} // namespace + +TEST(X509CertificateTest, GoogleCertParsing) { + scoped_refptr<X509Certificate> google_cert = + X509Certificate::CreateFromBytes( + reinterpret_cast<const char*>(google_der), sizeof(google_der)); + + CheckGoogleCert(google_cert, google_fingerprint, + 1238192407, // Mar 27 22:20:07 2009 GMT + 1269728407); // Mar 27 22:20:07 2010 GMT +} + TEST(X509CertificateTest, WebkitCertParsing) { scoped_refptr<X509Certificate> webkit_cert = X509Certificate::CreateFromBytes( reinterpret_cast<const char*>(webkit_der), sizeof(webkit_der)); @@ -528,4 +637,41 @@ TEST(X509CertificateTest, IntermediateCertificates) { } #endif +class X509CertificateParseTest + : public testing::TestWithParam<CertificateFormatTestData> { + public: + virtual ~X509CertificateParseTest() {} + virtual void SetUp() { + test_data_ = GetParam(); + } + virtual void TearDown() {} + + protected: + CertificateFormatTestData test_data_; +}; + +TEST_P(X509CertificateParseTest, CanParseFormat) { + FilePath certs_dir = GetTestCertsDirectory(); + CertificateList certs = CreateCertificateListFromFile( + certs_dir, test_data_.file_name, test_data_.format); + ASSERT_FALSE(certs.empty()); + ASSERT_LE(certs.size(), arraysize(test_data_.chain_fingerprints)); + CheckGoogleCert(certs.front(), google_parse_fingerprint, + kGoogleParseValidFrom, kGoogleParseValidTo); + + size_t i; + for (i = 0; i < arraysize(test_data_.chain_fingerprints) && + i < certs.size() && test_data_.chain_fingerprints[i] != NULL; ++i) { + const X509Certificate* cert = certs[i]; + const SHA1Fingerprint& actual_fingerprint = cert->fingerprint(); + unsigned char* expected_fingerprint = test_data_.chain_fingerprints[i]; + + for (size_t j = 0; j < 20; ++j) + EXPECT_EQ(expected_fingerprint[j], actual_fingerprint.data[j]); + } +} + +INSTANTIATE_TEST_CASE_P(, X509CertificateParseTest, + testing::ValuesIn(FormatTestData)); + } // namespace net diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc index 901c0a6..faa8871 100644 --- a/net/base/x509_certificate_win.cc +++ b/net/base/x509_certificate_win.cc @@ -434,6 +434,54 @@ void ParsePrincipal(const std::string& description, } } +void AddCertsFromStore(HCERTSTORE store, + X509Certificate::OSCertHandles* results) { + PCCERT_CONTEXT cert = NULL; + + while ((cert = CertEnumCertificatesInStore(store, cert)) != NULL) { + PCCERT_CONTEXT to_add = NULL; + if (CertAddCertificateContextToStore( + NULL, // The cert won't be persisted in any cert store. This breaks + // any association the context currently has to |store|, which + // allows us, the caller, to safely close |store| without + // releasing the cert handles. + cert, + CERT_STORE_ADD_USE_EXISTING, + &to_add) && to_add != NULL) { + // When processing stores generated from PKCS#7/PKCS#12 files, it + // appears that the order returned is the inverse of the order that it + // appeared in the file. + // TODO(rsleevi): Ensure this order is consistent across all Win + // versions + results->insert(results->begin(), to_add); + } + } +} + +X509Certificate::OSCertHandles ParsePKCS7(const char* data, size_t length) { + X509Certificate::OSCertHandles results; + CERT_BLOB data_blob; + data_blob.cbData = length; + data_blob.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(data)); + + HCERTSTORE out_store = NULL; + + DWORD expected_types = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED | + CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED; + + if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &data_blob, expected_types, + CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, + &out_store, NULL, NULL) || out_store == NULL) { + return results; + } + + AddCertsFromStore(out_store, &results); + CertCloseStore(out_store, CERT_CLOSE_STORE_CHECK_FLAG); + + return results; +} + } // namespace void X509Certificate::Initialize() { @@ -753,6 +801,27 @@ X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( return cert_handle; } +X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( + const char* data, int length, Format format) { + OSCertHandles results; + switch (format) { + case FORMAT_DER: { + OSCertHandle handle = CreateOSCertHandleFromBytes(data, length); + if (handle != NULL) + results.push_back(handle); + break; + } + case FORMAT_PKCS7: + results = ParsePKCS7(data, length); + break; + default: + NOTREACHED() << "Certificate format " << format << " unimplemented"; + break; + } + + return results; +} + // static X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( diff --git a/net/data/ssl/certificates/google.binary.p7b b/net/data/ssl/certificates/google.binary.p7b Binary files differnew file mode 100644 index 0000000..052e388 --- /dev/null +++ b/net/data/ssl/certificates/google.binary.p7b diff --git a/net/data/ssl/certificates/google.chain.pem b/net/data/ssl/certificates/google.chain.pem new file mode 100644 index 0000000..e78af71 --- /dev/null +++ b/net/data/ssl/certificates/google.chain.pem @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM +MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg +THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x +MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh +MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw +FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC +gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN +gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L +05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM +BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl +LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF +BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw +Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0 +ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF +AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5 +u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6 +z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDIzCCAoygAwIBAgIEMAAAAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVi +bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNTEzMDAw +MDAwWhcNMTQwNTEyMjM1OTU5WjBMMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhh +d3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBD +QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1NNn0I0Vf67NMf59HZGhPwtx +PKzMyGT7Y/wySweUvW+Aui/hBJPAM/wJMyPpC3QrccQDxtLN4i/1CWPN/0ilAL/g +5/OIty0y3pg25gqtAHvEZEo7hHUD8nCSfQ5i9SGraTaEMXWQ+L/HbIgbBpV8yeWo +3nWhLHpo39XKHIdYYBkCAwEAAaOB/jCB+zASBgNVHRMBAf8ECDAGAQH/AgEAMAsG +A1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAX +BgNVBAMTEFByaXZhdGVMYWJlbDMtMTUwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDov +L2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwMgYIKwYBBQUHAQEEJjAkMCIGCCsG +AQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMDQGA1UdJQQtMCsGCCsGAQUF +BwMBBggrBgEFBQcDAgYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEB +BQUAA4GBAFWsY+reod3SkF+fC852vhNRj5PZBSvIG3dLrWlQoe7e3P3bB+noOZTc +q3J5Lwa/q4FwxKjt6lM07e8eU9kGx1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTR +bcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXWrOU/VG+WHgWv +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/net/data/ssl/certificates/google.pem_cert.p7b b/net/data/ssl/certificates/google.pem_cert.p7b new file mode 100644 index 0000000..ba80fb0 --- /dev/null +++ b/net/data/ssl/certificates/google.pem_cert.p7b @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIGeQYJKoZIhvcNAQcCoIIGajCCBmYCAQExADALBgkqhkiG9w0BBwGgggZMMIID +ITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQsw +CQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRk +LjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEy +MTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw +FAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcwFQYD +VQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA +6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj +9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vu +uWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNV +HRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNv +bS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMC +BglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRwOi8v +b2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j +b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOB +gQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ON +gJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nR +UP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXzCCAyMwggKMoAMCAQIC +BDAAAAIwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZl +cmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDUxMzAwMDAwMFoXDTE0MDUxMjIz +NTk1OVowTDELMAkGA1UEBhMCWkExJTAjBgNVBAoTHFRoYXd0ZSBDb25zdWx0aW5n +IChQdHkpIEx0ZC4xFjAUBgNVBAMTDVRoYXd0ZSBTR0MgQ0EwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBANTTZ9CNFX+uzTH+fR2RoT8LcTyszMhk+2P8MksHlL1v +gLov4QSTwDP8CTMj6Qt0K3HEA8bSzeIv9Qljzf9IpQC/4OfziLctMt6YNuYKrQB7 +xGRKO4R1A/Jwkn0OYvUhq2k2hDF1kPi/x2yIGwaVfMnlqN51oSx6aN/VyhyHWGAZ +AgMBAAGjgf4wgfswEgYDVR0TAQH/BAgwBgEB/wIBADALBgNVHQ8EBAMCAQYwEQYJ +YIZIAYb4QgEBBAQDAgEGMCgGA1UdEQQhMB+kHTAbMRkwFwYDVQQDExBQcml2YXRl +TGFiZWwzLTE1MDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24u +Y29tL3BjYTMuY3JsMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDov +L29jc3AudGhhd3RlLmNvbTA0BgNVHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIG +CWCGSAGG+EIEAQYKYIZIAYb4RQEIATANBgkqhkiG9w0BAQUFAAOBgQBVrGPq3qHd +0pBfnwvOdr4TUY+T2QUryBt3S61pUKHu3tz92wfp6DmU3KtyeS8Gv6uBcMSo7epT +NO3vHlPZBsdWK9Fc9NGKjrQrsTeQSAhCJcU+ist/628E0W3FdKL3onx7YDx3zQ7O +SAJ/AS+2mzfgKio23NWF1qzlP1Rvlh4Fr6EAMQA= +-----END CERTIFICATE----- diff --git a/net/data/ssl/certificates/google.pem_pkcs7.p7b b/net/data/ssl/certificates/google.pem_pkcs7.p7b new file mode 100644 index 0000000..49e2eec --- /dev/null +++ b/net/data/ssl/certificates/google.pem_pkcs7.p7b @@ -0,0 +1,37 @@ +-----BEGIN PKCS7----- +MIIGeQYJKoZIhvcNAQcCoIIGajCCBmYCAQExADALBgkqhkiG9w0BBwGgggZMMIID +ITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBMMQsw +CQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkgTHRk +LjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0xMTEy +MTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw +FAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcwFQYD +VQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA +6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jNgtXj +9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L05vu +uWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAMBgNV +HRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3RlLmNv +bS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMC +BglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRwOi8v +b2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0ZS5j +b20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUFAAOB +gQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5u2ON +gJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6z5nR +UP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXzCCAyMwggKMoAMCAQIC +BDAAAAIwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZl +cmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDUxMzAwMDAwMFoXDTE0MDUxMjIz +NTk1OVowTDELMAkGA1UEBhMCWkExJTAjBgNVBAoTHFRoYXd0ZSBDb25zdWx0aW5n +IChQdHkpIEx0ZC4xFjAUBgNVBAMTDVRoYXd0ZSBTR0MgQ0EwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBANTTZ9CNFX+uzTH+fR2RoT8LcTyszMhk+2P8MksHlL1v +gLov4QSTwDP8CTMj6Qt0K3HEA8bSzeIv9Qljzf9IpQC/4OfziLctMt6YNuYKrQB7 +xGRKO4R1A/Jwkn0OYvUhq2k2hDF1kPi/x2yIGwaVfMnlqN51oSx6aN/VyhyHWGAZ +AgMBAAGjgf4wgfswEgYDVR0TAQH/BAgwBgEB/wIBADALBgNVHQ8EBAMCAQYwEQYJ +YIZIAYb4QgEBBAQDAgEGMCgGA1UdEQQhMB+kHTAbMRkwFwYDVQQDExBQcml2YXRl +TGFiZWwzLTE1MDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24u +Y29tL3BjYTMuY3JsMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDov +L29jc3AudGhhd3RlLmNvbTA0BgNVHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIG +CWCGSAGG+EIEAQYKYIZIAYb4RQEIATANBgkqhkiG9w0BAQUFAAOBgQBVrGPq3qHd +0pBfnwvOdr4TUY+T2QUryBt3S61pUKHu3tz92wfp6DmU3KtyeS8Gv6uBcMSo7epT +NO3vHlPZBsdWK9Fc9NGKjrQrsTeQSAhCJcU+ist/628E0W3FdKL3onx7YDx3zQ7O +SAJ/AS+2mzfgKio23NWF1qzlP1Rvlh4Fr6EAMQA= +-----END PKCS7----- diff --git a/net/data/ssl/certificates/google.single.der b/net/data/ssl/certificates/google.single.der Binary files differnew file mode 100644 index 0000000..f73df17 --- /dev/null +++ b/net/data/ssl/certificates/google.single.der diff --git a/net/data/ssl/certificates/google.single.pem b/net/data/ssl/certificates/google.single.pem new file mode 100644 index 0000000..a03adc4 --- /dev/null +++ b/net/data/ssl/certificates/google.single.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM +MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg +THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x +MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh +MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw +FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC +gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN +gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L +05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM +BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl +LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF +BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw +Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0 +ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF +AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5 +u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6 +z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw== +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/net/data/ssl/certificates/thawte.single.pem b/net/data/ssl/certificates/thawte.single.pem new file mode 100644 index 0000000..d326459 --- /dev/null +++ b/net/data/ssl/certificates/thawte.single.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDIzCCAoygAwIBAgIEMAAAAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVi +bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNTEzMDAw +MDAwWhcNMTQwNTEyMjM1OTU5WjBMMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhh +d3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBD +QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1NNn0I0Vf67NMf59HZGhPwtx +PKzMyGT7Y/wySweUvW+Aui/hBJPAM/wJMyPpC3QrccQDxtLN4i/1CWPN/0ilAL/g +5/OIty0y3pg25gqtAHvEZEo7hHUD8nCSfQ5i9SGraTaEMXWQ+L/HbIgbBpV8yeWo +3nWhLHpo39XKHIdYYBkCAwEAAaOB/jCB+zASBgNVHRMBAf8ECDAGAQH/AgEAMAsG +A1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAX +BgNVBAMTEFByaXZhdGVMYWJlbDMtMTUwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDov +L2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwMgYIKwYBBQUHAQEEJjAkMCIGCCsG +AQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMDQGA1UdJQQtMCsGCCsGAQUF +BwMBBggrBgEFBQcDAgYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEB +BQUAA4GBAFWsY+reod3SkF+fC852vhNRj5PZBSvIG3dLrWlQoe7e3P3bB+noOZTc +q3J5Lwa/q4FwxKjt6lM07e8eU9kGx1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTR +bcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXWrOU/VG+WHgWv +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/net/net.gyp b/net/net.gyp index 98dc526..ea740ff 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -125,6 +125,8 @@ 'base/network_change_notifier_win.h', 'base/nss_memio.c', 'base/nss_memio.h', + 'base/pem_tokenizer.cc', + 'base/pem_tokenizer.h', 'base/platform_mime_util.h', # TODO(tc): gnome-vfs? xdgmime? /etc/mime.types? 'base/platform_mime_util_linux.cc', @@ -674,6 +676,7 @@ 'base/net_test_constants.h', 'base/net_test_suite.h', 'base/net_util_unittest.cc', + 'base/pem_tokenizer_unittest.cc', 'base/registry_controlled_domain_unittest.cc', 'base/run_all_unittests.cc', 'base/sdch_filter_unittest.cc', |