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 <pk11pub.h> | 7 #include "net/third_party/mozilla_security_manager/nsKeygenHandler.h" |
8 #include <secmod.h> | |
9 #include <ssl.h> | |
10 #include <secder.h> // DER_Encode() | |
11 #include <cryptohi.h> // SEC_DerSignData() | |
12 #include <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo() | |
13 | 8 |
14 #include "base/base64.h" | 9 // PSM = Mozilla's Personal Security Manager. |
15 #include "base/nss_util_internal.h" | 10 namespace psm = mozilla_security_manager; |
16 #include "base/nss_util.h" | |
17 #include "base/logging.h" | |
18 | 11 |
19 namespace net { | 12 namespace net { |
20 | 13 |
21 const int64 DEFAULT_RSA_PUBLIC_EXPONENT = 0x10001; | |
22 | |
23 // Template for creating the signed public key structure to be sent to the CA. | |
24 DERTemplate SECAlgorithmIDTemplate[] = { | |
25 { DER_SEQUENCE, | |
26 0, NULL, sizeof(SECAlgorithmID) }, | |
27 { DER_OBJECT_ID, | |
28 offsetof(SECAlgorithmID, algorithm), }, | |
29 { DER_OPTIONAL | DER_ANY, | |
30 offsetof(SECAlgorithmID, parameters), }, | |
31 { 0, } | |
32 }; | |
33 | |
34 DERTemplate CERTSubjectPublicKeyInfoTemplate[] = { | |
35 { DER_SEQUENCE, | |
36 0, NULL, sizeof(CERTSubjectPublicKeyInfo) }, | |
37 { DER_INLINE, | |
38 offsetof(CERTSubjectPublicKeyInfo, algorithm), | |
39 SECAlgorithmIDTemplate, }, | |
40 { DER_BIT_STRING, | |
41 offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey), }, | |
42 { 0, } | |
43 }; | |
44 | |
45 DERTemplate CERTPublicKeyAndChallengeTemplate[] = { | |
46 { DER_SEQUENCE, | |
47 0, NULL, sizeof(CERTPublicKeyAndChallenge) }, | |
48 { DER_ANY, | |
49 offsetof(CERTPublicKeyAndChallenge, spki), }, | |
50 { DER_IA5_STRING, | |
51 offsetof(CERTPublicKeyAndChallenge, challenge), }, | |
52 { 0, } | |
53 }; | |
54 | |
55 void StoreKeyLocationInCache(const SECItem& public_key_info, | |
56 PK11SlotInfo *slot) { | |
57 KeygenHandler::Cache* cache = KeygenHandler::Cache::GetInstance(); | |
58 KeygenHandler::KeyLocation key_location; | |
59 const char* slot_name = PK11_GetSlotName(slot); | |
60 key_location.slot_name.assign(slot_name); | |
61 cache->Insert(std::string(reinterpret_cast<char*>(public_key_info.data), | |
62 public_key_info.len), key_location); | |
63 } | |
64 | |
65 bool KeygenHandler::KeyLocation::Equals( | 14 bool KeygenHandler::KeyLocation::Equals( |
66 const net::KeygenHandler::KeyLocation& location) const { | 15 const net::KeygenHandler::KeyLocation& location) const { |
67 return slot_name == location.slot_name; | 16 return slot_name == location.slot_name; |
68 } | 17 } |
69 | 18 |
70 // This function is largely copied from the Firefox's | |
71 // <keygen> implementation in security/manager/ssl/src/nsKeygenHandler.cpp | |
72 // FIXME(gauravsh): Do we need a copy of the Mozilla license here? | |
73 | |
74 std::string KeygenHandler::GenKeyAndSignChallenge() { | 19 std::string KeygenHandler::GenKeyAndSignChallenge() { |
75 // Key pair generation mechanism - only RSA is supported at present. | 20 return psm::GenKeyAndSignChallenge(key_size_in_bits_, challenge_, |
76 PRUint32 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; // from nss/pkcs11t.h | 21 stores_key_); |
77 | |
78 // Temporary structures used for generating the result | |
79 // in the right format. | |
80 PK11SlotInfo *slot = NULL; | |
81 PK11RSAGenParams rsaKeyGenParams; // Keygen parameters. | |
82 SECOidTag algTag; // used by SEC_DerSignData(). | |
83 SECKEYPrivateKey *privateKey = NULL; | |
84 SECKEYPublicKey *publicKey = NULL; | |
85 CERTSubjectPublicKeyInfo *spkInfo = NULL; | |
86 PRArenaPool *arena = NULL; | |
87 SECStatus sec_rv =SECFailure; | |
88 SECItem spkiItem; | |
89 SECItem pkacItem; | |
90 SECItem signedItem; | |
91 CERTPublicKeyAndChallenge pkac; | |
92 void *keyGenParams; | |
93 pkac.challenge.data = NULL; | |
94 bool isSuccess = true; // Set to false as soon as a step fails. | |
95 | |
96 std::string result_blob; // the result. | |
97 | |
98 // Ensure NSS is initialized. | |
99 base::EnsureNSSInit(); | |
100 | |
101 slot = base::GetDefaultNSSKeySlot(); | |
102 if (!slot) { | |
103 LOG(ERROR) << "Couldn't get Internal key slot!"; | |
104 isSuccess = false; | |
105 goto failure; | |
106 } | |
107 | |
108 switch (keyGenMechanism) { | |
109 case CKM_RSA_PKCS_KEY_PAIR_GEN: | |
110 rsaKeyGenParams.keySizeInBits = key_size_in_bits_; | |
111 rsaKeyGenParams.pe = DEFAULT_RSA_PUBLIC_EXPONENT; | |
112 keyGenParams = &rsaKeyGenParams; | |
113 | |
114 algTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; // from <nss/secoidt.h>. | |
115 break; | |
116 default: | |
117 // TODO(gauravsh): If we ever support other mechanisms, | |
118 // this can be changed. | |
119 LOG(ERROR) << "Only RSA keygen mechanism is supported"; | |
120 isSuccess = false; | |
121 goto failure; | |
122 break; | |
123 } | |
124 | |
125 // Need to make sure that the token was initialized. | |
126 // Assume a null password. | |
127 sec_rv = PK11_Authenticate(slot, PR_TRUE, NULL); | |
128 if (SECSuccess != sec_rv) { | |
129 LOG(ERROR) << "Couldn't initialze PK11 token!"; | |
130 isSuccess = false; | |
131 goto failure; | |
132 } | |
133 | |
134 LOG(INFO) << "Creating key pair..."; | |
135 privateKey = PK11_GenerateKeyPair(slot, | |
136 keyGenMechanism, | |
137 keyGenParams, | |
138 &publicKey, | |
139 PR_TRUE, // isPermanent? | |
140 PR_TRUE, // isSensitive? | |
141 NULL); | |
142 LOG(INFO) << "done."; | |
143 | |
144 if (!privateKey) { | |
145 LOG(INFO) << "Generation of Keypair failed!"; | |
146 isSuccess = false; | |
147 goto failure; | |
148 } | |
149 | |
150 // The CA expects the signed public key in a specific format | |
151 // Let's create that now. | |
152 | |
153 // Create a subject public key info from the public key. | |
154 spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey); | |
155 if (!spkInfo) { | |
156 LOG(ERROR) << "Couldn't create SubjectPublicKeyInfo from public key"; | |
157 isSuccess = false; | |
158 goto failure; | |
159 } | |
160 | |
161 // Temporary work store used by NSS. | |
162 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); | |
163 if (!arena) { | |
164 LOG(ERROR) << "PORT_NewArena: Couldn't allocate memory"; | |
165 isSuccess = false; | |
166 goto failure; | |
167 } | |
168 | |
169 // DER encode the whole subjectPublicKeyInfo. | |
170 sec_rv = DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate, | |
171 spkInfo); | |
172 if (SECSuccess != sec_rv) { | |
173 LOG(ERROR) << "Couldn't DER Encode subjectPublicKeyInfo"; | |
174 isSuccess = false; | |
175 goto failure; | |
176 } | |
177 | |
178 // Set up the PublicKeyAndChallenge data structure, then DER encode it. | |
179 pkac.spki = spkiItem; | |
180 pkac.challenge.len = challenge_.length(); | |
181 pkac.challenge.data = (unsigned char *)strdup(challenge_.c_str()); | |
182 if (!pkac.challenge.data) { | |
183 LOG(ERROR) << "Out of memory while making a copy of challenge data"; | |
184 isSuccess = false; | |
185 goto failure; | |
186 } | |
187 sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate, | |
188 &pkac); | |
189 if (SECSuccess != sec_rv) { | |
190 LOG(ERROR) << "Couldn't DER Encode PublicKeyAndChallenge"; | |
191 isSuccess = false; | |
192 goto failure; | |
193 } | |
194 | |
195 // Sign the DER encoded PublicKeyAndChallenge. | |
196 sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len, | |
197 privateKey, algTag); | |
198 if (SECSuccess != sec_rv) { | |
199 LOG(ERROR) << "Couldn't sign the DER encoded PublicKeyandChallenge"; | |
200 isSuccess = false; | |
201 goto failure; | |
202 } | |
203 | |
204 // Convert the signed public key and challenge into base64/ascii. | |
205 if (!base::Base64Encode(std::string(reinterpret_cast<char*>(signedItem.data), | |
206 signedItem.len), | |
207 &result_blob)) { | |
208 LOG(ERROR) << "Couldn't convert signed public key into base64"; | |
209 isSuccess = false; | |
210 goto failure; | |
211 } | |
212 | |
213 StoreKeyLocationInCache(spkiItem, slot); | |
214 | |
215 failure: | |
216 if (!isSuccess) { | |
217 LOG(ERROR) << "SSL Keygen failed!"; | |
218 } else { | |
219 LOG(INFO) << "SSL Keygen succeeded!"; | |
220 } | |
221 | |
222 // Do cleanups | |
223 if (privateKey) { | |
224 // On successful keygen we need to keep the private key, of course, | |
225 // or we won't be able to use the client certificate. | |
226 if (!isSuccess || !stores_key_) { | |
227 PK11_DestroyTokenObject(privateKey->pkcs11Slot, privateKey->pkcs11ID); | |
228 } | |
229 SECKEY_DestroyPrivateKey(privateKey); | |
230 } | |
231 | |
232 if (publicKey) { | |
233 if (!isSuccess || !stores_key_) { | |
234 PK11_DestroyTokenObject(publicKey->pkcs11Slot, publicKey->pkcs11ID); | |
235 } | |
236 SECKEY_DestroyPublicKey(publicKey); | |
237 } | |
238 if (spkInfo) { | |
239 SECKEY_DestroySubjectPublicKeyInfo(spkInfo); | |
240 } | |
241 if (arena) { | |
242 PORT_FreeArena(arena, PR_TRUE); | |
243 } | |
244 if (slot != NULL) { | |
245 PK11_FreeSlot(slot); | |
246 } | |
247 if (pkac.challenge.data) { | |
248 free(pkac.challenge.data); | |
249 } | |
250 | |
251 return (isSuccess ? result_blob : std::string()); | |
252 } | 22 } |
253 | 23 |
254 } // namespace net | 24 } // namespace net |
OLD | NEW |