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
21using base::Time;
22
23namespace net {
24
25namespace {
26
27typedef crypto::ScopedCAPIHandle<
28    HCERTSTORE,
29    crypto::CAPIDestroyerWithFlags<HCERTSTORE,
30                                   CertCloseStore, 0> > ScopedHCERTSTORE;
31
32void 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.
48void 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
76void 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
100X509Certificate::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.
127bool 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
147void 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
168void 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
201PCCERT_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
236bool 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
248bool 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
258X509Certificate::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
269X509Certificate::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
291X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
292    OSCertHandle cert_handle) {
293  return CertDuplicateCertificateContext(cert_handle);
294}
295
296// static
297void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
298  CertFreeCertificateContext(cert_handle);
299}
300
301// static
302SHA1HashValue 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
322SHA1HashValue 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
343X509Certificate::OSCertHandle
344X509Certificate::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
381bool 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
389void 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
436bool 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