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 //-----------------------------------------------------------------------------
40 // Decodes the cert's subjectAltName extension into a CERT_ALT_NAME_INFO
41 // structure and stores it in *output.
42 void GetCertSubjectAltName(
44 scoped_ptr
<CERT_ALT_NAME_INFO
, base::FreeDeleter
>* output
) {
45 PCERT_EXTENSION extension
= CertFindExtension(szOID_SUBJECT_ALT_NAME2
,
46 cert
->pCertInfo
->cExtension
,
47 cert
->pCertInfo
->rgExtension
);
51 CRYPT_DECODE_PARA decode_para
;
52 decode_para
.cbSize
= sizeof(decode_para
);
53 decode_para
.pfnAlloc
= crypto::CryptAlloc
;
54 decode_para
.pfnFree
= crypto::CryptFree
;
55 CERT_ALT_NAME_INFO
* alt_name_info
= NULL
;
56 DWORD alt_name_info_size
= 0;
58 rv
= CryptDecodeObjectEx(X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
59 szOID_SUBJECT_ALT_NAME2
,
60 extension
->Value
.pbData
,
61 extension
->Value
.cbData
,
62 CRYPT_DECODE_ALLOC_FLAG
| CRYPT_DECODE_NOCOPY_FLAG
,
67 output
->reset(alt_name_info
);
70 void AddCertsFromStore(HCERTSTORE store
,
71 X509Certificate::OSCertHandles
* results
) {
72 PCCERT_CONTEXT cert
= NULL
;
74 while ((cert
= CertEnumCertificatesInStore(store
, cert
)) != NULL
) {
75 PCCERT_CONTEXT to_add
= NULL
;
76 if (CertAddCertificateContextToStore(
77 NULL
, // The cert won't be persisted in any cert store. This breaks
78 // any association the context currently has to |store|, which
79 // allows us, the caller, to safely close |store| without
80 // releasing the cert handles.
82 CERT_STORE_ADD_USE_EXISTING
,
83 &to_add
) && to_add
!= NULL
) {
84 // When processing stores generated from PKCS#7/PKCS#12 files, it
85 // appears that the order returned is the inverse of the order that it
86 // appeared in the file.
87 // TODO(rsleevi): Ensure this order is consistent across all Win
89 results
->insert(results
->begin(), to_add
);
94 X509Certificate::OSCertHandles
ParsePKCS7(const char* data
, size_t length
) {
95 X509Certificate::OSCertHandles results
;
97 data_blob
.cbData
= length
;
98 data_blob
.pbData
= reinterpret_cast<BYTE
*>(const_cast<char*>(data
));
100 HCERTSTORE out_store
= NULL
;
102 DWORD expected_types
= CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
|
103 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED
|
104 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
;
106 if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &data_blob
, expected_types
,
107 CERT_QUERY_FORMAT_FLAG_BINARY
, 0, NULL
, NULL
, NULL
,
108 &out_store
, NULL
, NULL
) || out_store
== NULL
) {
112 AddCertsFromStore(out_store
, &results
);
113 CertCloseStore(out_store
, CERT_CLOSE_STORE_CHECK_FLAG
);
118 // Given a CERT_NAME_BLOB, returns true if it appears in a given list,
119 // formatted as a vector of strings holding DER-encoded X.509
120 // DistinguishedName entries.
121 bool IsCertNameBlobInIssuerList(
122 CERT_NAME_BLOB
* name_blob
,
123 const std::vector
<std::string
>& issuer_names
) {
124 for (std::vector
<std::string
>::const_iterator it
= issuer_names
.begin();
125 it
!= issuer_names
.end(); ++it
) {
126 CERT_NAME_BLOB issuer_blob
;
128 reinterpret_cast<BYTE
*>(const_cast<char*>(it
->data()));
129 issuer_blob
.cbData
= static_cast<DWORD
>(it
->length());
131 BOOL rb
= CertCompareCertificateName(
132 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, &issuer_blob
, name_blob
);
141 void X509Certificate::Initialize() {
142 DCHECK(cert_handle_
);
143 subject_
.ParseDistinguishedName(cert_handle_
->pCertInfo
->Subject
.pbData
,
144 cert_handle_
->pCertInfo
->Subject
.cbData
);
145 issuer_
.ParseDistinguishedName(cert_handle_
->pCertInfo
->Issuer
.pbData
,
146 cert_handle_
->pCertInfo
->Issuer
.cbData
);
148 valid_start_
= Time::FromFileTime(cert_handle_
->pCertInfo
->NotBefore
);
149 valid_expiry_
= Time::FromFileTime(cert_handle_
->pCertInfo
->NotAfter
);
151 fingerprint_
= CalculateFingerprint(cert_handle_
);
152 ca_fingerprint_
= CalculateCAFingerprint(intermediate_ca_certs_
);
154 const CRYPT_INTEGER_BLOB
* serial
= &cert_handle_
->pCertInfo
->SerialNumber
;
155 scoped_ptr
<uint8_t[]> serial_bytes(new uint8_t[serial
->cbData
]);
156 for (unsigned i
= 0; i
< serial
->cbData
; i
++)
157 serial_bytes
[i
] = serial
->pbData
[serial
->cbData
- i
- 1];
158 serial_number_
= std::string(
159 reinterpret_cast<char*>(serial_bytes
.get()), serial
->cbData
);
162 void X509Certificate::GetSubjectAltName(
163 std::vector
<std::string
>* dns_names
,
164 std::vector
<std::string
>* ip_addrs
) const {
173 scoped_ptr
<CERT_ALT_NAME_INFO
, base::FreeDeleter
> alt_name_info
;
174 GetCertSubjectAltName(cert_handle_
, &alt_name_info
);
175 CERT_ALT_NAME_INFO
* alt_name
= alt_name_info
.get();
177 int num_entries
= alt_name
->cAltEntry
;
178 for (int i
= 0; i
< num_entries
; i
++) {
179 // dNSName is an ASN.1 IA5String representing a string of ASCII
180 // characters, so we can use UTF16ToASCII here.
181 const CERT_ALT_NAME_ENTRY
& entry
= alt_name
->rgAltEntry
[i
];
183 if (dns_names
&& entry
.dwAltNameChoice
== CERT_ALT_NAME_DNS_NAME
) {
184 dns_names
->push_back(base::UTF16ToASCII(entry
.pwszDNSName
));
185 } else if (ip_addrs
&&
186 entry
.dwAltNameChoice
== CERT_ALT_NAME_IP_ADDRESS
) {
187 ip_addrs
->push_back(std::string(
188 reinterpret_cast<const char*>(entry
.IPAddress
.pbData
),
189 entry
.IPAddress
.cbData
));
195 PCCERT_CONTEXT
X509Certificate::CreateOSCertChainForCert() const {
196 // Create an in-memory certificate store to hold this certificate and
197 // any intermediate certificates in |intermediate_ca_certs_|. The store
198 // will be referenced in the returned PCCERT_CONTEXT, and will not be freed
199 // until the PCCERT_CONTEXT is freed.
200 ScopedHCERTSTORE
store(CertOpenStore(
201 CERT_STORE_PROV_MEMORY
, 0, NULL
,
202 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG
, NULL
));
206 // NOTE: This preserves all of the properties of |os_cert_handle()| except
207 // for CERT_KEY_PROV_HANDLE_PROP_ID and CERT_KEY_CONTEXT_PROP_ID - the two
208 // properties that hold access to already-opened private keys. If a handle
209 // has already been unlocked (eg: PIN prompt), then the first time that the
210 // identity is used for client auth, it may prompt the user again.
211 PCCERT_CONTEXT primary_cert
;
212 BOOL ok
= CertAddCertificateContextToStore(store
.get(), os_cert_handle(),
213 CERT_STORE_ADD_ALWAYS
,
215 if (!ok
|| !primary_cert
)
218 for (size_t i
= 0; i
< intermediate_ca_certs_
.size(); ++i
) {
219 CertAddCertificateContextToStore(store
.get(), intermediate_ca_certs_
[i
],
220 CERT_STORE_ADD_ALWAYS
, NULL
);
223 // Note: |store| is explicitly not released, as the call to CertCloseStore()
224 // when |store| goes out of scope will not actually free the store. Instead,
225 // the store will be freed when |primary_cert| is freed.
230 bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle
,
231 std::string
* encoded
) {
232 if (!cert_handle
|| !cert_handle
->pbCertEncoded
||
233 !cert_handle
->cbCertEncoded
) {
236 encoded
->assign(reinterpret_cast<char*>(cert_handle
->pbCertEncoded
),
237 cert_handle
->cbCertEncoded
);
242 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a
,
243 X509Certificate::OSCertHandle b
) {
247 return a
->cbCertEncoded
== b
->cbCertEncoded
&&
248 memcmp(a
->pbCertEncoded
, b
->pbCertEncoded
, a
->cbCertEncoded
) == 0;
252 X509Certificate::OSCertHandle
X509Certificate::CreateOSCertHandleFromBytes(
253 const char* data
, int length
) {
254 OSCertHandle cert_handle
= NULL
;
255 if (!CertAddEncodedCertificateToStore(
256 NULL
, X509_ASN_ENCODING
, reinterpret_cast<const BYTE
*>(data
),
257 length
, CERT_STORE_ADD_USE_EXISTING
, &cert_handle
))
263 X509Certificate::OSCertHandles
X509Certificate::CreateOSCertHandlesFromBytes(
264 const char* data
, int length
, Format format
) {
265 OSCertHandles results
;
267 case FORMAT_SINGLE_CERTIFICATE
: {
268 OSCertHandle handle
= CreateOSCertHandleFromBytes(data
, length
);
270 results
.push_back(handle
);
274 results
= ParsePKCS7(data
, length
);
277 NOTREACHED() << "Certificate format " << format
<< " unimplemented";
285 X509Certificate::OSCertHandle
X509Certificate::DupOSCertHandle(
286 OSCertHandle cert_handle
) {
287 return CertDuplicateCertificateContext(cert_handle
);
291 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle
) {
292 CertFreeCertificateContext(cert_handle
);
296 SHA1HashValue
X509Certificate::CalculateFingerprint(
298 DCHECK(NULL
!= cert
->pbCertEncoded
);
299 DCHECK_NE(static_cast<DWORD
>(0), cert
->cbCertEncoded
);
303 DWORD sha1_size
= sizeof(sha1
.data
);
304 rv
= CryptHashCertificate(NULL
, CALG_SHA1
, 0, cert
->pbCertEncoded
,
305 cert
->cbCertEncoded
, sha1
.data
, &sha1_size
);
306 DCHECK(rv
&& sha1_size
== sizeof(sha1
.data
));
308 memset(sha1
.data
, 0, sizeof(sha1
.data
));
313 SHA256HashValue
X509Certificate::CalculateFingerprint256(OSCertHandle cert
) {
314 DCHECK(NULL
!= cert
->pbCertEncoded
);
315 DCHECK_NE(0u, cert
->cbCertEncoded
);
317 SHA256HashValue sha256
;
318 size_t sha256_size
= sizeof(sha256
.data
);
320 // Use crypto::SHA256HashString for two reasons:
321 // * < Windows Vista does not have universal SHA-256 support.
322 // * More efficient on Windows > Vista (less overhead since non-default CSP
324 base::StringPiece
der_cert(reinterpret_cast<const char*>(cert
->pbCertEncoded
),
325 cert
->cbCertEncoded
);
326 crypto::SHA256HashString(der_cert
, sha256
.data
, sha256_size
);
330 SHA1HashValue
X509Certificate::CalculateCAFingerprint(
331 const OSCertHandles
& intermediates
) {
333 memset(sha1
.data
, 0, sizeof(sha1
.data
));
335 #if defined(USE_OPENSSL)
337 if (!SHA1_Init(&ctx
))
339 for (size_t i
= 0; i
< intermediates
.size(); ++i
) {
340 PCCERT_CONTEXT ca_cert
= intermediates
[i
];
341 if (!SHA1_Update(&ctx
, ca_cert
->pbCertEncoded
, ca_cert
->cbCertEncoded
))
344 SHA1_Final(sha1
.data
, &ctx
);
345 #else // !USE_OPENSSL
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
);
357 #endif // USE_OPENSSL
363 X509Certificate::OSCertHandle
X509Certificate::ReadOSCertHandleFromPickle(
364 base::PickleIterator
* pickle_iter
) {
367 if (!pickle_iter
->ReadData(&data
, &length
))
370 // Legacy serialized certificates were serialized with extended attributes,
371 // rather than as DER only. As a result, these serialized certificates are
372 // not portable across platforms and may have side-effects on Windows due
373 // to extended attributes being serialized/deserialized -
374 // http://crbug.com/118706. To avoid deserializing these attributes, write
375 // the deserialized cert into a temporary cert store and then create a new
376 // cert from the DER - that is, without attributes.
377 ScopedHCERTSTORE
store(
378 CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, NULL
, 0, NULL
));
382 OSCertHandle cert_handle
= NULL
;
383 if (!CertAddSerializedElementToStore(
384 store
.get(), reinterpret_cast<const BYTE
*>(data
), length
,
385 CERT_STORE_ADD_NEW
, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG
,
386 NULL
, reinterpret_cast<const void **>(&cert_handle
))) {
391 bool ok
= GetDEREncoded(cert_handle
, &encoded
);
392 FreeOSCertHandle(cert_handle
);
396 cert_handle
= CreateOSCertHandleFromBytes(encoded
.data(), encoded
.size());
401 bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle
,
402 base::Pickle
* pickle
) {
403 return pickle
->WriteData(
404 reinterpret_cast<char*>(cert_handle
->pbCertEncoded
),
405 cert_handle
->cbCertEncoded
);
409 void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle
,
411 PublicKeyType
* type
) {
412 *type
= kPublicKeyTypeUnknown
;
415 PCCRYPT_OID_INFO oid_info
= CryptFindOIDInfo(
416 CRYPT_OID_INFO_OID_KEY
,
417 cert_handle
->pCertInfo
->SubjectPublicKeyInfo
.Algorithm
.pszObjId
,
418 CRYPT_PUBKEY_ALG_OID_GROUP_ID
);
422 CHECK_EQ(oid_info
->dwGroupId
,
423 static_cast<DWORD
>(CRYPT_PUBKEY_ALG_OID_GROUP_ID
));
425 *size_bits
= CertGetPublicKeyLength(
426 X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
,
427 &cert_handle
->pCertInfo
->SubjectPublicKeyInfo
);
429 if (IS_SPECIAL_OID_INFO_ALGID(oid_info
->Algid
)) {
430 // For an EC public key, oid_info->Algid is CALG_OID_INFO_PARAMETERS
431 // (0xFFFFFFFE). Need to handle it as a special case.
432 if (strcmp(oid_info
->pszOID
, szOID_ECC_PUBLIC_KEY
) == 0) {
433 *type
= kPublicKeyTypeECDSA
;
439 switch (oid_info
->Algid
) {
442 *type
= kPublicKeyTypeRSA
;
445 *type
= kPublicKeyTypeDSA
;
448 *type
= kPublicKeyTypeECDSA
;
451 *type
= kPublicKeyTypeECDH
;
456 bool X509Certificate::IsIssuedByEncoded(
457 const std::vector
<std::string
>& valid_issuers
) {
459 // If the certificate's issuer in the list?
460 if (IsCertNameBlobInIssuerList(&cert_handle_
->pCertInfo
->Issuer
,
464 // Otherwise, is any of the intermediate CA subjects in the list?
465 for (OSCertHandles::iterator it
= intermediate_ca_certs_
.begin();
466 it
!= intermediate_ca_certs_
.end(); ++it
) {
467 if (IsCertNameBlobInIssuerList(&(*it
)->pCertInfo
->Issuer
,
477 bool X509Certificate::IsSelfSigned(OSCertHandle cert_handle
) {
478 return !!CryptVerifyCertificateSignatureEx(
481 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT
,
482 reinterpret_cast<void*>(const_cast<PCERT_CONTEXT
>(cert_handle
)),
483 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT
,
484 reinterpret_cast<void*>(const_cast<PCERT_CONTEXT
>(cert_handle
)),