OLD | NEW |
(Empty) | |
| 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 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 |
| 8 // until NSS 3.12.2 comes out and we update to it. |
| 9 #define Lock FOO_NSS_Lock |
| 10 #include <pk11pub.h> |
| 11 #include <secmod.h> |
| 12 #include <ssl.h> |
| 13 #include <nssb64.h> // NSSBase64_EncodeItem() |
| 14 #include <secder.h> // DER_Encode() |
| 15 #include <cryptohi.h> // SEC_DerSignData() |
| 16 #include <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo() |
| 17 #undef Lock |
| 18 |
| 19 #include "base/nss_init.h" |
| 20 #include "base/logging.h" |
| 21 |
| 22 namespace net { |
| 23 |
| 24 const int64 DEFAULT_RSA_PUBLIC_EXPONENT = 0x10001; |
| 25 |
| 26 // Template for creating the signed public key structure to be sent to the CA. |
| 27 DERTemplate SECAlgorithmIDTemplate[] = { |
| 28 { DER_SEQUENCE, |
| 29 0, NULL, sizeof(SECAlgorithmID) }, |
| 30 { DER_OBJECT_ID, |
| 31 offsetof(SECAlgorithmID, algorithm), }, |
| 32 { DER_OPTIONAL | DER_ANY, |
| 33 offsetof(SECAlgorithmID, parameters), }, |
| 34 { 0, } |
| 35 }; |
| 36 |
| 37 DERTemplate CERTSubjectPublicKeyInfoTemplate[] = { |
| 38 { DER_SEQUENCE, |
| 39 0, NULL, sizeof(CERTSubjectPublicKeyInfo) }, |
| 40 { DER_INLINE, |
| 41 offsetof(CERTSubjectPublicKeyInfo, algorithm), |
| 42 SECAlgorithmIDTemplate, }, |
| 43 { DER_BIT_STRING, |
| 44 offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey), }, |
| 45 { 0, } |
| 46 }; |
| 47 |
| 48 DERTemplate CERTPublicKeyAndChallengeTemplate[] = { |
| 49 { DER_SEQUENCE, |
| 50 0, NULL, sizeof(CERTPublicKeyAndChallenge) }, |
| 51 { DER_ANY, |
| 52 offsetof(CERTPublicKeyAndChallenge, spki), }, |
| 53 { DER_IA5_STRING, |
| 54 offsetof(CERTPublicKeyAndChallenge, challenge), }, |
| 55 { 0, } |
| 56 }; |
| 57 |
| 58 // This maps displayed strings indicating level of keysecurity in the <keygen> |
| 59 // menu to the key size in bits. |
| 60 // TODO(gauravsh): Should this mapping be moved else where? |
| 61 int RSAkeySizeMap[] = {2048, 1024}; |
| 62 |
| 63 KeygenHandler::KeygenHandler(int key_size_index, |
| 64 const std::string& challenge) |
| 65 : key_size_index_(key_size_index), |
| 66 challenge_(challenge) { |
| 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 |
| 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 |
OLD | NEW |