Home | History | Annotate | Download | only in net
      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 "chrome/common/net/x509_certificate_model.h"
      6 
      7 #include <cert.h>
      8 #include <cms.h>
      9 #include <hasht.h>
     10 #include <keyhi.h>  // SECKEY_DestroyPrivateKey
     11 #include <keythi.h>  // SECKEYPrivateKey
     12 #include <pk11pub.h>  // PK11_FindKeyByAnyCert
     13 #include <seccomon.h>  // SECItem
     14 #include <sechash.h>
     15 
     16 #include "base/logging.h"
     17 #include "base/strings/string_number_conversions.h"
     18 #include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h"
     19 #include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h"
     20 #include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h"
     21 #include "crypto/nss_util.h"
     22 #include "crypto/scoped_nss_types.h"
     23 #include "net/cert/x509_certificate.h"
     24 
     25 namespace psm = mozilla_security_manager;
     26 
     27 namespace {
     28 
     29 // Convert a char* return value from NSS into a std::string and free the NSS
     30 // memory.  If the arg is NULL, an empty string will be returned instead.
     31 std::string Stringize(char* nss_text, const std::string& alternative_text) {
     32   if (!nss_text)
     33     return alternative_text;
     34 
     35   std::string s = nss_text;
     36   PORT_Free(nss_text);
     37   return s;
     38 }
     39 
     40 // Hash a certificate using the given algorithm, return the result as a
     41 // colon-seperated hex string.  The len specified is the number of bytes
     42 // required for storing the raw fingerprint.
     43 // (It's a bit redundant that the caller needs to specify len in addition to the
     44 // algorithm, but given the limited uses, not worth fixing.)
     45 std::string HashCert(CERTCertificate* cert, HASH_HashType algorithm, int len) {
     46   unsigned char fingerprint[HASH_LENGTH_MAX];
     47 
     48   DCHECK(NULL != cert->derCert.data);
     49   DCHECK_NE(0U, cert->derCert.len);
     50   DCHECK_LE(len, HASH_LENGTH_MAX);
     51   memset(fingerprint, 0, len);
     52   SECStatus rv = HASH_HashBuf(algorithm, fingerprint, cert->derCert.data,
     53                               cert->derCert.len);
     54   DCHECK_EQ(rv, SECSuccess);
     55   return x509_certificate_model::ProcessRawBytes(fingerprint, len);
     56 }
     57 
     58 std::string ProcessSecAlgorithmInternal(SECAlgorithmID* algorithm_id) {
     59   return psm::GetOIDText(&algorithm_id->algorithm);
     60 }
     61 
     62 std::string ProcessExtension(
     63     const std::string& critical_label,
     64     const std::string& non_critical_label,
     65     CERTCertExtension* extension) {
     66   std::string criticality =
     67       extension->critical.data && extension->critical.data[0] ?
     68           critical_label : non_critical_label;
     69   return criticality + "\n" +
     70       psm::ProcessExtensionData(SECOID_FindOIDTag(&extension->id),
     71                                 &extension->value);
     72 }
     73 
     74 ////////////////////////////////////////////////////////////////////////////////
     75 // NSS certificate export functions.
     76 
     77 class FreeNSSCMSMessage {
     78  public:
     79   inline void operator()(NSSCMSMessage* x) const {
     80     NSS_CMSMessage_Destroy(x);
     81   }
     82 };
     83 typedef scoped_ptr_malloc<NSSCMSMessage, FreeNSSCMSMessage>
     84     ScopedNSSCMSMessage;
     85 
     86 class FreeNSSCMSSignedData {
     87  public:
     88   inline void operator()(NSSCMSSignedData* x) const {
     89     NSS_CMSSignedData_Destroy(x);
     90   }
     91 };
     92 typedef scoped_ptr_malloc<NSSCMSSignedData, FreeNSSCMSSignedData>
     93     ScopedNSSCMSSignedData;
     94 
     95 }  // namespace
     96 
     97 namespace x509_certificate_model {
     98 
     99 using net::X509Certificate;
    100 using std::string;
    101 
    102 string GetCertNameOrNickname(X509Certificate::OSCertHandle cert_handle) {
    103   string name = ProcessIDN(
    104       Stringize(CERT_GetCommonName(&cert_handle->subject), std::string()));
    105   if (!name.empty())
    106     return name;
    107   return GetNickname(cert_handle);
    108 }
    109 
    110 string GetNickname(X509Certificate::OSCertHandle cert_handle) {
    111   string name;
    112   if (cert_handle->nickname) {
    113     name = cert_handle->nickname;
    114     // Hack copied from mozilla: Cut off text before first :, which seems to
    115     // just be the token name.
    116     size_t colon_pos = name.find(':');
    117     if (colon_pos != string::npos)
    118       name = name.substr(colon_pos + 1);
    119   }
    120   return name;
    121 }
    122 
    123 string GetTokenName(X509Certificate::OSCertHandle cert_handle) {
    124   return psm::GetCertTokenName(cert_handle);
    125 }
    126 
    127 string GetVersion(X509Certificate::OSCertHandle cert_handle) {
    128   // If the version field is omitted from the certificate, the default
    129   // value is v1(0).
    130   unsigned long version = 0;
    131   if (cert_handle->version.len == 0 ||
    132       SEC_ASN1DecodeInteger(&cert_handle->version, &version) == SECSuccess) {
    133     return base::UintToString(version + 1);
    134   }
    135   return std::string();
    136 }
    137 
    138 net::CertType GetType(X509Certificate::OSCertHandle cert_handle) {
    139     return psm::GetCertType(cert_handle);
    140 }
    141 
    142 string GetEmailAddress(X509Certificate::OSCertHandle cert_handle) {
    143   if (cert_handle->emailAddr)
    144     return cert_handle->emailAddr;
    145   return std::string();
    146 }
    147 
    148 void GetUsageStrings(X509Certificate::OSCertHandle cert_handle,
    149                      std::vector<string>* usages) {
    150   psm::GetCertUsageStrings(cert_handle, usages);
    151 }
    152 
    153 string GetKeyUsageString(X509Certificate::OSCertHandle cert_handle) {
    154   SECItem key_usage;
    155   key_usage.data = NULL;
    156   string key_usage_str;
    157   if (CERT_FindKeyUsageExtension(cert_handle, &key_usage) == SECSuccess) {
    158     key_usage_str = psm::ProcessKeyUsageBitString(&key_usage, ',');
    159     PORT_Free(key_usage.data);
    160   }
    161   return key_usage_str;
    162 }
    163 
    164 string GetSerialNumberHexified(X509Certificate::OSCertHandle cert_handle,
    165                                const string& alternative_text) {
    166   return Stringize(CERT_Hexify(&cert_handle->serialNumber, true),
    167                    alternative_text);
    168 }
    169 
    170 string GetIssuerCommonName(X509Certificate::OSCertHandle cert_handle,
    171                            const string& alternative_text) {
    172   return Stringize(CERT_GetCommonName(&cert_handle->issuer), alternative_text);
    173 }
    174 
    175 string GetIssuerOrgName(X509Certificate::OSCertHandle cert_handle,
    176                         const string& alternative_text) {
    177   return Stringize(CERT_GetOrgName(&cert_handle->issuer), alternative_text);
    178 }
    179 
    180 string GetIssuerOrgUnitName(X509Certificate::OSCertHandle cert_handle,
    181                             const string& alternative_text) {
    182   return Stringize(CERT_GetOrgUnitName(&cert_handle->issuer), alternative_text);
    183 }
    184 
    185 string GetSubjectOrgName(X509Certificate::OSCertHandle cert_handle,
    186                          const string& alternative_text) {
    187   return Stringize(CERT_GetOrgName(&cert_handle->subject), alternative_text);
    188 }
    189 
    190 string GetSubjectOrgUnitName(X509Certificate::OSCertHandle cert_handle,
    191                              const string& alternative_text) {
    192   return Stringize(CERT_GetOrgUnitName(&cert_handle->subject),
    193                    alternative_text);
    194 }
    195 
    196 string GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle,
    197                             const string& alternative_text) {
    198   return Stringize(CERT_GetCommonName(&cert_handle->subject), alternative_text);
    199 }
    200 
    201 bool GetTimes(X509Certificate::OSCertHandle cert_handle,
    202               base::Time* issued, base::Time* expires) {
    203   PRTime pr_issued, pr_expires;
    204   if (CERT_GetCertTimes(cert_handle, &pr_issued, &pr_expires) == SECSuccess) {
    205     *issued = crypto::PRTimeToBaseTime(pr_issued);
    206     *expires = crypto::PRTimeToBaseTime(pr_expires);
    207     return true;
    208   }
    209   return false;
    210 }
    211 
    212 string GetTitle(X509Certificate::OSCertHandle cert_handle) {
    213   return psm::GetCertTitle(cert_handle);
    214 }
    215 
    216 string GetIssuerName(X509Certificate::OSCertHandle cert_handle) {
    217   return psm::ProcessName(&cert_handle->issuer);
    218 }
    219 
    220 string GetSubjectName(X509Certificate::OSCertHandle cert_handle) {
    221   return psm::ProcessName(&cert_handle->subject);
    222 }
    223 
    224 void GetEmailAddresses(X509Certificate::OSCertHandle cert_handle,
    225                        std::vector<string>* email_addresses) {
    226   for (const char* addr = CERT_GetFirstEmailAddress(cert_handle);
    227        addr; addr = CERT_GetNextEmailAddress(cert_handle, addr)) {
    228     // The first email addr (from Subject) may be duplicated in Subject
    229     // Alternative Name, so check subsequent addresses are not equal to the
    230     // first one before adding to the list.
    231     if (!email_addresses->size() || (*email_addresses)[0] != addr)
    232       email_addresses->push_back(addr);
    233   }
    234 }
    235 
    236 void GetNicknameStringsFromCertList(
    237     const std::vector<scoped_refptr<X509Certificate> >& certs,
    238     const string& cert_expired,
    239     const string& cert_not_yet_valid,
    240     std::vector<string>* nick_names) {
    241   CERTCertList* cert_list = CERT_NewCertList();
    242   for (size_t i = 0; i < certs.size(); ++i) {
    243     CERT_AddCertToListTail(
    244         cert_list,
    245         CERT_DupCertificate(certs[i]->os_cert_handle()));
    246   }
    247   // Would like to use CERT_GetCertNicknameWithValidity on each cert
    248   // individually instead of having to build a CERTCertList for this, but that
    249   // function is not exported.
    250   CERTCertNicknames* cert_nicknames = CERT_NicknameStringsFromCertList(
    251       cert_list,
    252       const_cast<char*>(cert_expired.c_str()),
    253       const_cast<char*>(cert_not_yet_valid.c_str()));
    254   DCHECK_EQ(cert_nicknames->numnicknames,
    255             static_cast<int>(certs.size()));
    256 
    257   for (int i = 0; i < cert_nicknames->numnicknames; ++i)
    258     nick_names->push_back(cert_nicknames->nicknames[i]);
    259 
    260   CERT_FreeNicknames(cert_nicknames);
    261   CERT_DestroyCertList(cert_list);
    262 }
    263 
    264 // For background see this discussion on dev-tech-crypto.lists.mozilla.org:
    265 // http://web.archiveorange.com/archive/v/6JJW7E40sypfZGtbkzxX
    266 //
    267 // NOTE: This function relies on the convention that the same PKCS#11 ID
    268 // is shared between a certificate and its associated private and public
    269 // keys.  I tried to implement this with PK11_GetLowLevelKeyIDForCert(),
    270 // but that always returns NULL on Chrome OS for me.
    271 std::string GetPkcs11Id(net::X509Certificate::OSCertHandle cert_handle) {
    272   std::string pkcs11_id;
    273   SECKEYPrivateKey *priv_key = PK11_FindKeyByAnyCert(cert_handle,
    274                                                      NULL /* wincx */);
    275   if (priv_key) {
    276     // Get the CKA_ID attribute for a key.
    277     SECItem* sec_item = PK11_GetLowLevelKeyIDForPrivateKey(priv_key);
    278     if (sec_item) {
    279       pkcs11_id = base::HexEncode(sec_item->data, sec_item->len);
    280       SECITEM_FreeItem(sec_item, PR_TRUE);
    281     }
    282     SECKEY_DestroyPrivateKey(priv_key);
    283   }
    284   return pkcs11_id;
    285 }
    286 
    287 void GetExtensions(
    288     const string& critical_label,
    289     const string& non_critical_label,
    290     X509Certificate::OSCertHandle cert_handle,
    291     Extensions* extensions) {
    292   if (cert_handle->extensions) {
    293     for (size_t i = 0; cert_handle->extensions[i] != NULL; ++i) {
    294       Extension extension;
    295       extension.name = psm::GetOIDText(&cert_handle->extensions[i]->id);
    296       extension.value = ProcessExtension(
    297           critical_label, non_critical_label, cert_handle->extensions[i]);
    298       extensions->push_back(extension);
    299     }
    300   }
    301 }
    302 
    303 string HashCertSHA256(X509Certificate::OSCertHandle cert_handle) {
    304   return HashCert(cert_handle, HASH_AlgSHA256, SHA256_LENGTH);
    305 }
    306 
    307 string HashCertSHA1(X509Certificate::OSCertHandle cert_handle) {
    308   return HashCert(cert_handle, HASH_AlgSHA1, SHA1_LENGTH);
    309 }
    310 
    311 void GetCertChainFromCert(X509Certificate::OSCertHandle cert_handle,
    312                           X509Certificate::OSCertHandles* cert_handles) {
    313   CERTCertList* cert_list =
    314       CERT_GetCertChainFromCert(cert_handle, PR_Now(), certUsageSSLServer);
    315   CERTCertListNode* node;
    316   for (node = CERT_LIST_HEAD(cert_list);
    317        !CERT_LIST_END(node, cert_list);
    318        node = CERT_LIST_NEXT(node)) {
    319     cert_handles->push_back(CERT_DupCertificate(node->cert));
    320   }
    321   CERT_DestroyCertList(cert_list);
    322 }
    323 
    324 void DestroyCertChain(X509Certificate::OSCertHandles* cert_handles) {
    325   for (X509Certificate::OSCertHandles::iterator i(cert_handles->begin());
    326        i != cert_handles->end(); ++i)
    327     CERT_DestroyCertificate(*i);
    328   cert_handles->clear();
    329 }
    330 
    331 string GetDerString(X509Certificate::OSCertHandle cert_handle) {
    332   return string(reinterpret_cast<const char*>(cert_handle->derCert.data),
    333                 cert_handle->derCert.len);
    334 }
    335 
    336 string GetCMSString(const X509Certificate::OSCertHandles& cert_chain,
    337                     size_t start, size_t end) {
    338   crypto::ScopedPLArenaPool arena(PORT_NewArena(1024));
    339   DCHECK(arena.get());
    340 
    341   ScopedNSSCMSMessage message(NSS_CMSMessage_Create(arena.get()));
    342   DCHECK(message.get());
    343 
    344   // First, create SignedData with the certificate only (no chain).
    345   ScopedNSSCMSSignedData signed_data(NSS_CMSSignedData_CreateCertsOnly(
    346       message.get(), cert_chain[start], PR_FALSE));
    347   if (!signed_data.get()) {
    348     DLOG(ERROR) << "NSS_CMSSignedData_Create failed";
    349     return std::string();
    350   }
    351   // Add the rest of the chain (if any).
    352   for (size_t i = start + 1; i < end; ++i) {
    353     if (NSS_CMSSignedData_AddCertificate(signed_data.get(), cert_chain[i]) !=
    354         SECSuccess) {
    355       DLOG(ERROR) << "NSS_CMSSignedData_AddCertificate failed on " << i;
    356       return std::string();
    357     }
    358   }
    359 
    360   NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(message.get());
    361   if (NSS_CMSContentInfo_SetContent_SignedData(
    362       message.get(), cinfo, signed_data.get()) == SECSuccess) {
    363     ignore_result(signed_data.release());
    364   } else {
    365     DLOG(ERROR) << "NSS_CMSMessage_GetContentInfo failed";
    366     return std::string();
    367   }
    368 
    369   SECItem cert_p7 = { siBuffer, NULL, 0 };
    370   NSSCMSEncoderContext *ecx = NSS_CMSEncoder_Start(message.get(), NULL, NULL,
    371                                                    &cert_p7, arena.get(), NULL,
    372                                                    NULL, NULL, NULL, NULL,
    373                                                    NULL);
    374   if (!ecx) {
    375     DLOG(ERROR) << "NSS_CMSEncoder_Start failed";
    376     return std::string();
    377   }
    378 
    379   if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
    380     DLOG(ERROR) << "NSS_CMSEncoder_Finish failed";
    381     return std::string();
    382   }
    383 
    384   return string(reinterpret_cast<const char*>(cert_p7.data), cert_p7.len);
    385 }
    386 
    387 string ProcessSecAlgorithmSignature(X509Certificate::OSCertHandle cert_handle) {
    388   return ProcessSecAlgorithmInternal(&cert_handle->signature);
    389 }
    390 
    391 string ProcessSecAlgorithmSubjectPublicKey(
    392     X509Certificate::OSCertHandle cert_handle) {
    393   return ProcessSecAlgorithmInternal(
    394       &cert_handle->subjectPublicKeyInfo.algorithm);
    395 }
    396 
    397 string ProcessSecAlgorithmSignatureWrap(
    398     X509Certificate::OSCertHandle cert_handle) {
    399   return ProcessSecAlgorithmInternal(
    400       &cert_handle->signatureWrap.signatureAlgorithm);
    401 }
    402 
    403 string ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle) {
    404   return psm::ProcessSubjectPublicKeyInfo(&cert_handle->subjectPublicKeyInfo);
    405 }
    406 
    407 string ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle) {
    408   return ProcessRawBits(cert_handle->signatureWrap.signature.data,
    409                         cert_handle->signatureWrap.signature.len);
    410 }
    411 
    412 void RegisterDynamicOids() {
    413   psm::RegisterDynamicOids();
    414 }
    415 
    416 }  // namespace x509_certificate_model
    417