• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 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 <pk11pub.h>
8 #include <secmod.h>
9 #include <ssl.h>
10 #include <nssb64.h>    // NSSBase64_EncodeItem()
11 #include <secder.h>    // DER_Encode()
12 #include <cryptohi.h>  // SEC_DerSignData()
13 #include <keyhi.h>     // SECKEY_CreateSubjectPublicKeyInfo()
14 
15 #include "base/nss_util.h"
16 #include "base/logging.h"
17 
18 namespace net {
19 
20 const int64 DEFAULT_RSA_PUBLIC_EXPONENT = 0x10001;
21 
22 // Template for creating the signed public key structure to be sent to the CA.
23 DERTemplate SECAlgorithmIDTemplate[] = {
24   { DER_SEQUENCE,
25     0, NULL, sizeof(SECAlgorithmID) },
26   { DER_OBJECT_ID,
27     offsetof(SECAlgorithmID, algorithm), },
28   { DER_OPTIONAL | DER_ANY,
29     offsetof(SECAlgorithmID, parameters), },
30   { 0, }
31 };
32 
33 DERTemplate CERTSubjectPublicKeyInfoTemplate[] = {
34   { DER_SEQUENCE,
35     0, NULL, sizeof(CERTSubjectPublicKeyInfo) },
36   { DER_INLINE,
37     offsetof(CERTSubjectPublicKeyInfo, algorithm),
38     SECAlgorithmIDTemplate, },
39   { DER_BIT_STRING,
40     offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey), },
41   { 0, }
42 };
43 
44 DERTemplate CERTPublicKeyAndChallengeTemplate[] = {
45   { DER_SEQUENCE,
46     0, NULL, sizeof(CERTPublicKeyAndChallenge) },
47   { DER_ANY,
48     offsetof(CERTPublicKeyAndChallenge, spki), },
49   { DER_IA5_STRING,
50     offsetof(CERTPublicKeyAndChallenge, challenge), },
51   { 0, }
52 };
53 
54 // This maps displayed strings indicating level of keysecurity in the <keygen>
55 // menu to the key size in bits.
56 // TODO(gauravsh): Should this mapping be moved else where?
57 int RSAkeySizeMap[] = {2048, 1024};
58 
KeygenHandler(int key_size_index,const std::string & challenge)59 KeygenHandler::KeygenHandler(int key_size_index,
60                              const std::string& challenge)
61     : key_size_index_(key_size_index),
62       challenge_(challenge) {
63   if (key_size_index_ < 0 ||
64       key_size_index_ >=
65           static_cast<int>(sizeof(RSAkeySizeMap) / sizeof(RSAkeySizeMap[0])))
66     key_size_index_ = 0;
67 }
68 
69 // This function is largely copied from the Firefox's
70 // <keygen> implementation in security/manager/ssl/src/nsKeygenHandler.cpp
71 // FIXME(gauravsh): Do we need a copy of the Mozilla license here?
72 
GenKeyAndSignChallenge()73 std::string KeygenHandler::GenKeyAndSignChallenge() {
74   // Key pair generation mechanism - only RSA is supported at present.
75   PRUint32 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;  // from nss/pkcs11t.h
76   char *keystring = NULL;  // Temporary store for result/
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 = PK11_GetInternalKeySlot();
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 = RSAkeySizeMap[key_size_index_];
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   keystring = NSSBase64_EncodeItem(arena,
206                                    NULL,  // NSS will allocate a buffer for us.
207                                    0,
208                                    &signedItem);
209   if (!keystring) {
210     LOG(ERROR) << "Couldn't convert signed public key into base64";
211     isSuccess = false;
212     goto failure;
213   }
214 
215   result_blob = keystring;
216 
217  failure:
218   if (!isSuccess) {
219     LOG(ERROR) << "SSL Keygen failed!";
220   } else {
221     LOG(INFO) << "SSl Keygen succeeded!";
222   }
223 
224   // Do cleanups
225   if (privateKey) {
226     // TODO(gauravsh): We still need to maintain the private key because it's
227     // used for certificate enrollment checks.
228 
229     // PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID);
230     // SECKEY_DestroyPrivateKey(privateKey);
231   }
232 
233   if (publicKey) {
234     PK11_DestroyTokenObject(publicKey->pkcs11Slot, publicKey->pkcs11ID);
235   }
236   if (spkInfo) {
237     SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
238   }
239   if (publicKey) {
240     SECKEY_DestroyPublicKey(publicKey);
241   }
242   if (arena) {
243     PORT_FreeArena(arena, PR_TRUE);
244   }
245   if (slot != NULL) {
246     PK11_FreeSlot(slot);
247   }
248   if (pkac.challenge.data) {
249     free(pkac.challenge.data);
250   }
251 
252   return (isSuccess ? result_blob : std::string());
253 }
254 
255 }  // namespace net
256