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.
5 #include "net/cert/x509_certificate.h"
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/pickle.h"
10 #include "base/sha1.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "crypto/capi_util.h"
14 #include "crypto/scoped_capi_types.h"
15 #include "crypto/sha2.h"
16 #include "net/base/net_errors.h"
18 // Implement CalculateChainFingerprint() with our native crypto library.
19 #if defined(USE_OPENSSL)
20 #include <openssl/sha.h>
25 #pragma comment(lib, "crypt32.lib")
33 typedef crypto::ScopedCAPIHandle
<
35 crypto::CAPIDestroyerWithFlags
<HCERTSTORE
,
36 CertCloseStore
, 0> > ScopedHCERTSTORE
;
38 void ExplodedTimeToSystemTime(const base::Time::Exploded
& exploded
,
39 SYSTEMTIME
* system_time
) {
40 system_time
->wYear
= static_cast<WORD
>(exploded
.year
);
41 system_time
->wMonth
= static_cast<WORD
>(exploded
.month
);
42 system_time
->wDayOfWeek
= static_cast<WORD
>(exploded
.day_of_week
);
43 system_time
->wDay
= static_cast<WORD
>(exploded
.day_of_month
);
44 system_time
->wHour
= static_cast<WORD
>(exploded
.hour
);
45 system_time
->wMinute
= static_cast<WORD
>(exploded
.minute
);
46 system_time
->wSecond
= static_cast<WORD
>(exploded
.second
);
47 system_time
->wMilliseconds
= static_cast<WORD
>(exploded
.millisecond
);
50 //-----------------------------------------------------------------------------
52 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
53 // structure and stores it in *output.
54 void GetCertSubjectAltName(
56 scoped_ptr
<CERT_ALT_NAME_INFO
, base::FreeDeleter
>* output
) {
57 PCERT_EXTENSION extension
= CertFindExtension(szOID_SUBJECT_ALT_NAME2
,
58 cert
->pCertInfo
->cExtension
,
59 cert
->pCertInfo
->rgExtension
);
63 CRYPT_DECODE_PARA decode_para
;
64 decode_para
.cbSize
= sizeof(decode_para
);
65 decode_para
.pfnAlloc
= crypto::CryptAlloc
;
66 decode_para
.pfnFree
= crypto::CryptFree
;
67 CERT_ALT_NAME_INFO
* alt_name_info
= NULL
;
68 DWORD alt_name_info_size
= 0;
70 rv
= CryptDecodeObjectEx(X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
71 szOID_SUBJECT_ALT_NAME2
,
72 extension
->Value
.pbData
,
73 extension
->Value
.cbData
,
74 CRYPT_DECODE_ALLOC_FLAG
| CRYPT_DECODE_NOCOPY_FLAG
,
79 output
->reset(alt_name_info
);
82 void AddCertsFromStore(HCERTSTORE store
,
83 X509Certificate::OSCertHandles
* results
) {
84 PCCERT_CONTEXT cert
= NULL
;
86 while ((cert
= CertEnumCertificatesInStore(store
, cert
)) != NULL
) {
87 PCCERT_CONTEXT to_add
= NULL
;
88 if (CertAddCertificateContextToStore(
89 NULL
, // The cert won't be persisted in any cert store. This breaks
90 // any association the context currently has to |store|, which
91 // allows us, the caller, to safely close |store| without
92 // releasing the cert handles.
94 CERT_STORE_ADD_USE_EXISTING
,
95 &to_add
) && to_add
!= NULL
) {
96 // When processing stores generated from PKCS#7/PKCS#12 files, it
97 // appears that the order returned is the inverse of the order that it
98 // appeared in the file.
99 // TODO(rsleevi): Ensure this order is consistent across all Win
101 results
->insert(results
->begin(), to_add
);
106 X509Certificate::OSCertHandles
ParsePKCS7(const char* data
, size_t length
) {
107 X509Certificate::OSCertHandles results
;
109 data_blob
.cbData
= length
;
110 data_blob
.pbData
= reinterpret_cast<BYTE
*>(const_cast<char*>(data
));
112 HCERTSTORE out_store
= NULL
;
114 DWORD expected_types
= CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
|
115 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED
|
116 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
;
118 if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &data_blob
, expected_types
,
119 CERT_QUERY_FORMAT_FLAG_BINARY
, 0, NULL
, NULL
, NULL
,
120 &out_store
, NULL
, NULL
) || out_store
== NULL
) {
124 AddCertsFromStore(out_store
, &results
);
125 CertCloseStore(out_store
, CERT_CLOSE_STORE_CHECK_FLAG
);
130 // Given a CERT_NAME_BLOB, returns true if it appears in a given list,
131 // formatted as a vector of strings holding DER-encoded X.509
132 // DistinguishedName entries.
133 bool IsCertNameBlobInIssuerList(
134 CERT_NAME_BLOB
* name_blob
,
135 const std::vector
<std::string
>& issuer_names
) {
136 for (std::vector
<std::string
>::const_iterator it
= issuer_names
.begin();
137 it
!= issuer_names
.end(); ++it
) {
138 CERT_NAME_BLOB issuer_blob
;
140 reinterpret_cast<BYTE
*>(const_cast<char*>(it
->data()));
141 issuer_blob
.cbData
= static_cast<DWORD
>(it
->length());
143 BOOL rb
= CertCompareCertificateName(
144 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, &issuer_blob
, name_blob
);
153 void X509Certificate::Initialize() {
154 DCHECK(cert_handle_
);
155 subject_
.ParseDistinguishedName(cert_handle_
->pCertInfo
->Subject
.pbData
,
156 cert_handle_
->pCertInfo
->Subject
.cbData
);
157 issuer_
.ParseDistinguishedName(cert_handle_
->pCertInfo
->Issuer
.pbData
,
158 cert_handle_
->pCertInfo
->Issuer
.cbData
);
160 valid_start_
= Time::FromFileTime(cert_handle_
->pCertInfo
->NotBefore
);
161 valid_expiry_
= Time::FromFileTime(cert_handle_
->pCertInfo
->NotAfter
);
163 fingerprint_
= CalculateFingerprint(cert_handle_
);
164 ca_fingerprint_
= CalculateCAFingerprint(intermediate_ca_certs_
);
166 const CRYPT_INTEGER_BLOB
* serial
= &cert_handle_
->pCertInfo
->SerialNumber
;
167 scoped_ptr
<uint8_t[]> serial_bytes(new uint8_t[serial
->cbData
]);
168 for (unsigned i
= 0; i
< serial
->cbData
; i
++)
169 serial_bytes
[i
] = serial
->pbData
[serial
->cbData
- i
- 1];
170 serial_number_
= std::string(
171 reinterpret_cast<char*>(serial_bytes
.get()), serial
->cbData
);
174 void X509Certificate::GetSubjectAltName(
175 std::vector
<std::string
>* dns_names
,
176 std::vector
<std::string
>* ip_addrs
) const {
185 scoped_ptr
<CERT_ALT_NAME_INFO
, base::FreeDeleter
> alt_name_info
;
186 GetCertSubjectAltName(cert_handle_
, &alt_name_info
);
187 CERT_ALT_NAME_INFO
* alt_name
= alt_name_info
.get();
189 int num_entries
= alt_name
->cAltEntry
;
190 for (int i
= 0; i
< num_entries
; i
++) {
191 // dNSName is an ASN.1 IA5String representing a string of ASCII
192 // characters, so we can use UTF16ToASCII here.
193 const CERT_ALT_NAME_ENTRY
& entry
= alt_name
->rgAltEntry
[i
];
195 if (dns_names
&& entry
.dwAltNameChoice
== CERT_ALT_NAME_DNS_NAME
) {
196 dns_names
->push_back(base::UTF16ToASCII(entry
.pwszDNSName
));
197 } else if (ip_addrs
&&
198 entry
.dwAltNameChoice
== CERT_ALT_NAME_IP_ADDRESS
) {
199 ip_addrs
->push_back(std::string(
200 reinterpret_cast<const char*>(entry
.IPAddress
.pbData
),
201 entry
.IPAddress
.cbData
));
207 PCCERT_CONTEXT
X509Certificate::CreateOSCertChainForCert() const {
208 // Create an in-memory certificate store to hold this certificate and
209 // any intermediate certificates in |intermediate_ca_certs_|. The store
210 // will be referenced in the returned PCCERT_CONTEXT, and will not be freed
211 // until the PCCERT_CONTEXT is freed.
212 ScopedHCERTSTORE
store(CertOpenStore(
213 CERT_STORE_PROV_MEMORY
, 0, NULL
,
214 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG
, NULL
));
218 // NOTE: This preserves all of the properties of |os_cert_handle()| except
219 // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two
220 // properties that hold access to already-opened private keys. If a handle
221 // has already been unlocked (eg: PIN prompt), then the first time that the
222 // identity is used for client auth, it may prompt the user again.
223 PCCERT_CONTEXT primary_cert
;
224 BOOL ok
= CertAddCertificateContextToStore(store
.get(), os_cert_handle(),
225 CERT_STORE_ADD_ALWAYS
,
227 if (!ok
|| !primary_cert
)
230 for (size_t i
= 0; i
< intermediate_ca_certs_
.size(); ++i
) {
231 CertAddCertificateContextToStore(store
.get(), intermediate_ca_certs_
[i
],
232 CERT_STORE_ADD_ALWAYS
, NULL
);
235 // Note: |store| is explicitly not released, as the call to CertCloseStore()
236 // when |store| goes out of scope will not actually free the store. Instead,
237 // the store will be freed when |primary_cert| is freed.
242 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle
,
243 std::string
* encoded
) {
244 if (!cert_handle
|| !cert_handle
->pbCertEncoded
||
245 !cert_handle
->cbCertEncoded
) {
248 encoded
->assign(reinterpret_cast<char*>(cert_handle
->pbCertEncoded
),
249 cert_handle
->cbCertEncoded
);
254 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a
,
255 X509Certificate::OSCertHandle b
) {
259 return a
->cbCertEncoded
== b
->cbCertEncoded
&&
260 memcmp(a
->pbCertEncoded
, b
->pbCertEncoded
, a
->cbCertEncoded
) == 0;
264 X509Certificate::OSCertHandle
X509Certificate::CreateOSCertHandleFromBytes(
265 const char* data
, int length
) {
266 OSCertHandle cert_handle
= NULL
;
267 if (!CertAddEncodedCertificateToStore(
268 NULL
, X509_ASN_ENCODING
, reinterpret_cast<const BYTE
*>(data
),
269 length
, CERT_STORE_ADD_USE_EXISTING
, &cert_handle
))
275 X509Certificate::OSCertHandles
X509Certificate::CreateOSCertHandlesFromBytes(
276 const char* data
, int length
, Format format
) {
277 OSCertHandles results
;
279 case FORMAT_SINGLE_CERTIFICATE
: {
280 OSCertHandle handle
= CreateOSCertHandleFromBytes(data
, length
);
282 results
.push_back(handle
);
286 results
= ParsePKCS7(data
, length
);
289 NOTREACHED() << "Certificate format " << format
<< " unimplemented";
297 X509Certificate::OSCertHandle
X509Certificate::DupOSCertHandle(
298 OSCertHandle cert_handle
) {
299 return CertDuplicateCertificateContext(cert_handle
);
303 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle
) {
304 CertFreeCertificateContext(cert_handle
);
308 SHA1HashValue
X509Certificate::CalculateFingerprint(
310 DCHECK(NULL
!= cert
->pbCertEncoded
);
311 DCHECK_NE(static_cast<DWORD
>(0), cert
->cbCertEncoded
);
315 DWORD sha1_size
= sizeof(sha1
.data
);
316 rv
= CryptHashCertificate(NULL
, CALG_SHA1
, 0, cert
->pbCertEncoded
,
317 cert
->cbCertEncoded
, sha1
.data
, &sha1_size
);
318 DCHECK(rv
&& sha1_size
== sizeof(sha1
.data
));
320 memset(sha1
.data
, 0, sizeof(sha1
.data
));
325 SHA256HashValue
X509Certificate::CalculateFingerprint256(OSCertHandle cert
) {
326 DCHECK(NULL
!= cert
->pbCertEncoded
);
327 DCHECK_NE(0u, cert
->cbCertEncoded
);
329 SHA256HashValue sha256
;
330 size_t sha256_size
= sizeof(sha256
.data
);
332 // Use crypto::SHA256HashString for two reasons:
333 // * < Windows Vista does not have universal SHA-256 support.
334 // * More efficient on Windows > Vista (less overhead since non-default CSP
336 base::StringPiece
der_cert(reinterpret_cast<const char*>(cert
->pbCertEncoded
),
337 cert
->cbCertEncoded
);
338 crypto::SHA256HashString(der_cert
, sha256
.data
, sha256_size
);
342 SHA1HashValue
X509Certificate::CalculateCAFingerprint(
343 const OSCertHandles
& intermediates
) {
345 memset(sha1
.data
, 0, sizeof(sha1
.data
));
347 #if defined(USE_OPENSSL)
349 if (!SHA1_Init(&ctx
))
351 for (size_t i
= 0; i
< intermediates
.size(); ++i
) {
352 PCCERT_CONTEXT ca_cert
= intermediates
[i
];
353 if (!SHA1_Update(&ctx
, ca_cert
->pbCertEncoded
, ca_cert
->cbCertEncoded
))
356 SHA1_Final(sha1
.data
, &ctx
);
357 #else // !USE_OPENSSL
358 SHA1Context
* sha1_ctx
= SHA1_NewContext();
361 SHA1_Begin(sha1_ctx
);
362 for (size_t i
= 0; i
< intermediates
.size(); ++i
) {
363 PCCERT_CONTEXT ca_cert
= intermediates
[i
];
364 SHA1_Update(sha1_ctx
, ca_cert
->pbCertEncoded
, ca_cert
->cbCertEncoded
);
366 unsigned int result_len
;
367 SHA1_End(sha1_ctx
, sha1
.data
, &result_len
, SHA1_LENGTH
);
368 SHA1_DestroyContext(sha1_ctx
, PR_TRUE
);
369 #endif // USE_OPENSSL
375 X509Certificate::OSCertHandle
X509Certificate::ReadOSCertHandleFromPickle(
376 base::PickleIterator
* pickle_iter
) {
379 if (!pickle_iter
->ReadData(&data
, &length
))
382 // Legacy serialized certificates were serialized with extended attributes,
383 // rather than as DER only. As a result, these serialized certificates are
384 // not portable across platforms and may have side-effects on Windows due
385 // to extended attributes being serialized/deserialized -
386 // http://crbug.com/118706. To avoid deserializing these attributes, write
387 // the deserialized cert into a temporary cert store and then create a new
388 // cert from the DER - that is, without attributes.
389 ScopedHCERTSTORE
store(
390 CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, NULL
, 0, NULL
));
394 OSCertHandle cert_handle
= NULL
;
395 if (!CertAddSerializedElementToStore(
396 store
.get(), reinterpret_cast<const BYTE
*>(data
), length
,
397 CERT_STORE_ADD_NEW
, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG
,
398 NULL
, reinterpret_cast<const void **>(&cert_handle
))) {
403 bool ok
= GetDEREncoded(cert_handle
, &encoded
);
404 FreeOSCertHandle(cert_handle
);
408 cert_handle
= CreateOSCertHandleFromBytes(encoded
.data(), encoded
.size());
413 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle
,
414 base::Pickle
* pickle
) {
415 return pickle
->WriteData(
416 reinterpret_cast<char*>(cert_handle
->pbCertEncoded
),
417 cert_handle
->cbCertEncoded
);
421 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle
,
423 PublicKeyType
* type
) {
424 *type
= kPublicKeyTypeUnknown
;
427 PCCRYPT_OID_INFO oid_info
= CryptFindOIDInfo(
428 CRYPT_OID_INFO_OID_KEY
,
429 cert_handle
->pCertInfo
->SubjectPublicKeyInfo
.Algorithm
.pszObjId
,
430 CRYPT_PUBKEY_ALG_OID_GROUP_ID
);
434 CHECK_EQ(oid_info
->dwGroupId
,
435 static_cast<DWORD
>(CRYPT_PUBKEY_ALG_OID_GROUP_ID
));
437 *size_bits
= CertGetPublicKeyLength(
438 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
439 &cert_handle
->pCertInfo
->SubjectPublicKeyInfo
);
441 if (IS_SPECIAL_OID_INFO_ALGID(oid_info
->Algid
)) {
442 // For an EC public key, oid_info->Algid is CALG_OID_INFO_PARAMETERS
443 // (0xFFFFFFFE). Need to handle it as a special case.
444 if (strcmp(oid_info
->pszOID
, szOID_ECC_PUBLIC_KEY
) == 0) {
445 *type
= kPublicKeyTypeECDSA
;
451 switch (oid_info
->Algid
) {
454 *type
= kPublicKeyTypeRSA
;
457 *type
= kPublicKeyTypeDSA
;
460 *type
= kPublicKeyTypeECDSA
;
463 *type
= kPublicKeyTypeECDH
;
468 bool X509Certificate::IsIssuedByEncoded(
469 const std::vector
<std::string
>& valid_issuers
) {
471 // If the certificate's issuer in the list?
472 if (IsCertNameBlobInIssuerList(&cert_handle_
->pCertInfo
->Issuer
,
476 // Otherwise, is any of the intermediate CA subjects in the list?
477 for (OSCertHandles::iterator it
= intermediate_ca_certs_
.begin();
478 it
!= intermediate_ca_certs_
.end(); ++it
) {
479 if (IsCertNameBlobInIssuerList(&(*it
)->pCertInfo
->Issuer
,
489 bool X509Certificate::IsSelfSigned(OSCertHandle cert_handle
) {
490 return !!CryptVerifyCertificateSignatureEx(
493 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT
,
494 reinterpret_cast<void*>(const_cast<PCERT_CONTEXT
>(cert_handle
)),
495 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT
,
496 reinterpret_cast<void*>(const_cast<PCERT_CONTEXT
>(cert_handle
)),