Home | History | Annotate | Download | only in cert
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/cert/x509_util.h"
      6 #include "net/cert/x509_util_nss.h"
      7 
      8 #include <cert.h>
      9 #include <secoid.h>
     10 
     11 #include "base/memory/ref_counted.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "crypto/ec_private_key.h"
     14 #include "crypto/scoped_nss_types.h"
     15 #include "crypto/signature_verifier.h"
     16 #include "net/cert/x509_certificate.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 namespace net {
     20 
     21 namespace {
     22 
     23 CERTCertificate* CreateNSSCertHandleFromBytes(const char* data, size_t length) {
     24   SECItem der_cert;
     25   der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
     26   der_cert.len  = length;
     27   der_cert.type = siDERCertBuffer;
     28 
     29   // Parse into a certificate structure.
     30   return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert, NULL,
     31                                  PR_FALSE, PR_TRUE);
     32 }
     33 
     34 #if !defined(OS_WIN) && !defined(OS_MACOSX)
     35 void VerifyCertificateSignature(const std::string& der_cert,
     36                                 const std::vector<uint8>& der_spki) {
     37   crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
     38 
     39   CERTSignedData sd;
     40   memset(&sd, 0, sizeof(sd));
     41 
     42   SECItem der_cert_item = {
     43     siDERCertBuffer,
     44     reinterpret_cast<unsigned char*>(const_cast<char*>(der_cert.data())),
     45     static_cast<unsigned int>(der_cert.size())
     46   };
     47   SECStatus rv = SEC_ASN1DecodeItem(arena.get(), &sd,
     48                                     SEC_ASN1_GET(CERT_SignedDataTemplate),
     49                                     &der_cert_item);
     50   ASSERT_EQ(SECSuccess, rv);
     51 
     52   // The CERTSignedData.signatureAlgorithm is decoded, but SignatureVerifier
     53   // wants the DER encoded form, so re-encode it again.
     54   SECItem* signature_algorithm = SEC_ASN1EncodeItem(
     55       arena.get(),
     56       NULL,
     57       &sd.signatureAlgorithm,
     58       SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
     59   ASSERT_TRUE(signature_algorithm);
     60 
     61   crypto::SignatureVerifier verifier;
     62   bool ok = verifier.VerifyInit(
     63       signature_algorithm->data,
     64       signature_algorithm->len,
     65       sd.signature.data,
     66       sd.signature.len / 8,  // Signature is a BIT STRING, convert to bytes.
     67       &der_spki[0],
     68       der_spki.size());
     69 
     70   ASSERT_TRUE(ok);
     71   verifier.VerifyUpdate(sd.data.data,
     72                         sd.data.len);
     73 
     74   ok = verifier.VerifyFinal();
     75   EXPECT_TRUE(ok);
     76 }
     77 #endif  // !defined(OS_WIN) && !defined(OS_MACOSX)
     78 
     79 void VerifyDomainBoundCert(const std::string& domain,
     80                            const std::string& der_cert) {
     81   // Origin Bound Cert OID.
     82   static const char oid_string[] = "1.3.6.1.4.1.11129.2.1.6";
     83 
     84   // Create object neccessary for extension lookup call.
     85   SECItem extension_object = {
     86     siAsciiString,
     87     (unsigned char*)domain.data(),
     88     static_cast<unsigned int>(domain.size())
     89   };
     90 
     91   // IA5Encode and arena allocate SECItem.
     92   PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     93   SECItem* expected = SEC_ASN1EncodeItem(arena,
     94                                          NULL,
     95                                          &extension_object,
     96                                          SEC_ASN1_GET(SEC_IA5StringTemplate));
     97 
     98   ASSERT_NE(static_cast<SECItem*>(NULL), expected);
     99 
    100   // Create OID SECItem.
    101   SECItem ob_cert_oid = { siDEROID, NULL, 0 };
    102   SECStatus ok = SEC_StringToOID(arena, &ob_cert_oid,
    103                                  oid_string, 0);
    104 
    105   ASSERT_EQ(SECSuccess, ok);
    106 
    107   SECOidTag ob_cert_oid_tag = SECOID_FindOIDTag(&ob_cert_oid);
    108 
    109   ASSERT_NE(SEC_OID_UNKNOWN, ob_cert_oid_tag);
    110 
    111   // This test is run on Mac and Win where X509Certificate::os_cert_handle isn't
    112   // an NSS type, so we have to manually create a NSS certificate object so we
    113   // can use CERT_FindCertExtension.  We also check the subject and validity
    114   // times using NSS since X509Certificate will fail with EC certs on OSX 10.5
    115   // (http://crbug.com/101231).
    116   CERTCertificate* nss_cert = CreateNSSCertHandleFromBytes(
    117       der_cert.data(), der_cert.size());
    118 
    119   char* common_name = CERT_GetCommonName(&nss_cert->subject);
    120   ASSERT_TRUE(common_name);
    121   EXPECT_STREQ("anonymous.invalid", common_name);
    122   PORT_Free(common_name);
    123   EXPECT_EQ(SECSuccess, CERT_CertTimesValid(nss_cert));
    124 
    125   // Lookup Origin Bound Cert extension in generated cert.
    126   SECItem actual = { siBuffer, NULL, 0 };
    127   ok = CERT_FindCertExtension(nss_cert,
    128                               ob_cert_oid_tag,
    129                               &actual);
    130   CERT_DestroyCertificate(nss_cert);
    131   ASSERT_EQ(SECSuccess, ok);
    132 
    133   // Compare expected and actual extension values.
    134   PRBool result = SECITEM_ItemsAreEqual(expected, &actual);
    135   ASSERT_TRUE(result);
    136 
    137   // Do Cleanup.
    138   SECITEM_FreeItem(&actual, PR_FALSE);
    139   PORT_FreeArena(arena, PR_FALSE);
    140 }
    141 
    142 }  // namespace
    143 
    144 // This test creates a domain-bound cert from an EC private key and
    145 // then verifies the content of the certificate.
    146 TEST(X509UtilNSSTest, CreateDomainBoundCertEC) {
    147   // Create a sample ASCII weborigin.
    148   std::string domain = "weborigin.com";
    149   base::Time now = base::Time::Now();
    150 
    151   scoped_ptr<crypto::ECPrivateKey> private_key(
    152       crypto::ECPrivateKey::Create());
    153   std::string der_cert;
    154   ASSERT_TRUE(x509_util::CreateDomainBoundCertEC(
    155       private_key.get(),
    156       domain, 1,
    157       now,
    158       now + base::TimeDelta::FromDays(1),
    159       &der_cert));
    160 
    161   VerifyDomainBoundCert(domain, der_cert);
    162 
    163 #if !defined(OS_WIN) && !defined(OS_MACOSX)
    164   // signature_verifier_win and signature_verifier_mac can't handle EC certs.
    165   std::vector<uint8> spki;
    166   ASSERT_TRUE(private_key->ExportPublicKey(&spki));
    167   VerifyCertificateSignature(der_cert, spki);
    168 #endif
    169 }
    170 
    171 }  // namespace net
    172