OLD | NEW |
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 |
OLD | NEW |