Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: net/base/keygen_handler_win.cc

Issue 1591006: Adds support for the <keygen> element to Windows, matching... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Fix keygen_handler_nss.cc compilation errors. Fix more nits. Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/base/keygen_handler_unittest.cc ('k') | net/base/net_error_list.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/base/keygen_handler.h" 5 #include "net/base/keygen_handler.h"
6 6
7 #include <windows.h>
8 #include <wincrypt.h>
9 #pragma comment(lib, "crypt32.lib")
10 #include <rpc.h>
11 #pragma comment(lib, "rpcrt4.lib")
12
13 #include <list>
14 #include <string>
15 #include <vector>
16
17 #include "base/base64.h"
18 #include "base/basictypes.h"
7 #include "base/logging.h" 19 #include "base/logging.h"
20 #include "base/string_piece.h"
21 #include "base/utf_string_conversions.h"
8 22
9 namespace net { 23 namespace net {
10 24
25 // TODO(rsleevi): The following encoding functions are adapted from
26 // base/crypto/rsa_private_key.h and can/should probably be refactored.
27 static const uint8 kSequenceTag = 0x30;
28
29 void PrependLength(size_t size, std::list<BYTE>* data) {
30 // The high bit is used to indicate whether additional octets are needed to
31 // represent the length.
32 if (size < 0x80) {
33 data->push_front(static_cast<BYTE>(size));
34 } else {
35 uint8 num_bytes = 0;
36 while (size > 0) {
37 data->push_front(static_cast<BYTE>(size & 0xFF));
38 size >>= 8;
39 num_bytes++;
40 }
41 CHECK_LE(num_bytes, 4);
42 data->push_front(0x80 | num_bytes);
43 }
44 }
45
46 void PrependTypeHeaderAndLength(uint8 type, uint32 length,
47 std::vector<BYTE>* output) {
48 std::list<BYTE> type_and_length;
49
50 PrependLength(length, &type_and_length);
51 type_and_length.push_front(type);
52
53 output->insert(output->begin(), type_and_length.begin(),
54 type_and_length.end());
55 }
56
57 bool EncodeAndAppendType(LPCSTR type, const void* to_encode,
58 std::vector<BYTE>* output) {
59 BOOL ok;
60 DWORD size = 0;
61 ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode, NULL, &size);
62 DCHECK(ok);
63 if (!ok)
64 return false;
65
66 std::vector<BYTE>::size_type old_size = output->size();
67 output->resize(old_size + size);
68
69 ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode,
70 &(*output)[old_size], &size);
71 DCHECK(ok);
72 if (!ok)
73 return false;
74
75 // Sometimes the initial call to CryptEncodeObject gave a generous estimate
76 // of the size, so shrink back to what was actually used.
77 output->resize(old_size + size);
78
79 return true;
80 }
81
82 // Appends a DER IA5String containing |challenge| to |output|.
83 // Returns true if encoding was successful.
84 bool EncodeChallenge(const std::string& challenge, std::vector<BYTE>* output) {
85 CERT_NAME_VALUE challenge_nv;
86 challenge_nv.dwValueType = CERT_RDN_IA5_STRING;
87 challenge_nv.Value.pbData = const_cast<BYTE*>(
88 reinterpret_cast<const BYTE*>(challenge.c_str()));
89 challenge_nv.Value.cbData = challenge.size();
90
91 return EncodeAndAppendType(X509_ANY_STRING, &challenge_nv, output);
92 }
93
94 // Appends a DER SubjectPublicKeyInfo structure for the signing key in |prov|
95 // to |output|.
96 // Returns true if encoding was successful.
97 bool EncodeSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) {
98 BOOL ok;
99 DWORD size = 0;
100
101 // From the private key stored in HCRYPTPROV, obtain the public key, stored
102 // as a CERT_PUBLIC_KEY_INFO structure. Currently, only RSA public keys are
103 // supported.
104 ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
105 szOID_RSA_RSA, 0, NULL, NULL, &size);
106 DCHECK(ok);
107 if (!ok)
108 return false;
109
110 std::vector<BYTE> public_key_info(size);
111 PCERT_PUBLIC_KEY_INFO public_key_casted =
112 reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&public_key_info[0]);
113 ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
114 szOID_RSA_RSA, 0, NULL, public_key_casted,
115 &size);
116 DCHECK(ok);
117 if (!ok)
118 return false;
119
120 public_key_info.resize(size);
121
122 return EncodeAndAppendType(X509_PUBLIC_KEY_INFO, &public_key_info[0],
123 output);
124 }
125
126 // Generates an ASN.1 DER representation of the PublicKeyAndChallenge structure
127 // from the signing key of |prov| and the specified |challenge| and appends it
128 // to |output|.
129 // True if the the encoding was successfully generated.
130 bool GetPublicKeyAndChallenge(HCRYPTPROV prov, const std::string& challenge,
131 std::vector<BYTE>* output) {
132 if (!EncodeSubjectPublicKeyInfo(prov, output) ||
133 !EncodeChallenge(challenge, output)) {
134 return false;
135 }
136
137 PrependTypeHeaderAndLength(kSequenceTag, output->size(), output);
138 return true;
139 }
140
141 // Generates a DER encoded SignedPublicKeyAndChallenge structure from the
142 // signing key of |prov| and the specified |challenge| string and appends it
143 // to |output|.
144 // True if the encoding was successfully generated.
145 bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov,
146 const std::string& challenge,
147 std::string* output) {
148 std::vector<BYTE> pkac;
149 if (!GetPublicKeyAndChallenge(prov, challenge, &pkac))
150 return false;
151
152 std::vector<BYTE> signature;
153 std::vector<BYTE> signed_pkac;
154 DWORD size = 0;
155 BOOL ok;
156
157 // While the MSDN documentation states that CERT_SIGNED_CONTENT_INFO should
158 // be an X.509 certificate type, for encoding this is not necessary. The
159 // result of encoding this structure will be a DER-encoded structure with
160 // the ASN.1 equivalent of
161 // ::= SEQUENCE {
162 // ToBeSigned IMPLICIT OCTET STRING,
163 // SignatureAlgorithm AlgorithmIdentifier,
164 // Signature BIT STRING
165 // }
166 //
167 // This happens to be the same naive type as an SPKAC, so this works.
168 CERT_SIGNED_CONTENT_INFO info;
169 info.ToBeSigned.cbData = pkac.size();
170 info.ToBeSigned.pbData = &pkac[0];
171 info.SignatureAlgorithm.pszObjId = szOID_RSA_MD5RSA;
172 info.SignatureAlgorithm.Parameters.cbData = 0;
173 info.SignatureAlgorithm.Parameters.pbData = NULL;
174
175 ok = CryptSignCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
176 info.ToBeSigned.pbData, info.ToBeSigned.cbData,
177 &info.SignatureAlgorithm, NULL, NULL, &size);
178 DCHECK(ok);
179 if (!ok)
180 return false;
181
182 signature.resize(size);
183 info.Signature.cbData = signature.size();
184 info.Signature.pbData = &signature[0];
185 info.Signature.cUnusedBits = 0;
186
187 ok = CryptSignCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
188 info.ToBeSigned.pbData, info.ToBeSigned.cbData,
189 &info.SignatureAlgorithm, NULL,
190 info.Signature.pbData, &info.Signature.cbData);
191 DCHECK(ok);
192 if (!ok || !EncodeAndAppendType(X509_CERT, &info, &signed_pkac))
193 return false;
194
195 output->assign(reinterpret_cast<char*>(&signed_pkac[0]),
196 signed_pkac.size());
197
198 return true;
199 }
200
201 // Generates a unique name for the container which will store the key that is
202 // generated. The traditional Windows approach is to use a GUID here.
203 std::wstring GetNewKeyContainerId() {
204 RPC_STATUS status = RPC_S_OK;
205 std::wstring result;
206
207 UUID id = { 0 };
208 status = UuidCreateSequential(&id);
209 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY)
210 return result;
211
212 RPC_WSTR rpc_string = NULL;
213 status = UuidToString(&id, &rpc_string);
214 if (status != RPC_S_OK)
215 return result;
216
217 // RPC_WSTR is unsigned short*. wchar_t is a built-in type of Visual C++,
218 // so the type cast is necessary.
219 result.assign(reinterpret_cast<wchar_t*>(rpc_string));
220 RpcStringFree(&rpc_string);
221
222 return result;
223 }
224
225 void StoreKeyLocationInCache(HCRYPTPROV prov) {
226 BOOL ok;
227 DWORD size = 0;
228
229 // Though it is known the container and provider name, as they are supplied
230 // during GenKeyAndSignChallenge, explicitly resolving them via
231 // CryptGetProvParam ensures that any defaults (such as provider name being
232 // NULL) or any CSP modifications to the container name are properly
233 // reflected.
234
235 // Find the container name. Though the MSDN documentation states it will
236 // return the exact same value as supplied when the provider was aquired, it
237 // also notes the return type will be CHAR, /not/ WCHAR.
238 ok = CryptGetProvParam(prov, PP_CONTAINER, NULL, &size, 0);
239 if (!ok)
240 return;
241
242 std::vector<BYTE> buffer(size);
243 ok = CryptGetProvParam(prov, PP_CONTAINER, &buffer[0], &size, 0);
244 if (!ok)
245 return;
246
247 KeygenHandler::KeyLocation key_location;
248 UTF8ToWide(reinterpret_cast<char*>(&buffer[0]), size,
249 &key_location.container_name);
250
251 // Get the provider name. This will always resolve, even if NULL (indicating
252 // the default provider) was supplied to the CryptAcquireContext.
253 size = 0;
254 ok = CryptGetProvParam(prov, PP_NAME, NULL, &size, 0);
255 if (!ok)
256 return;
257
258 buffer.resize(size);
259 ok = CryptGetProvParam(prov, PP_NAME, &buffer[0], &size, 0);
260 if (!ok)
261 return;
262
263 UTF8ToWide(reinterpret_cast<char*>(&buffer[0]), size,
264 &key_location.provider_name);
265
266 std::vector<BYTE> public_key_info;
267 if (!EncodeSubjectPublicKeyInfo(prov, &public_key_info))
268 return;
269
270 KeygenHandler::Cache* cache = KeygenHandler::Cache::GetInstance();
271 cache->Insert(std::string(public_key_info.begin(), public_key_info.end()),
272 key_location);
273 }
274
275 bool KeygenHandler::KeyLocation::Equals(
276 const KeygenHandler::KeyLocation& location) const {
277 return container_name == location.container_name &&
278 provider_name == location.provider_name;
279 }
280
11 std::string KeygenHandler::GenKeyAndSignChallenge() { 281 std::string KeygenHandler::GenKeyAndSignChallenge() {
12 NOTIMPLEMENTED(); 282 std::string result;
13 return std::string(); 283
284 bool is_success = true;
285 HCRYPTPROV prov = NULL;
286 HCRYPTKEY key = NULL;
287 DWORD flags = (key_size_in_bits_ << 16) | CRYPT_EXPORTABLE;
288 std::string spkac;
289
290 std::wstring new_key_id;
291
292 // TODO(rsleevi): Have the user choose which provider they should use, which
293 // needs to be filtered by those providers which can provide the key type
294 // requested or the key size requested. This is especially important for
295 // generating certificates that will be stored on smart cards.
296 const int kMaxAttempts = 5;
297 BOOL ok = FALSE;
298 for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
299 // Per MSDN documentation for CryptAcquireContext, if applications will be
300 // creating their own keys, they should ensure unique naming schemes to
301 // prevent overlap with any other applications or consumers of CSPs, and
302 // *should not* store new keys within the default, NULL key container.
303 new_key_id = GetNewKeyContainerId();
304 if (new_key_id.empty())
305 return result;
306
307 // Only create new key containers, so that existing key containers are not
308 // overwritten.
309 ok = CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL,
310 CRYPT_SILENT | CRYPT_NEWKEYSET);
311
312 if (ok || GetLastError() != NTE_BAD_KEYSET)
313 break;
314 }
315 if (!ok) {
316 LOG(ERROR) << "Couldn't acquire a CryptoAPI provider context: "
317 << GetLastError();
318 is_success = false;
319 goto failure;
320 }
321
322 if (!CryptGenKey(prov, CALG_RSA_KEYX, flags, &key)) {
323 LOG(ERROR) << "Couldn't generate an RSA key";
324 is_success = false;
325 goto failure;
326 }
327
328 if (!GetSignedPublicKeyAndChallenge(prov, challenge_, &spkac)) {
329 LOG(ERROR) << "Couldn't generate the signed public key and challenge";
330 is_success = false;
331 goto failure;
332 }
333
334 if (!base::Base64Encode(spkac, &result)) {
335 LOG(ERROR) << "Couldn't convert signed key into base64";
336 is_success = false;
337 goto failure;
338 }
339
340 StoreKeyLocationInCache(prov);
341
342 failure:
343 if (!is_success) {
344 LOG(ERROR) << "SSL Keygen failed";
345 } else {
346 LOG(INFO) << "SSL Key succeeded";
347 }
348 if (key) {
349 // Securely destroys the handle, but leaves the underlying key alone. The
350 // key can be obtained again by resolving the key location. If
351 // |stores_key_| is false, the underlying key will be destroyed below.
352 CryptDestroyKey(key);
353 }
354
355 if (prov) {
356 CryptReleaseContext(prov, 0);
357 prov = NULL;
358 if (!stores_key_) {
359 // Fully destroys any of the keys that were created and releases prov.
360 CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL,
361 CRYPT_SILENT | CRYPT_DELETEKEYSET);
362 }
363 }
364
365 return result;
14 } 366 }
15 367
16 } // namespace net 368 } // namespace net
OLDNEW
« no previous file with comments | « net/base/keygen_handler_unittest.cc ('k') | net/base/net_error_list.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698