• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_certificate.h"
6 
7 #include <blapi.h>  // Implement CalculateChainFingerprint() with NSS.
8 
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/pickle.h"
12 #include "base/sha1.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "crypto/capi_util.h"
16 #include "crypto/scoped_capi_types.h"
17 #include "net/base/net_errors.h"
18 
19 #pragma comment(lib, "crypt32.lib")
20 
21 using base::Time;
22 
23 namespace net {
24 
25 namespace {
26 
27 typedef crypto::ScopedCAPIHandle<
28     HCERTSTORE,
29     crypto::CAPIDestroyerWithFlags<HCERTSTORE,
30                                    CertCloseStore, 0> > ScopedHCERTSTORE;
31 
ExplodedTimeToSystemTime(const base::Time::Exploded & exploded,SYSTEMTIME * system_time)32 void ExplodedTimeToSystemTime(const base::Time::Exploded& exploded,
33                               SYSTEMTIME* system_time) {
34   system_time->wYear = exploded.year;
35   system_time->wMonth = exploded.month;
36   system_time->wDayOfWeek = exploded.day_of_week;
37   system_time->wDay = exploded.day_of_month;
38   system_time->wHour = exploded.hour;
39   system_time->wMinute = exploded.minute;
40   system_time->wSecond = exploded.second;
41   system_time->wMilliseconds = exploded.millisecond;
42 }
43 
44 //-----------------------------------------------------------------------------
45 
46 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
47 // structure and stores it in *output.
GetCertSubjectAltName(PCCERT_CONTEXT cert,scoped_ptr<CERT_ALT_NAME_INFO,base::FreeDeleter> * output)48 void GetCertSubjectAltName(
49     PCCERT_CONTEXT cert,
50     scoped_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter>* output) {
51   PCERT_EXTENSION extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
52                                                 cert->pCertInfo->cExtension,
53                                                 cert->pCertInfo->rgExtension);
54   if (!extension)
55     return;
56 
57   CRYPT_DECODE_PARA decode_para;
58   decode_para.cbSize = sizeof(decode_para);
59   decode_para.pfnAlloc = crypto::CryptAlloc;
60   decode_para.pfnFree = crypto::CryptFree;
61   CERT_ALT_NAME_INFO* alt_name_info = NULL;
62   DWORD alt_name_info_size = 0;
63   BOOL rv;
64   rv = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
65                            szOID_SUBJECT_ALT_NAME2,
66                            extension->Value.pbData,
67                            extension->Value.cbData,
68                            CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
69                            &decode_para,
70                            &alt_name_info,
71                            &alt_name_info_size);
72   if (rv)
73     output->reset(alt_name_info);
74 }
75 
AddCertsFromStore(HCERTSTORE store,X509Certificate::OSCertHandles * results)76 void AddCertsFromStore(HCERTSTORE store,
77                        X509Certificate::OSCertHandles* results) {
78   PCCERT_CONTEXT cert = NULL;
79 
80   while ((cert = CertEnumCertificatesInStore(store, cert)) != NULL) {
81     PCCERT_CONTEXT to_add = NULL;
82     if (CertAddCertificateContextToStore(
83         NULL,  // The cert won't be persisted in any cert store. This breaks
84                // any association the context currently has to |store|, which
85                // allows us, the caller, to safely close |store| without
86                // releasing the cert handles.
87         cert,
88         CERT_STORE_ADD_USE_EXISTING,
89         &to_add) && to_add != NULL) {
90       // When processing stores generated from PKCS#7/PKCS#12 files, it
91       // appears that the order returned is the inverse of the order that it
92       // appeared in the file.
93       // TODO(rsleevi): Ensure this order is consistent across all Win
94       // versions
95       results->insert(results->begin(), to_add);
96     }
97   }
98 }
99 
ParsePKCS7(const char * data,size_t length)100 X509Certificate::OSCertHandles ParsePKCS7(const char* data, size_t length) {
101   X509Certificate::OSCertHandles results;
102   CERT_BLOB data_blob;
103   data_blob.cbData = length;
104   data_blob.pbData = reinterpret_cast<BYTE*>(const_cast<char*>(data));
105 
106   HCERTSTORE out_store = NULL;
107 
108   DWORD expected_types = CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
109                          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
110                          CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED;
111 
112   if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &data_blob, expected_types,
113                         CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL,
114                         &out_store, NULL, NULL) || out_store == NULL) {
115     return results;
116   }
117 
118   AddCertsFromStore(out_store, &results);
119   CertCloseStore(out_store, CERT_CLOSE_STORE_CHECK_FLAG);
120 
121   return results;
122 }
123 
124 // Given a CERT_NAME_BLOB, returns true if it appears in a given list,
125 // formatted as a vector of strings holding DER-encoded X.509
126 // DistinguishedName entries.
IsCertNameBlobInIssuerList(CERT_NAME_BLOB * name_blob,const std::vector<std::string> & issuer_names)127 bool IsCertNameBlobInIssuerList(
128     CERT_NAME_BLOB* name_blob,
129     const std::vector<std::string>& issuer_names) {
130   for (std::vector<std::string>::const_iterator it = issuer_names.begin();
131        it != issuer_names.end(); ++it) {
132     CERT_NAME_BLOB issuer_blob;
133     issuer_blob.pbData =
134         reinterpret_cast<BYTE*>(const_cast<char*>(it->data()));
135     issuer_blob.cbData = static_cast<DWORD>(it->length());
136 
137     BOOL rb = CertCompareCertificateName(
138         X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &issuer_blob, name_blob);
139     if (rb)
140       return true;
141   }
142   return false;
143 }
144 
145 }  // namespace
146 
Initialize()147 void X509Certificate::Initialize() {
148   DCHECK(cert_handle_);
149   subject_.ParseDistinguishedName(cert_handle_->pCertInfo->Subject.pbData,
150                                   cert_handle_->pCertInfo->Subject.cbData);
151   issuer_.ParseDistinguishedName(cert_handle_->pCertInfo->Issuer.pbData,
152                                  cert_handle_->pCertInfo->Issuer.cbData);
153 
154   valid_start_ = Time::FromFileTime(cert_handle_->pCertInfo->NotBefore);
155   valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter);
156 
157   fingerprint_ = CalculateFingerprint(cert_handle_);
158   ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
159 
160   const CRYPT_INTEGER_BLOB* serial = &cert_handle_->pCertInfo->SerialNumber;
161   scoped_ptr<uint8[]> serial_bytes(new uint8[serial->cbData]);
162   for (unsigned i = 0; i < serial->cbData; i++)
163     serial_bytes[i] = serial->pbData[serial->cbData - i - 1];
164   serial_number_ = std::string(
165       reinterpret_cast<char*>(serial_bytes.get()), serial->cbData);
166 }
167 
GetSubjectAltName(std::vector<std::string> * dns_names,std::vector<std::string> * ip_addrs) const168 void X509Certificate::GetSubjectAltName(
169     std::vector<std::string>* dns_names,
170     std::vector<std::string>* ip_addrs) const {
171   if (dns_names)
172     dns_names->clear();
173   if (ip_addrs)
174     ip_addrs->clear();
175 
176   if (!cert_handle_)
177     return;
178 
179   scoped_ptr<CERT_ALT_NAME_INFO, base::FreeDeleter> alt_name_info;
180   GetCertSubjectAltName(cert_handle_, &alt_name_info);
181   CERT_ALT_NAME_INFO* alt_name = alt_name_info.get();
182   if (alt_name) {
183     int num_entries = alt_name->cAltEntry;
184     for (int i = 0; i < num_entries; i++) {
185       // dNSName is an ASN.1 IA5String representing a string of ASCII
186       // characters, so we can use UTF16ToASCII here.
187       const CERT_ALT_NAME_ENTRY& entry = alt_name->rgAltEntry[i];
188 
189       if (dns_names && entry.dwAltNameChoice == CERT_ALT_NAME_DNS_NAME) {
190         dns_names->push_back(base::UTF16ToASCII(entry.pwszDNSName));
191       } else if (ip_addrs &&
192                  entry.dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
193         ip_addrs->push_back(std::string(
194             reinterpret_cast<const char*>(entry.IPAddress.pbData),
195             entry.IPAddress.cbData));
196       }
197     }
198   }
199 }
200 
CreateOSCertChainForCert() const201 PCCERT_CONTEXT X509Certificate::CreateOSCertChainForCert() const {
202   // Create an in-memory certificate store to hold this certificate and
203   // any intermediate certificates in |intermediate_ca_certs_|. The store
204   // will be referenced in the returned PCCERT_CONTEXT, and will not be freed
205   // until the PCCERT_CONTEXT is freed.
206   ScopedHCERTSTORE store(CertOpenStore(
207       CERT_STORE_PROV_MEMORY, 0, NULL,
208       CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL));
209   if (!store.get())
210     return NULL;
211 
212   // NOTE: This preserves all of the properties of |os_cert_handle()| except
213   // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two
214   // properties that hold access to already-opened private keys. If a handle
215   // has already been unlocked (eg: PIN prompt), then the first time that the
216   // identity is used for client auth, it may prompt the user again.
217   PCCERT_CONTEXT primary_cert;
218   BOOL ok = CertAddCertificateContextToStore(store.get(), os_cert_handle(),
219                                              CERT_STORE_ADD_ALWAYS,
220                                              &primary_cert);
221   if (!ok || !primary_cert)
222     return NULL;
223 
224   for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
225     CertAddCertificateContextToStore(store.get(), intermediate_ca_certs_[i],
226                                      CERT_STORE_ADD_ALWAYS, NULL);
227   }
228 
229   // Note: |store| is explicitly not released, as the call to CertCloseStore()
230   // when |store| goes out of scope will not actually free the store. Instead,
231   // the store will be freed when |primary_cert| is freed.
232   return primary_cert;
233 }
234 
235 // static
GetDEREncoded(X509Certificate::OSCertHandle cert_handle,std::string * encoded)236 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
237                                     std::string* encoded) {
238   if (!cert_handle || !cert_handle->pbCertEncoded ||
239       !cert_handle->cbCertEncoded) {
240     return false;
241   }
242   encoded->assign(reinterpret_cast<char*>(cert_handle->pbCertEncoded),
243                   cert_handle->cbCertEncoded);
244   return true;
245 }
246 
247 // static
IsSameOSCert(X509Certificate::OSCertHandle a,X509Certificate::OSCertHandle b)248 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
249                                    X509Certificate::OSCertHandle b) {
250   DCHECK(a && b);
251   if (a == b)
252     return true;
253   return a->cbCertEncoded == b->cbCertEncoded &&
254       memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0;
255 }
256 
257 // static
CreateOSCertHandleFromBytes(const char * data,int length)258 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
259     const char* data, int length) {
260   OSCertHandle cert_handle = NULL;
261   if (!CertAddEncodedCertificateToStore(
262       NULL, X509_ASN_ENCODING, reinterpret_cast<const BYTE*>(data),
263       length, CERT_STORE_ADD_USE_EXISTING, &cert_handle))
264     return NULL;
265 
266   return cert_handle;
267 }
268 
CreateOSCertHandlesFromBytes(const char * data,int length,Format format)269 X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
270     const char* data, int length, Format format) {
271   OSCertHandles results;
272   switch (format) {
273     case FORMAT_SINGLE_CERTIFICATE: {
274       OSCertHandle handle = CreateOSCertHandleFromBytes(data, length);
275       if (handle != NULL)
276         results.push_back(handle);
277       break;
278     }
279     case FORMAT_PKCS7:
280       results = ParsePKCS7(data, length);
281       break;
282     default:
283       NOTREACHED() << "Certificate format " << format << " unimplemented";
284       break;
285   }
286 
287   return results;
288 }
289 
290 // static
DupOSCertHandle(OSCertHandle cert_handle)291 X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
292     OSCertHandle cert_handle) {
293   return CertDuplicateCertificateContext(cert_handle);
294 }
295 
296 // static
FreeOSCertHandle(OSCertHandle cert_handle)297 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
298   CertFreeCertificateContext(cert_handle);
299 }
300 
301 // static
CalculateFingerprint(OSCertHandle cert)302 SHA1HashValue X509Certificate::CalculateFingerprint(
303     OSCertHandle cert) {
304   DCHECK(NULL != cert->pbCertEncoded);
305   DCHECK_NE(static_cast<DWORD>(0), cert->cbCertEncoded);
306 
307   BOOL rv;
308   SHA1HashValue sha1;
309   DWORD sha1_size = sizeof(sha1.data);
310   rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded,
311                             cert->cbCertEncoded, sha1.data, &sha1_size);
312   DCHECK(rv && sha1_size == sizeof(sha1.data));
313   if (!rv)
314     memset(sha1.data, 0, sizeof(sha1.data));
315   return sha1;
316 }
317 
318 // TODO(wtc): This function is implemented with NSS low-level hash
319 // functions to ensure it is fast.  Reimplement this function with
320 // CryptoAPI.  May need to cache the HCRYPTPROV to reduce the overhead.
321 // static
CalculateCAFingerprint(const OSCertHandles & intermediates)322 SHA1HashValue X509Certificate::CalculateCAFingerprint(
323     const OSCertHandles& intermediates) {
324   SHA1HashValue sha1;
325   memset(sha1.data, 0, sizeof(sha1.data));
326 
327   SHA1Context* sha1_ctx = SHA1_NewContext();
328   if (!sha1_ctx)
329     return sha1;
330   SHA1_Begin(sha1_ctx);
331   for (size_t i = 0; i < intermediates.size(); ++i) {
332     PCCERT_CONTEXT ca_cert = intermediates[i];
333     SHA1_Update(sha1_ctx, ca_cert->pbCertEncoded, ca_cert->cbCertEncoded);
334   }
335   unsigned int result_len;
336   SHA1_End(sha1_ctx, sha1.data, &result_len, SHA1_LENGTH);
337   SHA1_DestroyContext(sha1_ctx, PR_TRUE);
338 
339   return sha1;
340 }
341 
342 // static
343 X509Certificate::OSCertHandle
ReadOSCertHandleFromPickle(PickleIterator * pickle_iter)344 X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
345   const char* data;
346   int length;
347   if (!pickle_iter->ReadData(&data, &length))
348     return NULL;
349 
350   // Legacy serialized certificates were serialized with extended attributes,
351   // rather than as DER only. As a result, these serialized certificates are
352   // not portable across platforms and may have side-effects on Windows due
353   // to extended attributes being serialized/deserialized -
354   // http://crbug.com/118706. To avoid deserializing these attributes, write
355   // the deserialized cert into a temporary cert store and then create a new
356   // cert from the DER - that is, without attributes.
357   ScopedHCERTSTORE store(
358       CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL));
359   if (!store.get())
360     return NULL;
361 
362   OSCertHandle cert_handle = NULL;
363   if (!CertAddSerializedElementToStore(
364           store.get(), reinterpret_cast<const BYTE*>(data), length,
365           CERT_STORE_ADD_NEW, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG,
366           NULL, reinterpret_cast<const void **>(&cert_handle))) {
367     return NULL;
368   }
369 
370   std::string encoded;
371   bool ok = GetDEREncoded(cert_handle, &encoded);
372   FreeOSCertHandle(cert_handle);
373   cert_handle = NULL;
374 
375   if (ok)
376     cert_handle = CreateOSCertHandleFromBytes(encoded.data(), encoded.size());
377   return cert_handle;
378 }
379 
380 // static
WriteOSCertHandleToPickle(OSCertHandle cert_handle,Pickle * pickle)381 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
382                                                 Pickle* pickle) {
383   return pickle->WriteData(
384       reinterpret_cast<char*>(cert_handle->pbCertEncoded),
385       cert_handle->cbCertEncoded);
386 }
387 
388 // static
GetPublicKeyInfo(OSCertHandle cert_handle,size_t * size_bits,PublicKeyType * type)389 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
390                                        size_t* size_bits,
391                                        PublicKeyType* type) {
392   *type = kPublicKeyTypeUnknown;
393   *size_bits = 0;
394 
395   PCCRYPT_OID_INFO oid_info = CryptFindOIDInfo(
396       CRYPT_OID_INFO_OID_KEY,
397       cert_handle->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId,
398       CRYPT_PUBKEY_ALG_OID_GROUP_ID);
399   if (!oid_info)
400     return;
401 
402   CHECK_EQ(oid_info->dwGroupId,
403            static_cast<DWORD>(CRYPT_PUBKEY_ALG_OID_GROUP_ID));
404 
405   *size_bits = CertGetPublicKeyLength(
406       X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
407       &cert_handle->pCertInfo->SubjectPublicKeyInfo);
408 
409   if (IS_SPECIAL_OID_INFO_ALGID(oid_info->Algid)) {
410     // For an EC public key, oid_info->Algid is CALG_OID_INFO_PARAMETERS
411     // (0xFFFFFFFE). Need to handle it as a special case.
412     if (strcmp(oid_info->pszOID, szOID_ECC_PUBLIC_KEY) == 0) {
413       *type = kPublicKeyTypeECDSA;
414     } else {
415       NOTREACHED();
416     }
417     return;
418   }
419   switch (oid_info->Algid) {
420     case CALG_RSA_SIGN:
421     case CALG_RSA_KEYX:
422       *type = kPublicKeyTypeRSA;
423       break;
424     case CALG_DSS_SIGN:
425       *type = kPublicKeyTypeDSA;
426       break;
427     case CALG_ECDSA:
428       *type = kPublicKeyTypeECDSA;
429       break;
430     case CALG_ECDH:
431       *type = kPublicKeyTypeECDH;
432       break;
433   }
434 }
435 
IsIssuedByEncoded(const std::vector<std::string> & valid_issuers)436 bool X509Certificate::IsIssuedByEncoded(
437     const std::vector<std::string>& valid_issuers) {
438 
439   // If the certificate's issuer in the list?
440   if (IsCertNameBlobInIssuerList(&cert_handle_->pCertInfo->Issuer,
441                                  valid_issuers)) {
442     return true;
443   }
444   // Otherwise, is any of the intermediate CA subjects in the list?
445   for (OSCertHandles::iterator it = intermediate_ca_certs_.begin();
446        it != intermediate_ca_certs_.end(); ++it) {
447     if (IsCertNameBlobInIssuerList(&(*it)->pCertInfo->Issuer,
448                                    valid_issuers)) {
449       return true;
450     }
451   }
452 
453   return false;
454 }
455 
456 }  // namespace net
457