• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is mozilla.org code.
17  *
18  * The Initial Developer of the Original Code is
19  * Netscape Communications Corporation.
20  * Portions created by the Initial Developer are Copyright (C) 1998
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  *   Vipul Gupta <vipul.gupta@sun.com>
25  *   Douglas Stebila <douglas@stebila.ca>
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either the GNU General Public License Version 2 or later (the "GPL"), or
29  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40 
41 #include "net/third_party/mozilla_security_manager/nsKeygenHandler.h"
42 
43 #include <pk11pub.h>
44 #include <prerror.h>   // PR_GetError()
45 #include <secmod.h>
46 #include <secder.h>    // DER_Encode()
47 #include <cryptohi.h>  // SEC_DerSignData()
48 #include <keyhi.h>     // SECKEY_CreateSubjectPublicKeyInfo()
49 
50 #include "base/base64.h"
51 #include "base/logging.h"
52 #include "crypto/nss_util.h"
53 #include "url/gurl.h"
54 
55 namespace {
56 
57 // Template for creating the signed public key structure to be sent to the CA.
58 DERTemplate SECAlgorithmIDTemplate[] = {
59   { DER_SEQUENCE,
60     0, NULL, sizeof(SECAlgorithmID) },
61   { DER_OBJECT_ID,
62     offsetof(SECAlgorithmID, algorithm), },
63   { DER_OPTIONAL | DER_ANY,
64     offsetof(SECAlgorithmID, parameters), },
65   { 0, }
66 };
67 
68 DERTemplate CERTSubjectPublicKeyInfoTemplate[] = {
69   { DER_SEQUENCE,
70     0, NULL, sizeof(CERTSubjectPublicKeyInfo) },
71   { DER_INLINE,
72     offsetof(CERTSubjectPublicKeyInfo, algorithm),
73     SECAlgorithmIDTemplate, },
74   { DER_BIT_STRING,
75     offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey), },
76   { 0, }
77 };
78 
79 DERTemplate CERTPublicKeyAndChallengeTemplate[] = {
80   { DER_SEQUENCE,
81     0, NULL, sizeof(CERTPublicKeyAndChallenge) },
82   { DER_ANY,
83     offsetof(CERTPublicKeyAndChallenge, spki), },
84   { DER_IA5_STRING,
85     offsetof(CERTPublicKeyAndChallenge, challenge), },
86   { 0, }
87 };
88 
89 }  // namespace
90 
91 namespace mozilla_security_manager {
92 
93 // This function is based on the nsKeygenFormProcessor::GetPublicKey function
94 // in mozilla/security/manager/ssl/src/nsKeygenHandler.cpp.
GenKeyAndSignChallenge(int key_size_in_bits,const std::string & challenge,const GURL & url,PK11SlotInfo * slot,bool stores_key)95 std::string GenKeyAndSignChallenge(int key_size_in_bits,
96                                    const std::string& challenge,
97                                    const GURL& url,
98                                    PK11SlotInfo* slot,
99                                    bool stores_key) {
100   // Key pair generation mechanism - only RSA is supported at present.
101   PRUint32 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;  // from nss/pkcs11t.h
102 
103   // Temporary structures used for generating the result
104   // in the right format.
105   PK11RSAGenParams rsaKeyGenParams;  // Keygen parameters.
106   SECOidTag algTag;  // used by SEC_DerSignData().
107   SECKEYPrivateKey *privateKey = NULL;
108   SECKEYPublicKey *publicKey = NULL;
109   CERTSubjectPublicKeyInfo *spkInfo = NULL;
110   PLArenaPool *arena = NULL;
111   SECStatus sec_rv =SECFailure;
112   SECItem spkiItem;
113   SECItem pkacItem;
114   SECItem signedItem;
115   CERTPublicKeyAndChallenge pkac;
116   void *keyGenParams;
117   bool isSuccess = true;  // Set to false as soon as a step fails.
118 
119   std::string result_blob;  // the result.
120 
121   switch (keyGenMechanism) {
122     case CKM_RSA_PKCS_KEY_PAIR_GEN:
123       rsaKeyGenParams.keySizeInBits = key_size_in_bits;
124       rsaKeyGenParams.pe = DEFAULT_RSA_KEYGEN_PE;
125       keyGenParams = &rsaKeyGenParams;
126 
127       algTag = DEFAULT_RSA_KEYGEN_ALG;
128       break;
129     default:
130       // TODO(gauravsh): If we ever support other mechanisms,
131       // this can be changed.
132       LOG(ERROR) << "Only RSA keygen mechanism is supported";
133       isSuccess = false;
134       goto failure;
135   }
136 
137   VLOG(1) << "Creating key pair...";
138   {
139     crypto::AutoNSSWriteLock lock;
140     privateKey = PK11_GenerateKeyPair(slot,
141                                       keyGenMechanism,
142                                       keyGenParams,
143                                       &publicKey,
144                                       PR_TRUE,  // isPermanent?
145                                       PR_TRUE,  // isSensitive?
146                                       NULL);
147   }
148   VLOG(1) << "done.";
149 
150   if (!privateKey) {
151     LOG(ERROR) << "Generation of Keypair failed!";
152     isSuccess = false;
153     goto failure;
154   }
155 
156   // Set friendly names for the keys.
157   if (url.has_host()) {
158     // TODO(davidben): Use something like "Key generated for
159     // example.com", but localize it.
160     const std::string& label = url.host();
161     {
162       crypto::AutoNSSWriteLock lock;
163       PK11_SetPublicKeyNickname(publicKey, label.c_str());
164       PK11_SetPrivateKeyNickname(privateKey, label.c_str());
165     }
166   }
167 
168   // The CA expects the signed public key in a specific format
169   // Let's create that now.
170 
171   // Create a subject public key info from the public key.
172   spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey);
173   if (!spkInfo) {
174     LOG(ERROR) << "Couldn't create SubjectPublicKeyInfo from public key";
175     isSuccess = false;
176     goto failure;
177   }
178 
179   arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
180   if (!arena) {
181     LOG(ERROR) << "PORT_NewArena: Couldn't allocate memory";
182     isSuccess = false;
183     goto failure;
184   }
185 
186   // DER encode the whole subjectPublicKeyInfo.
187   sec_rv = DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate,
188                       spkInfo);
189   if (SECSuccess != sec_rv) {
190     LOG(ERROR) << "Couldn't DER Encode subjectPublicKeyInfo";
191     isSuccess = false;
192     goto failure;
193   }
194 
195   // Set up the PublicKeyAndChallenge data structure, then DER encode it.
196   pkac.spki = spkiItem;
197   pkac.challenge.type = siBuffer;
198   pkac.challenge.len = challenge.length();
199   pkac.challenge.data = (unsigned char *)challenge.data();
200   sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate,
201                       &pkac);
202   if (SECSuccess != sec_rv) {
203     LOG(ERROR) << "Couldn't DER Encode PublicKeyAndChallenge";
204     isSuccess = false;
205     goto failure;
206   }
207 
208   // Sign the DER encoded PublicKeyAndChallenge.
209   sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len,
210                            privateKey, algTag);
211   if (SECSuccess != sec_rv) {
212     LOG(ERROR) << "Couldn't sign the DER encoded PublicKeyandChallenge";
213     isSuccess = false;
214     goto failure;
215   }
216 
217   // Convert the signed public key and challenge into base64/ascii.
218   base::Base64Encode(
219       std::string(reinterpret_cast<char*>(signedItem.data), signedItem.len),
220       &result_blob);
221 
222  failure:
223   if (!isSuccess) {
224     LOG(ERROR) << "SSL Keygen failed! (NSS error code " << PR_GetError() << ")";
225   } else {
226     VLOG(1) << "SSL Keygen succeeded!";
227   }
228 
229   // Do cleanups
230   if (privateKey) {
231     // On successful keygen we need to keep the private key, of course,
232     // or we won't be able to use the client certificate.
233     if (!isSuccess || !stores_key) {
234       crypto::AutoNSSWriteLock lock;
235       PK11_DestroyTokenObject(privateKey->pkcs11Slot, privateKey->pkcs11ID);
236     }
237     SECKEY_DestroyPrivateKey(privateKey);
238   }
239 
240   if (publicKey) {
241     if (!isSuccess || !stores_key) {
242       crypto::AutoNSSWriteLock lock;
243       PK11_DestroyTokenObject(publicKey->pkcs11Slot, publicKey->pkcs11ID);
244     }
245     SECKEY_DestroyPublicKey(publicKey);
246   }
247   if (spkInfo) {
248     SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
249   }
250   if (arena) {
251     PORT_FreeArena(arena, PR_TRUE);
252   }
253 
254   return (isSuccess ? result_blob : std::string());
255 }
256 
257 }  // namespace mozilla_security_manager
258