1 /*
   2  * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.tools.keytool;
  27 
  28 import java.io.IOException;
  29 import java.security.cert.X509Certificate;
  30 import java.security.cert.CertificateException;
  31 import java.security.cert.CertificateEncodingException;
  32 import java.security.*;
  33 import java.util.Date;
  34 
  35 import sun.security.pkcs10.PKCS10;
  36 import sun.security.x509.*;
  37 
  38 
  39 /**
  40  * Generate a pair of keys, and provide access to them.  This class is
  41  * provided primarily for ease of use.
  42  *
  43  * <P>This provides some simple certificate management functionality.
  44  * Specifically, it allows you to create self-signed X.509 certificates
  45  * as well as PKCS 10 based certificate signing requests.
  46  *
  47  * <P>Keys for some public key signature algorithms have algorithm
  48  * parameters, such as DSS/DSA.  Some sites' Certificate Authorities
  49  * adopt fixed algorithm parameters, which speeds up some operations
  50  * including key generation and signing.  <em>At this time, this interface
  51  * does not provide a way to provide such algorithm parameters, e.g.
  52  * by providing the CA certificate which includes those parameters.</em>
  53  *
  54  * <P>Also, note that at this time only signature-capable keys may be
  55  * acquired through this interface.  Diffie-Hellman keys, used for secure
  56  * key exchange, may be supported later.
  57  *
  58  * @author David Brownell
  59  * @author Hemma Prafullchandra
  60  * @see PKCS10
  61  * @see X509CertImpl
  62  */
  63 public final class CertAndKeyGen {
  64     /**
  65      * Creates a CertAndKeyGen object for a particular key type
  66      * and signature algorithm.
  67      *
  68      * @param keyType type of key, e.g. "RSA", "DSA"
  69      * @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA",
  70      *          "MD2WithRSA", "SHAwithDSA". If set to null, a default
  71      *          algorithm matching the private key will be chosen after
  72      *          the first keypair is generated.
  73      * @exception NoSuchAlgorithmException on unrecognized algorithms.
  74      */
  75     public CertAndKeyGen (String keyType, String sigAlg)
  76     throws NoSuchAlgorithmException
  77     {
  78         keyGen = KeyPairGenerator.getInstance(keyType);
  79         this.sigAlg = sigAlg;
  80     }
  81 
  82     /**
  83      * Creates a CertAndKeyGen object for a particular key type,
  84      * signature algorithm, and provider.
  85      *
  86      * @param keyType type of key, e.g. "RSA", "DSA"
  87      * @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA",
  88      *          "MD2WithRSA", "SHAwithDSA". If set to null, a default
  89      *          algorithm matching the private key will be chosen after
  90      *          the first keypair is generated.
  91      * @param providerName name of the provider
  92      * @exception NoSuchAlgorithmException on unrecognized algorithms.
  93      * @exception NoSuchProviderException on unrecognized providers.
  94      */
  95     public CertAndKeyGen (String keyType, String sigAlg, String providerName)
  96     throws NoSuchAlgorithmException, NoSuchProviderException
  97     {
  98         if (providerName == null) {
  99             keyGen = KeyPairGenerator.getInstance(keyType);
 100         } else {
 101             try {
 102                 keyGen = KeyPairGenerator.getInstance(keyType, providerName);
 103             } catch (Exception e) {
 104                 // try first available provider instead
 105                 keyGen = KeyPairGenerator.getInstance(keyType);
 106             }
 107         }
 108         this.sigAlg = sigAlg;
 109     }
 110 
 111     /**
 112      * Sets the source of random numbers used when generating keys.
 113      * If you do not provide one, a system default facility is used.
 114      * You may wish to provide your own source of random numbers
 115      * to get a reproducible sequence of keys and signatures, or
 116      * because you may be able to take advantage of strong sources
 117      * of randomness/entropy in your environment.
 118      */
 119     public void         setRandom (SecureRandom generator)
 120     {
 121         prng = generator;
 122     }
 123 
 124     // want "public void generate (X509Certificate)" ... inherit DSA/D-H param
 125 
 126     /**
 127      * Generates a random public/private key pair, with a given key
 128      * size.  Different algorithms provide different degrees of security
 129      * for the same key size, because of the "work factor" involved in
 130      * brute force attacks.  As computers become faster, it becomes
 131      * easier to perform such attacks.  Small keys are to be avoided.
 132      *
 133      * <P>Note that not all values of "keyBits" are valid for all
 134      * algorithms, and not all public key algorithms are currently
 135      * supported for use in X.509 certificates.  If the algorithm
 136      * you specified does not produce X.509 compatible keys, an
 137      * invalid key exception is thrown.
 138      *
 139      * @param keyBits the number of bits in the keys.
 140      * @exception InvalidKeyException if the environment does not
 141      *  provide X.509 public keys for this signature algorithm.
 142      */
 143     public void generate (int keyBits)
 144     throws InvalidKeyException
 145     {
 146         KeyPair pair;
 147 
 148         try {
 149             if (prng == null) {
 150                 prng = new SecureRandom();
 151             }
 152             keyGen.initialize(keyBits, prng);
 153             pair = keyGen.generateKeyPair();
 154 
 155         } catch (Exception e) {
 156             throw new IllegalArgumentException(e.getMessage());
 157         }
 158 
 159         publicKey = pair.getPublic();
 160         privateKey = pair.getPrivate();
 161 
 162         // publicKey's format must be X.509 otherwise
 163         // the whole CertGen part of this class is broken.
 164         if (!"X.509".equalsIgnoreCase(publicKey.getFormat())) {
 165             throw new IllegalArgumentException("Public key format is "
 166                 + publicKey.getFormat() + ", must be X.509");
 167         }
 168 
 169         if (sigAlg == null) {
 170             sigAlg = AlgorithmId.getDefaultSigAlgForKey(privateKey);
 171             if (sigAlg == null) {
 172                 throw new IllegalArgumentException(
 173                         "Cannot derive signature algorithm from "
 174                                 + privateKey.getAlgorithm());
 175             }
 176         }
 177     }
 178 
 179     /**
 180      * Returns the public key of the generated key pair if it is of type
 181      * <code>X509Key</code>, or null if the public key is of a different type.
 182      *
 183      * XXX Note: This behaviour is needed for backwards compatibility.
 184      * What this method really should return is the public key of the
 185      * generated key pair, regardless of whether or not it is an instance of
 186      * <code>X509Key</code>. Accordingly, the return type of this method
 187      * should be <code>PublicKey</code>.
 188      */
 189     public X509Key getPublicKey()
 190     {
 191         if (!(publicKey instanceof X509Key)) {
 192             return null;
 193         }
 194         return (X509Key)publicKey;
 195     }
 196 
 197     /**
 198      * Always returns the public key of the generated key pair. Used
 199      * by KeyTool only.
 200      *
 201      * The publicKey is not necessarily to be an instance of
 202      * X509Key in some JCA/JCE providers, for example SunPKCS11.
 203      */
 204     public PublicKey getPublicKeyAnyway() {
 205         return publicKey;
 206     }
 207 
 208     /**
 209      * Returns the private key of the generated key pair.
 210      *
 211      * <P><STRONG><em>Be extremely careful when handling private keys.
 212      * When private keys are not kept secret, they lose their ability
 213      * to securely authenticate specific entities ... that is a huge
 214      * security risk!</em></STRONG>
 215      */
 216     public PrivateKey getPrivateKey ()
 217     {
 218         return privateKey;
 219     }
 220 
 221     /**
 222      * Returns a self-signed X.509v3 certificate for the public key.
 223      * The certificate is immediately valid. No extensions.
 224      *
 225      * <P>Such certificates normally are used to identify a "Certificate
 226      * Authority" (CA).  Accordingly, they will not always be accepted by
 227      * other parties.  However, such certificates are also useful when
 228      * you are bootstrapping your security infrastructure, or deploying
 229      * system prototypes.
 230      *
 231      * @param myname X.500 name of the subject (who is also the issuer)
 232      * @param firstDate the issue time of the certificate
 233      * @param validity how long the certificate should be valid, in seconds
 234      * @exception CertificateException on certificate handling errors.
 235      * @exception InvalidKeyException on key handling errors.
 236      * @exception SignatureException on signature handling errors.
 237      * @exception NoSuchAlgorithmException on unrecognized algorithms.
 238      * @exception NoSuchProviderException on unrecognized providers.
 239      */
 240     public X509Certificate getSelfCertificate (
 241             X500Name myname, Date firstDate, long validity)
 242     throws CertificateException, InvalidKeyException, SignatureException,
 243         NoSuchAlgorithmException, NoSuchProviderException
 244     {
 245         return getSelfCertificate(myname, firstDate, validity, null);
 246     }
 247 
 248     // Like above, plus a CertificateExtensions argument, which can be null.
 249     public X509Certificate getSelfCertificate (X500Name myname, Date firstDate,
 250             long validity, CertificateExtensions ext)
 251     throws CertificateException, InvalidKeyException, SignatureException,
 252         NoSuchAlgorithmException, NoSuchProviderException
 253     {
 254         X509CertImpl    cert;
 255         Date            lastDate;
 256 
 257         try {
 258             lastDate = new Date ();
 259             lastDate.setTime (firstDate.getTime () + validity * 1000);
 260 
 261             CertificateValidity interval =
 262                                    new CertificateValidity(firstDate,lastDate);
 263 
 264             X509CertInfo info = new X509CertInfo();
 265             // Add all mandatory attributes
 266             info.set(X509CertInfo.VERSION,
 267                      new CertificateVersion(CertificateVersion.V3));
 268             info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(
 269                     new java.util.Random().nextInt() & 0x7fffffff));
 270             AlgorithmId algID = AlgorithmId.get(sigAlg);
 271             info.set(X509CertInfo.ALGORITHM_ID,
 272                      new CertificateAlgorithmId(algID));
 273             info.set(X509CertInfo.SUBJECT, myname);
 274             info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));
 275             info.set(X509CertInfo.VALIDITY, interval);
 276             info.set(X509CertInfo.ISSUER, myname);
 277             if (ext != null) info.set(X509CertInfo.EXTENSIONS, ext);
 278 
 279             cert = new X509CertImpl(info);
 280             cert.sign(privateKey, this.sigAlg);
 281 
 282             return (X509Certificate)cert;
 283 
 284         } catch (IOException e) {
 285              throw new CertificateEncodingException("getSelfCert: " +
 286                                                     e.getMessage());
 287         }
 288     }
 289 
 290     // Keep the old method
 291     public X509Certificate getSelfCertificate (X500Name myname, long validity)
 292     throws CertificateException, InvalidKeyException, SignatureException,
 293         NoSuchAlgorithmException, NoSuchProviderException
 294     {
 295         return getSelfCertificate(myname, new Date(), validity);
 296     }
 297 
 298     /**
 299      * Returns a PKCS #10 certificate request.  The caller uses either
 300      * <code>PKCS10.print</code> or <code>PKCS10.toByteArray</code>
 301      * operations on the result, to get the request in an appropriate
 302      * transmission format.
 303      *
 304      * <P>PKCS #10 certificate requests are sent, along with some proof
 305      * of identity, to Certificate Authorities (CAs) which then issue
 306      * X.509 public key certificates.
 307      *
 308      * @param myname X.500 name of the subject
 309      * @exception InvalidKeyException on key handling errors.
 310      * @exception SignatureException on signature handling errors.
 311      */
 312     public PKCS10 getCertRequest (X500Name myname)
 313     throws InvalidKeyException, SignatureException
 314     {
 315         PKCS10  req = new PKCS10 (publicKey);
 316 
 317         try {
 318             Signature signature = Signature.getInstance(sigAlg);
 319             signature.initSign (privateKey);
 320             req.encodeAndSign(myname, signature);
 321 
 322         } catch (CertificateException e) {
 323             throw new SignatureException (sigAlg + " CertificateException");
 324 
 325         } catch (IOException e) {
 326             throw new SignatureException (sigAlg + " IOException");
 327 
 328         } catch (NoSuchAlgorithmException e) {
 329             // "can't happen"
 330             throw new SignatureException (sigAlg + " unavailable?");
 331         }
 332         return req;
 333     }
 334 
 335     private SecureRandom        prng;
 336     private String              sigAlg;
 337     private KeyPairGenerator    keyGen;
 338     private PublicKey           publicKey;
 339     private PrivateKey          privateKey;
 340 }