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 "net/base/net_errors.h"
19 #pragma comment(lib, "crypt32.lib")
27 typedef crypto::ScopedCAPIHandle
<
29 crypto::CAPIDestroyerWithFlags
<HCERTSTORE
,
30 CertCloseStore
, 0> > ScopedHCERTSTORE
;
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
;
44 //-----------------------------------------------------------------------------
46 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
47 // structure and stores it in *output.
48 void GetCertSubjectAltName(
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
);
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;
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
,
73 output
->reset(alt_name_info
);
76 void AddCertsFromStore(HCERTSTORE store
,
77 X509Certificate::OSCertHandles
* results
) {
78 PCCERT_CONTEXT cert
= NULL
;
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.
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
95 results
->insert(results
->begin(), to_add
);
100 X509Certificate::OSCertHandles
ParsePKCS7(const char* data
, size_t length
) {
101 X509Certificate::OSCertHandles results
;
103 data_blob
.cbData
= length
;
104 data_blob
.pbData
= reinterpret_cast<BYTE
*>(const_cast<char*>(data
));
106 HCERTSTORE out_store
= NULL
;
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
;
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
) {
118 AddCertsFromStore(out_store
, &results
);
119 CertCloseStore(out_store
, CERT_CLOSE_STORE_CHECK_FLAG
);
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.
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
;
134 reinterpret_cast<BYTE
*>(const_cast<char*>(it
->data()));
135 issuer_blob
.cbData
= static_cast<DWORD
>(it
->length());
137 BOOL rb
= CertCompareCertificateName(
138 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, &issuer_blob
, name_blob
);
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
);
154 valid_start_
= Time::FromFileTime(cert_handle_
->pCertInfo
->NotBefore
);
155 valid_expiry_
= Time::FromFileTime(cert_handle_
->pCertInfo
->NotAfter
);
157 fingerprint_
= CalculateFingerprint(cert_handle_
);
158 ca_fingerprint_
= CalculateCAFingerprint(intermediate_ca_certs_
);
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
);
168 void X509Certificate::GetSubjectAltName(
169 std::vector
<std::string
>* dns_names
,
170 std::vector
<std::string
>* ip_addrs
) const {
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();
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
];
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
));
201 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
));
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
,
221 if (!ok
|| !primary_cert
)
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
);
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.
236 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle
,
237 std::string
* encoded
) {
238 if (!cert_handle
->pbCertEncoded
|| !cert_handle
->cbCertEncoded
)
240 encoded
->assign(reinterpret_cast<char*>(cert_handle
->pbCertEncoded
),
241 cert_handle
->cbCertEncoded
);
246 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a
,
247 X509Certificate::OSCertHandle b
) {
251 return a
->cbCertEncoded
== b
->cbCertEncoded
&&
252 memcmp(a
->pbCertEncoded
, b
->pbCertEncoded
, a
->cbCertEncoded
) == 0;
256 X509Certificate::OSCertHandle
X509Certificate::CreateOSCertHandleFromBytes(
257 const char* data
, int length
) {
258 OSCertHandle cert_handle
= NULL
;
259 if (!CertAddEncodedCertificateToStore(
260 NULL
, X509_ASN_ENCODING
, reinterpret_cast<const BYTE
*>(data
),
261 length
, CERT_STORE_ADD_USE_EXISTING
, &cert_handle
))
267 X509Certificate::OSCertHandles
X509Certificate::CreateOSCertHandlesFromBytes(
268 const char* data
, int length
, Format format
) {
269 OSCertHandles results
;
271 case FORMAT_SINGLE_CERTIFICATE
: {
272 OSCertHandle handle
= CreateOSCertHandleFromBytes(data
, length
);
274 results
.push_back(handle
);
278 results
= ParsePKCS7(data
, length
);
281 NOTREACHED() << "Certificate format " << format
<< " unimplemented";
289 X509Certificate::OSCertHandle
X509Certificate::DupOSCertHandle(
290 OSCertHandle cert_handle
) {
291 return CertDuplicateCertificateContext(cert_handle
);
295 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle
) {
296 CertFreeCertificateContext(cert_handle
);
300 SHA1HashValue
X509Certificate::CalculateFingerprint(
302 DCHECK(NULL
!= cert
->pbCertEncoded
);
303 DCHECK_NE(static_cast<DWORD
>(0), cert
->cbCertEncoded
);
307 DWORD sha1_size
= sizeof(sha1
.data
);
308 rv
= CryptHashCertificate(NULL
, CALG_SHA1
, 0, cert
->pbCertEncoded
,
309 cert
->cbCertEncoded
, sha1
.data
, &sha1_size
);
310 DCHECK(rv
&& sha1_size
== sizeof(sha1
.data
));
312 memset(sha1
.data
, 0, sizeof(sha1
.data
));
316 // TODO(wtc): This function is implemented with NSS low-level hash
317 // functions to ensure it is fast. Reimplement this function with
318 // CryptoAPI. May need to cache the HCRYPTPROV to reduce the overhead.
320 SHA1HashValue
X509Certificate::CalculateCAFingerprint(
321 const OSCertHandles
& intermediates
) {
323 memset(sha1
.data
, 0, sizeof(sha1
.data
));
325 SHA1Context
* sha1_ctx
= SHA1_NewContext();
328 SHA1_Begin(sha1_ctx
);
329 for (size_t i
= 0; i
< intermediates
.size(); ++i
) {
330 PCCERT_CONTEXT ca_cert
= intermediates
[i
];
331 SHA1_Update(sha1_ctx
, ca_cert
->pbCertEncoded
, ca_cert
->cbCertEncoded
);
333 unsigned int result_len
;
334 SHA1_End(sha1_ctx
, sha1
.data
, &result_len
, SHA1_LENGTH
);
335 SHA1_DestroyContext(sha1_ctx
, PR_TRUE
);
341 X509Certificate::OSCertHandle
342 X509Certificate::ReadOSCertHandleFromPickle(PickleIterator
* pickle_iter
) {
345 if (!pickle_iter
->ReadData(&data
, &length
))
348 // Legacy serialized certificates were serialized with extended attributes,
349 // rather than as DER only. As a result, these serialized certificates are
350 // not portable across platforms and may have side-effects on Windows due
351 // to extended attributes being serialized/deserialized -
352 // http://crbug.com/118706. To avoid deserializing these attributes, write
353 // the deserialized cert into a temporary cert store and then create a new
354 // cert from the DER - that is, without attributes.
355 ScopedHCERTSTORE
store(
356 CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, NULL
, 0, NULL
));
360 OSCertHandle cert_handle
= NULL
;
361 if (!CertAddSerializedElementToStore(
362 store
.get(), reinterpret_cast<const BYTE
*>(data
), length
,
363 CERT_STORE_ADD_NEW
, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG
,
364 NULL
, reinterpret_cast<const void **>(&cert_handle
))) {
369 bool ok
= GetDEREncoded(cert_handle
, &encoded
);
370 FreeOSCertHandle(cert_handle
);
374 cert_handle
= CreateOSCertHandleFromBytes(encoded
.data(), encoded
.size());
379 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle
,
381 return pickle
->WriteData(
382 reinterpret_cast<char*>(cert_handle
->pbCertEncoded
),
383 cert_handle
->cbCertEncoded
);
387 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle
,
389 PublicKeyType
* type
) {
390 *type
= kPublicKeyTypeUnknown
;
393 PCCRYPT_OID_INFO oid_info
= CryptFindOIDInfo(
394 CRYPT_OID_INFO_OID_KEY
,
395 cert_handle
->pCertInfo
->SubjectPublicKeyInfo
.Algorithm
.pszObjId
,
396 CRYPT_PUBKEY_ALG_OID_GROUP_ID
);
400 CHECK_EQ(oid_info
->dwGroupId
,
401 static_cast<DWORD
>(CRYPT_PUBKEY_ALG_OID_GROUP_ID
));
403 *size_bits
= CertGetPublicKeyLength(
404 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
405 &cert_handle
->pCertInfo
->SubjectPublicKeyInfo
);
407 if (IS_SPECIAL_OID_INFO_ALGID(oid_info
->Algid
)) {
408 // For an EC public key, oid_info->Algid is CALG_OID_INFO_PARAMETERS
409 // (0xFFFFFFFE). Need to handle it as a special case.
410 if (strcmp(oid_info
->pszOID
, szOID_ECC_PUBLIC_KEY
) == 0) {
411 *type
= kPublicKeyTypeECDSA
;
417 switch (oid_info
->Algid
) {
420 *type
= kPublicKeyTypeRSA
;
423 *type
= kPublicKeyTypeDSA
;
426 *type
= kPublicKeyTypeECDSA
;
429 *type
= kPublicKeyTypeECDH
;
434 bool X509Certificate::IsIssuedByEncoded(
435 const std::vector
<std::string
>& valid_issuers
) {
437 // If the certificate's issuer in the list?
438 if (IsCertNameBlobInIssuerList(&cert_handle_
->pCertInfo
->Issuer
,
442 // Otherwise, is any of the intermediate CA subjects in the list?
443 for (OSCertHandles::iterator it
= intermediate_ca_certs_
.begin();
444 it
!= intermediate_ca_certs_
.end(); ++it
) {
445 if (IsCertNameBlobInIssuerList(&(*it
)->pCertInfo
->Issuer
,