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