1 // Copyright 2013 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/ssl/client_cert_store_win.h"
10 #define SECURITY_WIN32 // Needs to be defined before including security.h
14 #include "base/callback.h"
15 #include "base/logging.h"
16 #include "crypto/scoped_capi_types.h"
17 #include "crypto/wincrypt_shim.h"
18 #include "net/cert/x509_util.h"
24 // Callback required by Windows API function CertFindChainInStore(). In addition
25 // to filtering by extended/enhanced key usage, we do not show expired
26 // certificates and require digital signature usage in the key usage extension.
28 // This matches our behavior on Mac OS X and that of NSS. It also matches the
29 // default behavior of IE8. See http://support.microsoft.com/kb/890326 and
30 // http://blogs.msdn.com/b/askie/archive/2009/06/09/my-expired-client-certifica
31 // tes-no-longer-display-when-connecting-to-my-web-server-using-ie8.aspx
32 static BOOL WINAPI
ClientCertFindCallback(PCCERT_CONTEXT cert_context
,
34 // Verify the certificate key usage is appropriate or not specified.
36 if (CertGetIntendedKeyUsage(X509_ASN_ENCODING
, cert_context
->pCertInfo
,
38 if (!(key_usage
& CERT_DIGITAL_SIGNATURE_KEY_USAGE
))
41 DWORD err
= GetLastError();
42 // If |err| is non-zero, it's an actual error. Otherwise the extension
43 // just isn't present, and we treat it as if everything was allowed.
45 DLOG(ERROR
) << "CertGetIntendedKeyUsage failed: " << err
;
50 // Verify the current time is within the certificate's validity period.
51 if (CertVerifyTimeValidity(NULL
, cert_context
->pCertInfo
) != 0)
54 // Verify private key metadata is associated with this certificate.
55 // TODO(ppi): Is this really needed? Isn't it equivalent to leaving
56 // CERT_CHAIN_FIND_BY_ISSUER_NO_KEY_FLAG not set in |find_flags| argument of
57 // CertFindChainInStore()?
59 if (!CertGetCertificateContextProperty(
60 cert_context
, CERT_KEY_PROV_INFO_PROP_ID
, NULL
, &size
)) {
67 void GetClientCertsImpl(HCERTSTORE cert_store
,
68 const SSLCertRequestInfo
& request
,
69 CertificateList
* selected_certs
) {
70 selected_certs
->clear();
72 const size_t auth_count
= request
.cert_authorities
.size();
73 std::vector
<CERT_NAME_BLOB
> issuers(auth_count
);
74 for (size_t i
= 0; i
< auth_count
; ++i
) {
75 issuers
[i
].cbData
= static_cast<DWORD
>(request
.cert_authorities
[i
].size());
76 issuers
[i
].pbData
= reinterpret_cast<BYTE
*>(
77 const_cast<char*>(request
.cert_authorities
[i
].data()));
80 // Enumerate the client certificates.
81 CERT_CHAIN_FIND_BY_ISSUER_PARA find_by_issuer_para
;
82 memset(&find_by_issuer_para
, 0, sizeof(find_by_issuer_para
));
83 find_by_issuer_para
.cbSize
= sizeof(find_by_issuer_para
);
84 find_by_issuer_para
.pszUsageIdentifier
= szOID_PKIX_KP_CLIENT_AUTH
;
85 find_by_issuer_para
.cIssuer
= static_cast<DWORD
>(auth_count
);
86 find_by_issuer_para
.rgIssuer
=
87 reinterpret_cast<CERT_NAME_BLOB
*>(issuers
.data());
88 find_by_issuer_para
.pfnFindCallback
= ClientCertFindCallback
;
90 PCCERT_CHAIN_CONTEXT chain_context
= NULL
;
91 DWORD find_flags
= CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG
|
92 CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG
;
94 // Find a certificate chain.
95 chain_context
= CertFindChainInStore(cert_store
,
98 CERT_CHAIN_FIND_BY_ISSUER
,
101 if (!chain_context
) {
102 if (GetLastError() != CRYPT_E_NOT_FOUND
)
103 DPLOG(ERROR
) << "CertFindChainInStore failed: ";
107 // Get the leaf certificate.
108 PCCERT_CONTEXT cert_context
=
109 chain_context
->rgpChain
[0]->rgpElement
[0]->pCertContext
;
110 // Copy the certificate, so that it is valid after |cert_store| is closed.
111 PCCERT_CONTEXT cert_context2
= NULL
;
112 BOOL ok
= CertAddCertificateContextToStore(NULL
, cert_context
,
113 CERT_STORE_ADD_USE_EXISTING
,
120 // Grab the intermediates, if any.
121 X509Certificate::OSCertHandles intermediates
;
122 for (DWORD i
= 1; i
< chain_context
->rgpChain
[0]->cElement
; ++i
) {
123 PCCERT_CONTEXT chain_intermediate
=
124 chain_context
->rgpChain
[0]->rgpElement
[i
]->pCertContext
;
125 PCCERT_CONTEXT copied_intermediate
= NULL
;
126 ok
= CertAddCertificateContextToStore(NULL
, chain_intermediate
,
127 CERT_STORE_ADD_USE_EXISTING
,
128 &copied_intermediate
);
130 intermediates
.push_back(copied_intermediate
);
132 scoped_refptr
<X509Certificate
> cert
= X509Certificate::CreateFromHandle(
133 cert_context2
, intermediates
);
134 selected_certs
->push_back(cert
);
135 CertFreeCertificateContext(cert_context2
);
136 for (size_t i
= 0; i
< intermediates
.size(); ++i
)
137 CertFreeCertificateContext(intermediates
[i
]);
140 std::sort(selected_certs
->begin(), selected_certs
->end(),
141 x509_util::ClientCertSorter());
146 ClientCertStoreWin::ClientCertStoreWin() {}
148 ClientCertStoreWin::~ClientCertStoreWin() {}
150 void ClientCertStoreWin::GetClientCerts(const SSLCertRequestInfo
& request
,
151 CertificateList
* selected_certs
,
152 const base::Closure
& callback
) {
153 // Client certificates of the user are in the "MY" system certificate store.
154 HCERTSTORE my_cert_store
= CertOpenSystemStore(NULL
, L
"MY");
155 if (!my_cert_store
) {
156 PLOG(ERROR
) << "Could not open the \"MY\" system certificate store: ";
157 selected_certs
->clear();
162 GetClientCertsImpl(my_cert_store
, request
, selected_certs
);
163 if (!CertCloseStore(my_cert_store
, CERT_CLOSE_STORE_CHECK_FLAG
))
164 PLOG(ERROR
) << "Could not close the \"MY\" system certificate store: ";
168 bool ClientCertStoreWin::SelectClientCertsForTesting(
169 const CertificateList
& input_certs
,
170 const SSLCertRequestInfo
& request
,
171 CertificateList
* selected_certs
) {
172 typedef crypto::ScopedCAPIHandle
<
174 crypto::CAPIDestroyerWithFlags
<HCERTSTORE
,
175 CertCloseStore
, 0> > ScopedHCERTSTORE
;
177 ScopedHCERTSTORE
test_store(CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, NULL
, 0,
182 // Add available certificates to the test store.
183 for (size_t i
= 0; i
< input_certs
.size(); ++i
) {
184 // Add the certificate to the test store.
185 PCCERT_CONTEXT cert
= NULL
;
186 if (!CertAddCertificateContextToStore(test_store
,
187 input_certs
[i
]->os_cert_handle(),
188 CERT_STORE_ADD_NEW
, &cert
)) {
191 // Add dummy private key data to the certificate - otherwise the certificate
192 // would be discarded by the filtering routines.
193 CRYPT_KEY_PROV_INFO private_key_data
;
194 memset(&private_key_data
, 0, sizeof(private_key_data
));
195 if (!CertSetCertificateContextProperty(cert
,
196 CERT_KEY_PROV_INFO_PROP_ID
,
197 0, &private_key_data
)) {
200 // Decrement the reference count of the certificate (since we requested a
202 if (!CertFreeCertificateContext(cert
))
206 GetClientCertsImpl(test_store
.get(), request
, selected_certs
);