OLD | NEW |
1 // Copyright (c) 2010 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> | 7 #include <windows.h> |
8 #include <wincrypt.h> | 8 #include <wincrypt.h> |
9 #pragma comment(lib, "crypt32.lib") | 9 #pragma comment(lib, "crypt32.lib") |
10 #include <rpc.h> | 10 #include <rpc.h> |
11 #pragma comment(lib, "rpcrt4.lib") | 11 #pragma comment(lib, "rpcrt4.lib") |
12 | 12 |
13 #include <list> | 13 #include <list> |
14 #include <string> | 14 #include <string> |
15 #include <vector> | 15 #include <vector> |
16 | 16 |
17 #include "base/base64.h" | 17 #include "base/base64.h" |
18 #include "base/basictypes.h" | 18 #include "base/basictypes.h" |
19 #include "base/logging.h" | 19 #include "base/logging.h" |
20 #include "base/string_piece.h" | 20 #include "base/string_piece.h" |
| 21 #include "base/string_util.h" |
21 #include "base/utf_string_conversions.h" | 22 #include "base/utf_string_conversions.h" |
22 | 23 |
23 namespace net { | 24 namespace net { |
24 | 25 |
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, | 26 bool EncodeAndAppendType(LPCSTR type, const void* to_encode, |
58 std::vector<BYTE>* output) { | 27 std::vector<BYTE>* output) { |
59 BOOL ok; | 28 BOOL ok; |
60 DWORD size = 0; | 29 DWORD size = 0; |
61 ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode, NULL, &size); | 30 ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode, NULL, &size); |
62 DCHECK(ok); | 31 DCHECK(ok); |
63 if (!ok) | 32 if (!ok) |
64 return false; | 33 return false; |
65 | 34 |
66 std::vector<BYTE>::size_type old_size = output->size(); | 35 std::vector<BYTE>::size_type old_size = output->size(); |
67 output->resize(old_size + size); | 36 output->resize(old_size + size); |
68 | 37 |
69 ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode, | 38 ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode, |
70 &(*output)[old_size], &size); | 39 &(*output)[old_size], &size); |
71 DCHECK(ok); | 40 DCHECK(ok); |
72 if (!ok) | 41 if (!ok) |
73 return false; | 42 return false; |
74 | 43 |
75 // Sometimes the initial call to CryptEncodeObject gave a generous estimate | 44 // Sometimes the initial call to CryptEncodeObject gave a generous estimate |
76 // of the size, so shrink back to what was actually used. | 45 // of the size, so shrink back to what was actually used. |
77 output->resize(old_size + size); | 46 output->resize(old_size + size); |
78 | 47 |
79 return true; | 48 return true; |
80 } | 49 } |
81 | 50 |
82 // Appends a DER IA5String containing |challenge| to |output|. | 51 // Assigns the contents of a CERT_PUBLIC_KEY_INFO structure for the signing |
83 // Returns true if encoding was successful. | 52 // key in |prov| to |output|. Returns true if encoding was successful. |
84 bool EncodeChallenge(const std::string& challenge, std::vector<BYTE>* output) { | 53 bool GetSubjectPublicKeyInfo(HCRYPTPROV prov, 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.data())); | |
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; | 54 BOOL ok; |
99 DWORD size = 0; | 55 DWORD size = 0; |
100 | 56 |
101 // From the private key stored in HCRYPTPROV, obtain the public key, stored | 57 // 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 | 58 // as a CERT_PUBLIC_KEY_INFO structure. Currently, only RSA public keys are |
103 // supported. | 59 // supported. |
104 ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, | 60 ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, |
105 szOID_RSA_RSA, 0, NULL, NULL, &size); | 61 szOID_RSA_RSA, 0, NULL, NULL, &size); |
106 DCHECK(ok); | 62 DCHECK(ok); |
107 if (!ok) | 63 if (!ok) |
108 return false; | 64 return false; |
109 | 65 |
110 std::vector<BYTE> public_key_info(size); | 66 output->resize(size); |
| 67 |
111 PCERT_PUBLIC_KEY_INFO public_key_casted = | 68 PCERT_PUBLIC_KEY_INFO public_key_casted = |
112 reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&public_key_info[0]); | 69 reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&(*output)[0]); |
113 ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, | 70 ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, |
114 szOID_RSA_RSA, 0, NULL, public_key_casted, | 71 szOID_RSA_RSA, 0, NULL, public_key_casted, |
115 &size); | 72 &size); |
116 DCHECK(ok); | 73 DCHECK(ok); |
117 if (!ok) | 74 if (!ok) |
118 return false; | 75 return false; |
119 | 76 |
120 public_key_info.resize(size); | 77 output->resize(size); |
| 78 |
| 79 return true; |
| 80 } |
| 81 |
| 82 // Appends a DER SubjectPublicKeyInfo structure for the signing key in |prov| |
| 83 // to |output|. |
| 84 // Returns true if encoding was successful. |
| 85 bool EncodeSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) { |
| 86 std::vector<BYTE> public_key_info; |
| 87 if (!GetSubjectPublicKeyInfo(prov, &public_key_info)) |
| 88 return false; |
121 | 89 |
122 return EncodeAndAppendType(X509_PUBLIC_KEY_INFO, &public_key_info[0], | 90 return EncodeAndAppendType(X509_PUBLIC_KEY_INFO, &public_key_info[0], |
123 output); | 91 output); |
124 } | 92 } |
125 | 93 |
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 | 94 // Generates a DER encoded SignedPublicKeyAndChallenge structure from the |
142 // signing key of |prov| and the specified |challenge| string and appends it | 95 // signing key of |prov| and the specified ASCII |challenge| string and |
143 // to |output|. | 96 // appends it to |output|. |
144 // True if the encoding was successfully generated. | 97 // True if the encoding was successfully generated. |
145 bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov, | 98 bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov, |
146 const std::string& challenge, | 99 const std::string& challenge, |
147 std::string* output) { | 100 std::string* output) { |
148 std::vector<BYTE> pkac; | 101 std::wstring wide_challenge = ASCIIToWide(challenge); |
149 if (!GetPublicKeyAndChallenge(prov, challenge, &pkac)) | 102 std::vector<BYTE> spki; |
| 103 |
| 104 if (!GetSubjectPublicKeyInfo(prov, &spki)) |
150 return false; | 105 return false; |
151 | 106 |
152 std::vector<BYTE> signature; | 107 // PublicKeyAndChallenge ::= SEQUENCE { |
| 108 // spki SubjectPublicKeyInfo, |
| 109 // challenge IA5STRING |
| 110 // } |
| 111 CERT_KEYGEN_REQUEST_INFO pkac; |
| 112 pkac.dwVersion = CERT_KEYGEN_REQUEST_V1; |
| 113 pkac.SubjectPublicKeyInfo = |
| 114 *reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&spki[0]); |
| 115 pkac.pwszChallengeString = const_cast<wchar_t*>(wide_challenge.c_str()); |
| 116 |
| 117 CRYPT_ALGORITHM_IDENTIFIER sig_alg; |
| 118 memset(&sig_alg, 0, sizeof(sig_alg)); |
| 119 sig_alg.pszObjId = szOID_RSA_MD5RSA; |
| 120 |
| 121 BOOL ok; |
| 122 DWORD size = 0; |
153 std::vector<BYTE> signed_pkac; | 123 std::vector<BYTE> signed_pkac; |
154 DWORD size = 0; | 124 ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, |
155 BOOL ok; | 125 X509_KEYGEN_REQUEST_TO_BE_SIGNED, |
156 | 126 &pkac, &sig_alg, NULL, |
157 // While the MSDN documentation states that CERT_SIGNED_CONTENT_INFO should | 127 NULL, &size); |
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); | 128 DCHECK(ok); |
179 if (!ok) | 129 if (!ok) |
180 return false; | 130 return false; |
181 | 131 |
182 signature.resize(size); | 132 signed_pkac.resize(size); |
183 info.Signature.cbData = signature.size(); | 133 ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, |
184 info.Signature.pbData = &signature[0]; | 134 X509_KEYGEN_REQUEST_TO_BE_SIGNED, |
185 info.Signature.cUnusedBits = 0; | 135 &pkac, &sig_alg, NULL, |
186 | 136 &signed_pkac[0], &size); |
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); | 137 DCHECK(ok); |
192 if (!ok || !EncodeAndAppendType(X509_CERT, &info, &signed_pkac)) | 138 if (!ok) |
193 return false; | 139 return false; |
194 | 140 |
195 output->assign(reinterpret_cast<char*>(&signed_pkac[0]), | 141 output->assign(reinterpret_cast<char*>(&signed_pkac[0]), size); |
196 signed_pkac.size()); | |
197 | |
198 return true; | 142 return true; |
199 } | 143 } |
200 | 144 |
201 // Generates a unique name for the container which will store the key that is | 145 // 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. | 146 // generated. The traditional Windows approach is to use a GUID here. |
203 std::wstring GetNewKeyContainerId() { | 147 std::wstring GetNewKeyContainerId() { |
204 RPC_STATUS status = RPC_S_OK; | 148 RPC_STATUS status = RPC_S_OK; |
205 std::wstring result; | 149 std::wstring result; |
206 | 150 |
207 UUID id = { 0 }; | 151 UUID id = { 0 }; |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 // Fully destroys any of the keys that were created and releases prov. | 303 // Fully destroys any of the keys that were created and releases prov. |
360 CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL, | 304 CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL, |
361 CRYPT_SILENT | CRYPT_DELETEKEYSET); | 305 CRYPT_SILENT | CRYPT_DELETEKEYSET); |
362 } | 306 } |
363 } | 307 } |
364 | 308 |
365 return result; | 309 return result; |
366 } | 310 } |
367 | 311 |
368 } // namespace net | 312 } // namespace net |
OLD | NEW |