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