| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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. | |
| 4 | |
| 5 #include "net/base/keygen_handler.h" | |
| 6 | |
| 7 #include <windows.h> | |
| 8 #include <rpc.h> | |
| 9 | |
| 10 #include <list> | |
| 11 #include <string> | |
| 12 #include <vector> | |
| 13 | |
| 14 #include "base/base64.h" | |
| 15 #include "base/logging.h" | |
| 16 #include "base/strings/string_piece.h" | |
| 17 #include "base/strings/string_util.h" | |
| 18 #include "base/strings/utf_string_conversions.h" | |
| 19 #include "crypto/capi_util.h" | |
| 20 #include "crypto/scoped_capi_types.h" | |
| 21 #include "crypto/wincrypt_shim.h" | |
| 22 | |
| 23 namespace net { | |
| 24 | |
| 25 // Assigns the contents of a CERT_PUBLIC_KEY_INFO structure for the signing | |
| 26 // key in |prov| to |output|. Returns true if encoding was successful. | |
| 27 bool GetSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) { | |
| 28 BOOL ok; | |
| 29 DWORD size = 0; | |
| 30 | |
| 31 // From the private key stored in HCRYPTPROV, obtain the public key, stored | |
| 32 // as a CERT_PUBLIC_KEY_INFO structure. Currently, only RSA public keys are | |
| 33 // supported. | |
| 34 ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, | |
| 35 const_cast<char*>(szOID_RSA_RSA), 0, NULL, | |
| 36 NULL, &size); | |
| 37 DCHECK(ok); | |
| 38 if (!ok) | |
| 39 return false; | |
| 40 | |
| 41 output->resize(size); | |
| 42 | |
| 43 PCERT_PUBLIC_KEY_INFO public_key_casted = | |
| 44 reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&(*output)[0]); | |
| 45 ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, | |
| 46 const_cast<char*>(szOID_RSA_RSA), 0, NULL, | |
| 47 public_key_casted, &size); | |
| 48 DCHECK(ok); | |
| 49 if (!ok) | |
| 50 return false; | |
| 51 | |
| 52 output->resize(size); | |
| 53 | |
| 54 return true; | |
| 55 } | |
| 56 | |
| 57 // Generates a DER encoded SignedPublicKeyAndChallenge structure from the | |
| 58 // signing key of |prov| and the specified ASCII |challenge| string and | |
| 59 // appends it to |output|. | |
| 60 // True if the encoding was successfully generated. | |
| 61 bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov, | |
| 62 const std::string& challenge, | |
| 63 std::string* output) { | |
| 64 base::string16 challenge16 = base::ASCIIToUTF16(challenge); | |
| 65 std::vector<BYTE> spki; | |
| 66 | |
| 67 if (!GetSubjectPublicKeyInfo(prov, &spki)) | |
| 68 return false; | |
| 69 | |
| 70 // PublicKeyAndChallenge ::= SEQUENCE { | |
| 71 // spki SubjectPublicKeyInfo, | |
| 72 // challenge IA5STRING | |
| 73 // } | |
| 74 CERT_KEYGEN_REQUEST_INFO pkac; | |
| 75 pkac.dwVersion = CERT_KEYGEN_REQUEST_V1; | |
| 76 pkac.SubjectPublicKeyInfo = | |
| 77 *reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&spki[0]); | |
| 78 pkac.pwszChallengeString = const_cast<base::char16*>(challenge16.c_str()); | |
| 79 | |
| 80 CRYPT_ALGORITHM_IDENTIFIER sig_alg; | |
| 81 memset(&sig_alg, 0, sizeof(sig_alg)); | |
| 82 sig_alg.pszObjId = const_cast<char*>(szOID_RSA_MD5RSA); | |
| 83 | |
| 84 BOOL ok; | |
| 85 DWORD size = 0; | |
| 86 std::vector<BYTE> signed_pkac; | |
| 87 ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, | |
| 88 X509_KEYGEN_REQUEST_TO_BE_SIGNED, | |
| 89 &pkac, &sig_alg, NULL, | |
| 90 NULL, &size); | |
| 91 DCHECK(ok); | |
| 92 if (!ok) | |
| 93 return false; | |
| 94 | |
| 95 signed_pkac.resize(size); | |
| 96 ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, | |
| 97 X509_KEYGEN_REQUEST_TO_BE_SIGNED, | |
| 98 &pkac, &sig_alg, NULL, | |
| 99 &signed_pkac[0], &size); | |
| 100 DCHECK(ok); | |
| 101 if (!ok) | |
| 102 return false; | |
| 103 | |
| 104 output->assign(reinterpret_cast<char*>(&signed_pkac[0]), size); | |
| 105 return true; | |
| 106 } | |
| 107 | |
| 108 // Generates a unique name for the container which will store the key that is | |
| 109 // generated. The traditional Windows approach is to use a GUID here. | |
| 110 std::wstring GetNewKeyContainerId() { | |
| 111 RPC_STATUS status = RPC_S_OK; | |
| 112 std::wstring result; | |
| 113 | |
| 114 UUID id = { 0 }; | |
| 115 status = UuidCreateSequential(&id); | |
| 116 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) | |
| 117 return result; | |
| 118 | |
| 119 RPC_WSTR rpc_string = NULL; | |
| 120 status = UuidToString(&id, &rpc_string); | |
| 121 if (status != RPC_S_OK) | |
| 122 return result; | |
| 123 | |
| 124 // RPC_WSTR is unsigned short*. wchar_t is a built-in type of Visual C++, | |
| 125 // so the type cast is necessary. | |
| 126 result.assign(reinterpret_cast<wchar_t*>(rpc_string)); | |
| 127 RpcStringFree(&rpc_string); | |
| 128 | |
| 129 return result; | |
| 130 } | |
| 131 | |
| 132 // This is a helper struct designed to optionally delete a key after releasing | |
| 133 // the associated provider. | |
| 134 struct KeyContainer { | |
| 135 public: | |
| 136 explicit KeyContainer(bool delete_keyset) | |
| 137 : delete_keyset_(delete_keyset) {} | |
| 138 | |
| 139 ~KeyContainer() { | |
| 140 if (provider_) { | |
| 141 provider_.reset(); | |
| 142 if (delete_keyset_ && !key_id_.empty()) { | |
| 143 HCRYPTPROV provider; | |
| 144 crypto::CryptAcquireContextLocked(&provider, key_id_.c_str(), NULL, | |
| 145 PROV_RSA_FULL, CRYPT_SILENT | CRYPT_DELETEKEYSET); | |
| 146 } | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 crypto::ScopedHCRYPTPROV provider_; | |
| 151 std::wstring key_id_; | |
| 152 | |
| 153 private: | |
| 154 bool delete_keyset_; | |
| 155 }; | |
| 156 | |
| 157 std::string KeygenHandler::GenKeyAndSignChallenge() { | |
| 158 KeyContainer key_container(!stores_key_); | |
| 159 | |
| 160 // TODO(rsleevi): Have the user choose which provider they should use, which | |
| 161 // needs to be filtered by those providers which can provide the key type | |
| 162 // requested or the key size requested. This is especially important for | |
| 163 // generating certificates that will be stored on smart cards. | |
| 164 const int kMaxAttempts = 5; | |
| 165 int attempt; | |
| 166 for (attempt = 0; attempt < kMaxAttempts; ++attempt) { | |
| 167 // Per MSDN documentation for CryptAcquireContext, if applications will be | |
| 168 // creating their own keys, they should ensure unique naming schemes to | |
| 169 // prevent overlap with any other applications or consumers of CSPs, and | |
| 170 // *should not* store new keys within the default, NULL key container. | |
| 171 key_container.key_id_ = GetNewKeyContainerId(); | |
| 172 if (key_container.key_id_.empty()) | |
| 173 return std::string(); | |
| 174 | |
| 175 // Only create new key containers, so that existing key containers are not | |
| 176 // overwritten. | |
| 177 if (crypto::CryptAcquireContextLocked(key_container.provider_.receive(), | |
| 178 key_container.key_id_.c_str(), NULL, PROV_RSA_FULL, | |
| 179 CRYPT_SILENT | CRYPT_NEWKEYSET)) | |
| 180 break; | |
| 181 | |
| 182 if (GetLastError() != static_cast<DWORD>(NTE_BAD_KEYSET)) { | |
| 183 LOG(ERROR) << "Keygen failed: Couldn't acquire a CryptoAPI provider " | |
| 184 "context: " << GetLastError(); | |
| 185 return std::string(); | |
| 186 } | |
| 187 } | |
| 188 if (attempt == kMaxAttempts) { | |
| 189 LOG(ERROR) << "Keygen failed: Couldn't acquire a CryptoAPI provider " | |
| 190 "context: Max retries exceeded"; | |
| 191 return std::string(); | |
| 192 } | |
| 193 | |
| 194 { | |
| 195 crypto::ScopedHCRYPTKEY key; | |
| 196 if (!CryptGenKey(key_container.provider_, CALG_RSA_KEYX, | |
| 197 (key_size_in_bits_ << 16) | CRYPT_EXPORTABLE, key.receive())) { | |
| 198 LOG(ERROR) << "Keygen failed: Couldn't generate an RSA key"; | |
| 199 return std::string(); | |
| 200 } | |
| 201 | |
| 202 std::string spkac; | |
| 203 if (!GetSignedPublicKeyAndChallenge(key_container.provider_, challenge_, | |
| 204 &spkac)) { | |
| 205 LOG(ERROR) << "Keygen failed: Couldn't generate the signed public key " | |
| 206 "and challenge"; | |
| 207 return std::string(); | |
| 208 } | |
| 209 | |
| 210 std::string result; | |
| 211 base::Base64Encode(spkac, &result); | |
| 212 | |
| 213 VLOG(1) << "Keygen succeeded"; | |
| 214 return result; | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 } // namespace net | |
| OLD | NEW |