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 <blapi.h> // Implement CalculateChainFingerprint() with NSS.
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 "crypto/sha2.h"
18 #include "net/base/net_errors.h"
20 #pragma comment(lib, "crypt32.lib")
28 typedef crypto::ScopedCAPIHandle
<
30 crypto::CAPIDestroyerWithFlags
<HCERTSTORE
,
31 CertCloseStore
, 0> > ScopedHCERTSTORE
;
33 void ExplodedTimeToSystemTime(const base::Time::Exploded
& exploded
,
34 SYSTEMTIME
* system_time
) {
35 system_time
->wYear
= exploded
.year
;
36 system_time
->wMonth
= exploded
.month
;
37 system_time
->wDayOfWeek
= exploded
.day_of_week
;
38 system_time
->wDay
= exploded
.day_of_month
;
39 system_time
->wHour
= exploded
.hour
;
40 system_time
->wMinute
= exploded
.minute
;
41 system_time
->wSecond
= exploded
.second
;
42 system_time
->wMilliseconds
= exploded
.millisecond
;
45 //-----------------------------------------------------------------------------
47 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
48 // structure and stores it in *output.
49 void GetCertSubjectAltName(
51 scoped_ptr
<CERT_ALT_NAME_INFO
, base::FreeDeleter
>* output
) {
52 PCERT_EXTENSION extension
= CertFindExtension(szOID_SUBJECT_ALT_NAME2
,
53 cert
->pCertInfo
->cExtension
,
54 cert
->pCertInfo
->rgExtension
);
58 CRYPT_DECODE_PARA decode_para
;
59 decode_para
.cbSize
= sizeof(decode_para
);
60 decode_para
.pfnAlloc
= crypto::CryptAlloc
;
61 decode_para
.pfnFree
= crypto::CryptFree
;
62 CERT_ALT_NAME_INFO
* alt_name_info
= NULL
;
63 DWORD alt_name_info_size
= 0;
65 rv
= CryptDecodeObjectEx(X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
66 szOID_SUBJECT_ALT_NAME2
,
67 extension
->Value
.pbData
,
68 extension
->Value
.cbData
,
69 CRYPT_DECODE_ALLOC_FLAG
| CRYPT_DECODE_NOCOPY_FLAG
,
74 output
->reset(alt_name_info
);
77 void AddCertsFromStore(HCERTSTORE store
,
78 X509Certificate::OSCertHandles
* results
) {
79 PCCERT_CONTEXT cert
= NULL
;
81 while ((cert
= CertEnumCertificatesInStore(store
, cert
)) != NULL
) {
82 PCCERT_CONTEXT to_add
= NULL
;
83 if (CertAddCertificateContextToStore(
84 NULL
, // The cert won't be persisted in any cert store. This breaks
85 // any association the context currently has to |store|, which
86 // allows us, the caller, to safely close |store| without
87 // releasing the cert handles.
89 CERT_STORE_ADD_USE_EXISTING
,
90 &to_add
) && to_add
!= NULL
) {
91 // When processing stores generated from PKCS#7/PKCS#12 files, it
92 // appears that the order returned is the inverse of the order that it
93 // appeared in the file.
94 // TODO(rsleevi): Ensure this order is consistent across all Win
96 results
->insert(results
->begin(), to_add
);
101 X509Certificate::OSCertHandles
ParsePKCS7(const char* data
, size_t length
) {
102 X509Certificate::OSCertHandles results
;
104 data_blob
.cbData
= length
;
105 data_blob
.pbData
= reinterpret_cast<BYTE
*>(const_cast<char*>(data
));
107 HCERTSTORE out_store
= NULL
;
109 DWORD expected_types
= CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
|
110 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED
|
111 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
;
113 if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &data_blob
, expected_types
,
114 CERT_QUERY_FORMAT_FLAG_BINARY
, 0, NULL
, NULL
, NULL
,
115 &out_store
, NULL
, NULL
) || out_store
== NULL
) {
119 AddCertsFromStore(out_store
, &results
);
120 CertCloseStore(out_store
, CERT_CLOSE_STORE_CHECK_FLAG
);
125 // Given a CERT_NAME_BLOB, returns true if it appears in a given list,
126 // formatted as a vector of strings holding DER-encoded X.509
127 // DistinguishedName entries.
128 bool IsCertNameBlobInIssuerList(
129 CERT_NAME_BLOB
* name_blob
,
130 const std::vector
<std::string
>& issuer_names
) {
131 for (std::vector
<std::string
>::const_iterator it
= issuer_names
.begin();
132 it
!= issuer_names
.end(); ++it
) {
133 CERT_NAME_BLOB issuer_blob
;
135 reinterpret_cast<BYTE
*>(const_cast<char*>(it
->data()));
136 issuer_blob
.cbData
= static_cast<DWORD
>(it
->length());
138 BOOL rb
= CertCompareCertificateName(
139 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, &issuer_blob
, name_blob
);
148 void X509Certificate::Initialize() {
149 DCHECK(cert_handle_
);
150 subject_
.ParseDistinguishedName(cert_handle_
->pCertInfo
->Subject
.pbData
,
151 cert_handle_
->pCertInfo
->Subject
.cbData
);
152 issuer_
.ParseDistinguishedName(cert_handle_
->pCertInfo
->Issuer
.pbData
,
153 cert_handle_
->pCertInfo
->Issuer
.cbData
);
155 valid_start_
= Time::FromFileTime(cert_handle_
->pCertInfo
->NotBefore
);
156 valid_expiry_
= Time::FromFileTime(cert_handle_
->pCertInfo
->NotAfter
);
158 fingerprint_
= CalculateFingerprint(cert_handle_
);
159 ca_fingerprint_
= CalculateCAFingerprint(intermediate_ca_certs_
);
161 const CRYPT_INTEGER_BLOB
* serial
= &cert_handle_
->pCertInfo
->SerialNumber
;
162 scoped_ptr
<uint8
[]> serial_bytes(new uint8
[serial
->cbData
]);
163 for (unsigned i
= 0; i
< serial
->cbData
; i
++)
164 serial_bytes
[i
] = serial
->pbData
[serial
->cbData
- i
- 1];
165 serial_number_
= std::string(
166 reinterpret_cast<char*>(serial_bytes
.get()), serial
->cbData
);
169 void X509Certificate::GetSubjectAltName(
170 std::vector
<std::string
>* dns_names
,
171 std::vector
<std::string
>* ip_addrs
) const {
180 scoped_ptr
<CERT_ALT_NAME_INFO
, base::FreeDeleter
> alt_name_info
;
181 GetCertSubjectAltName(cert_handle_
, &alt_name_info
);
182 CERT_ALT_NAME_INFO
* alt_name
= alt_name_info
.get();
184 int num_entries
= alt_name
->cAltEntry
;
185 for (int i
= 0; i
< num_entries
; i
++) {
186 // dNSName is an ASN.1 IA5String representing a string of ASCII
187 // characters, so we can use UTF16ToASCII here.
188 const CERT_ALT_NAME_ENTRY
& entry
= alt_name
->rgAltEntry
[i
];
190 if (dns_names
&& entry
.dwAltNameChoice
== CERT_ALT_NAME_DNS_NAME
) {
191 dns_names
->push_back(base::UTF16ToASCII(entry
.pwszDNSName
));
192 } else if (ip_addrs
&&
193 entry
.dwAltNameChoice
== CERT_ALT_NAME_IP_ADDRESS
) {
194 ip_addrs
->push_back(std::string(
195 reinterpret_cast<const char*>(entry
.IPAddress
.pbData
),
196 entry
.IPAddress
.cbData
));
202 PCCERT_CONTEXT
X509Certificate::CreateOSCertChainForCert() const {
203 // Create an in-memory certificate store to hold this certificate and
204 // any intermediate certificates in |intermediate_ca_certs_|. The store
205 // will be referenced in the returned PCCERT_CONTEXT, and will not be freed
206 // until the PCCERT_CONTEXT is freed.
207 ScopedHCERTSTORE
store(CertOpenStore(
208 CERT_STORE_PROV_MEMORY
, 0, NULL
,
209 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG
, NULL
));
213 // NOTE: This preserves all of the properties of |os_cert_handle()| except
214 // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two
215 // properties that hold access to already-opened private keys. If a handle
216 // has already been unlocked (eg: PIN prompt), then the first time that the
217 // identity is used for client auth, it may prompt the user again.
218 PCCERT_CONTEXT primary_cert
;
219 BOOL ok
= CertAddCertificateContextToStore(store
.get(), os_cert_handle(),
220 CERT_STORE_ADD_ALWAYS
,
222 if (!ok
|| !primary_cert
)
225 for (size_t i
= 0; i
< intermediate_ca_certs_
.size(); ++i
) {
226 CertAddCertificateContextToStore(store
.get(), intermediate_ca_certs_
[i
],
227 CERT_STORE_ADD_ALWAYS
, NULL
);
230 // Note: |store| is explicitly not released, as the call to CertCloseStore()
231 // when |store| goes out of scope will not actually free the store. Instead,
232 // the store will be freed when |primary_cert| is freed.
237 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle
,
238 std::string
* encoded
) {
239 if (!cert_handle
|| !cert_handle
->pbCertEncoded
||
240 !cert_handle
->cbCertEncoded
) {
243 encoded
->assign(reinterpret_cast<char*>(cert_handle
->pbCertEncoded
),
244 cert_handle
->cbCertEncoded
);
249 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a
,
250 X509Certificate::OSCertHandle b
) {
254 return a
->cbCertEncoded
== b
->cbCertEncoded
&&
255 memcmp(a
->pbCertEncoded
, b
->pbCertEncoded
, a
->cbCertEncoded
) == 0;
259 X509Certificate::OSCertHandle
X509Certificate::CreateOSCertHandleFromBytes(
260 const char* data
, int length
) {
261 OSCertHandle cert_handle
= NULL
;
262 if (!CertAddEncodedCertificateToStore(
263 NULL
, X509_ASN_ENCODING
, reinterpret_cast<const BYTE
*>(data
),
264 length
, CERT_STORE_ADD_USE_EXISTING
, &cert_handle
))
270 X509Certificate::OSCertHandles
X509Certificate::CreateOSCertHandlesFromBytes(
271 const char* data
, int length
, Format format
) {
272 OSCertHandles results
;
274 case FORMAT_SINGLE_CERTIFICATE
: {
275 OSCertHandle handle
= CreateOSCertHandleFromBytes(data
, length
);
277 results
.push_back(handle
);
281 results
= ParsePKCS7(data
, length
);
284 NOTREACHED() << "Certificate format " << format
<< " unimplemented";
292 X509Certificate::OSCertHandle
X509Certificate::DupOSCertHandle(
293 OSCertHandle cert_handle
) {
294 return CertDuplicateCertificateContext(cert_handle
);
298 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle
) {
299 CertFreeCertificateContext(cert_handle
);
303 SHA1HashValue
X509Certificate::CalculateFingerprint(
305 DCHECK(NULL
!= cert
->pbCertEncoded
);
306 DCHECK_NE(static_cast<DWORD
>(0), cert
->cbCertEncoded
);
310 DWORD sha1_size
= sizeof(sha1
.data
);
311 rv
= CryptHashCertificate(NULL
, CALG_SHA1
, 0, cert
->pbCertEncoded
,
312 cert
->cbCertEncoded
, sha1
.data
, &sha1_size
);
313 DCHECK(rv
&& sha1_size
== sizeof(sha1
.data
));
315 memset(sha1
.data
, 0, sizeof(sha1
.data
));
320 SHA256HashValue
X509Certificate::CalculateFingerprint256(OSCertHandle cert
) {
321 DCHECK(NULL
!= cert
->pbCertEncoded
);
322 DCHECK_NE(0u, cert
->cbCertEncoded
);
324 SHA256HashValue sha256
;
325 size_t sha256_size
= sizeof(sha256
.data
);
327 // Use crypto::SHA256HashString for two reasons:
328 // * < Windows Vista does not have universal SHA-256 support.
329 // * More efficient on Windows > Vista (less overhead since non-default CSP
331 base::StringPiece
der_cert(reinterpret_cast<const char*>(cert
->pbCertEncoded
),
332 cert
->cbCertEncoded
);
333 crypto::SHA256HashString(der_cert
, sha256
.data
, sha256_size
);
337 // TODO(wtc): This function is implemented with NSS low-level hash
338 // functions to ensure it is fast. Reimplement this function with
339 // CryptoAPI. May need to cache the HCRYPTPROV to reduce the overhead.
341 SHA1HashValue
X509Certificate::CalculateCAFingerprint(
342 const OSCertHandles
& intermediates
) {
344 memset(sha1
.data
, 0, sizeof(sha1
.data
));
346 SHA1Context
* sha1_ctx
= SHA1_NewContext();
349 SHA1_Begin(sha1_ctx
);
350 for (size_t i
= 0; i
< intermediates
.size(); ++i
) {
351 PCCERT_CONTEXT ca_cert
= intermediates
[i
];
352 SHA1_Update(sha1_ctx
, ca_cert
->pbCertEncoded
, ca_cert
->cbCertEncoded
);
354 unsigned int result_len
;
355 SHA1_End(sha1_ctx
, sha1
.data
, &result_len
, SHA1_LENGTH
);
356 SHA1_DestroyContext(sha1_ctx
, PR_TRUE
);
362 X509Certificate::OSCertHandle
363 X509Certificate::ReadOSCertHandleFromPickle(PickleIterator
* pickle_iter
) {
366 if (!pickle_iter
->ReadData(&data
, &length
))
369 // Legacy serialized certificates were serialized with extended attributes,
370 // rather than as DER only. As a result, these serialized certificates are
371 // not portable across platforms and may have side-effects on Windows due
372 // to extended attributes being serialized/deserialized -
373 // http://crbug.com/118706. To avoid deserializing these attributes, write
374 // the deserialized cert into a temporary cert store and then create a new
375 // cert from the DER - that is, without attributes.
376 ScopedHCERTSTORE
store(
377 CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, NULL
, 0, NULL
));
381 OSCertHandle cert_handle
= NULL
;
382 if (!CertAddSerializedElementToStore(
383 store
.get(), reinterpret_cast<const BYTE
*>(data
), length
,
384 CERT_STORE_ADD_NEW
, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG
,
385 NULL
, reinterpret_cast<const void **>(&cert_handle
))) {
390 bool ok
= GetDEREncoded(cert_handle
, &encoded
);
391 FreeOSCertHandle(cert_handle
);
395 cert_handle
= CreateOSCertHandleFromBytes(encoded
.data(), encoded
.size());
400 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle
,
402 return pickle
->WriteData(
403 reinterpret_cast<char*>(cert_handle
->pbCertEncoded
),
404 cert_handle
->cbCertEncoded
);
408 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle
,
410 PublicKeyType
* type
) {
411 *type
= kPublicKeyTypeUnknown
;
414 PCCRYPT_OID_INFO oid_info
= CryptFindOIDInfo(
415 CRYPT_OID_INFO_OID_KEY
,
416 cert_handle
->pCertInfo
->SubjectPublicKeyInfo
.Algorithm
.pszObjId
,
417 CRYPT_PUBKEY_ALG_OID_GROUP_ID
);
421 CHECK_EQ(oid_info
->dwGroupId
,
422 static_cast<DWORD
>(CRYPT_PUBKEY_ALG_OID_GROUP_ID
));
424 *size_bits
= CertGetPublicKeyLength(
425 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
426 &cert_handle
->pCertInfo
->SubjectPublicKeyInfo
);
428 if (IS_SPECIAL_OID_INFO_ALGID(oid_info
->Algid
)) {
429 // For an EC public key, oid_info->Algid is CALG_OID_INFO_PARAMETERS
430 // (0xFFFFFFFE). Need to handle it as a special case.
431 if (strcmp(oid_info
->pszOID
, szOID_ECC_PUBLIC_KEY
) == 0) {
432 *type
= kPublicKeyTypeECDSA
;
438 switch (oid_info
->Algid
) {
441 *type
= kPublicKeyTypeRSA
;
444 *type
= kPublicKeyTypeDSA
;
447 *type
= kPublicKeyTypeECDSA
;
450 *type
= kPublicKeyTypeECDH
;
455 bool X509Certificate::IsIssuedByEncoded(
456 const std::vector
<std::string
>& valid_issuers
) {
458 // If the certificate's issuer in the list?
459 if (IsCertNameBlobInIssuerList(&cert_handle_
->pCertInfo
->Issuer
,
463 // Otherwise, is any of the intermediate CA subjects in the list?
464 for (OSCertHandles::iterator it
= intermediate_ca_certs_
.begin();
465 it
!= intermediate_ca_certs_
.end(); ++it
) {
466 if (IsCertNameBlobInIssuerList(&(*it
)->pCertInfo
->Issuer
,