| 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 CERT_KEYGEN_REQUEST_INFO request; |
| 108 request.dwVersion = CERT_KEYGEN_REQUEST_V1; |
| 109 request.SubjectPublicKeyInfo = |
| 110 *reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&spki[0]); |
| 111 request.pwszChallengeString = const_cast<wchar_t*>(wide_challenge.c_str()); |
| 112 |
| 113 CRYPT_ALGORITHM_IDENTIFIER sig_alg; |
| 114 memset(&sig_alg, 0, sizeof(sig_alg)); |
| 115 sig_alg.pszObjId = szOID_RSA_MD5RSA; |
| 116 |
| 117 BOOL ok; |
| 118 DWORD size = 0; |
| 153 std::vector<BYTE> signed_pkac; | 119 std::vector<BYTE> signed_pkac; |
| 154 DWORD size = 0; | 120 ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, |
| 155 BOOL ok; | 121 X509_KEYGEN_REQUEST_TO_BE_SIGNED, |
| 156 | 122 &request, &sig_alg, NULL, |
| 157 // While the MSDN documentation states that CERT_SIGNED_CONTENT_INFO should | 123 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); | 124 DCHECK(ok); |
| 179 if (!ok) | 125 if (!ok) |
| 180 return false; | 126 return false; |
| 181 | 127 |
| 182 signature.resize(size); | 128 signed_pkac.resize(size); |
| 183 info.Signature.cbData = signature.size(); | 129 ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, |
| 184 info.Signature.pbData = &signature[0]; | 130 X509_KEYGEN_REQUEST_TO_BE_SIGNED, |
| 185 info.Signature.cUnusedBits = 0; | 131 &request, &sig_alg, NULL, |
| 186 | 132 &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); | 133 DCHECK(ok); |
| 192 if (!ok || !EncodeAndAppendType(X509_CERT, &info, &signed_pkac)) | 134 if (!ok) |
| 193 return false; | 135 return false; |
| 194 | 136 |
| 195 output->assign(reinterpret_cast<char*>(&signed_pkac[0]), | 137 output->assign(reinterpret_cast<char*>(&signed_pkac[0]), size); |
| 196 signed_pkac.size()); | |
| 197 | |
| 198 return true; | 138 return true; |
| 199 } | 139 } |
| 200 | 140 |
| 201 // Generates a unique name for the container which will store the key that is | 141 // 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. | 142 // generated. The traditional Windows approach is to use a GUID here. |
| 203 std::wstring GetNewKeyContainerId() { | 143 std::wstring GetNewKeyContainerId() { |
| 204 RPC_STATUS status = RPC_S_OK; | 144 RPC_STATUS status = RPC_S_OK; |
| 205 std::wstring result; | 145 std::wstring result; |
| 206 | 146 |
| 207 UUID id = { 0 }; | 147 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. | 299 // Fully destroys any of the keys that were created and releases prov. |
| 360 CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL, | 300 CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL, |
| 361 CRYPT_SILENT | CRYPT_DELETEKEYSET); | 301 CRYPT_SILENT | CRYPT_DELETEKEYSET); |
| 362 } | 302 } |
| 363 } | 303 } |
| 364 | 304 |
| 365 return result; | 305 return result; |
| 366 } | 306 } |
| 367 | 307 |
| 368 } // namespace net | 308 } // namespace net |
| OLD | NEW |