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 (at) sun.com> 25 * Douglas Stebila <douglas (at) 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. 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 if (!base::Base64Encode(std::string(reinterpret_cast<char*>(signedItem.data), 219 signedItem.len), 220 &result_blob)) { 221 LOG(ERROR) << "Couldn't convert signed public key into base64"; 222 isSuccess = false; 223 goto failure; 224 } 225 226 failure: 227 if (!isSuccess) { 228 LOG(ERROR) << "SSL Keygen failed! (NSS error code " << PR_GetError() << ")"; 229 } else { 230 VLOG(1) << "SSL Keygen succeeded!"; 231 } 232 233 // Do cleanups 234 if (privateKey) { 235 // On successful keygen we need to keep the private key, of course, 236 // or we won't be able to use the client certificate. 237 if (!isSuccess || !stores_key) { 238 crypto::AutoNSSWriteLock lock; 239 PK11_DestroyTokenObject(privateKey->pkcs11Slot, privateKey->pkcs11ID); 240 } 241 SECKEY_DestroyPrivateKey(privateKey); 242 } 243 244 if (publicKey) { 245 if (!isSuccess || !stores_key) { 246 crypto::AutoNSSWriteLock lock; 247 PK11_DestroyTokenObject(publicKey->pkcs11Slot, publicKey->pkcs11ID); 248 } 249 SECKEY_DestroyPublicKey(publicKey); 250 } 251 if (spkInfo) { 252 SECKEY_DestroySubjectPublicKeyInfo(spkInfo); 253 } 254 if (arena) { 255 PORT_FreeArena(arena, PR_TRUE); 256 } 257 258 return (isSuccess ? result_blob : std::string()); 259 } 260 261 } // namespace mozilla_security_manager 262