Home | History | Annotate | Download | only in mozilla_security_manager
      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