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