| /* | |
| * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.security.tools.keytool; | |
| import java.io.IOException; | |
| import java.security.cert.X509Certificate; | |
| import java.security.cert.CertificateException; | |
| import java.security.cert.CertificateEncodingException; | |
| import java.security.*; | |
| import java.security.spec.ECGenParameterSpec; | |
| import java.security.spec.NamedParameterSpec; | |
| import java.util.Calendar; | |
| import java.util.Date; | |
| import java.util.GregorianCalendar; | |
| import java.util.TimeZone; | |
| import sun.security.pkcs10.PKCS10; | |
| import sun.security.util.SignatureUtil; | |
| import sun.security.x509.*; | |
| /** | |
| * Generate a pair of keys, and provide access to them. This class is | |
| * provided primarily for ease of use. | |
| * | |
| * <P>This provides some simple certificate management functionality. | |
| * Specifically, it allows you to create self-signed X.509 certificates | |
| * as well as PKCS 10 based certificate signing requests. | |
| * | |
| * <P>Keys for some public key signature algorithms have algorithm | |
| * parameters, such as DSS/DSA. Some sites' Certificate Authorities | |
| * adopt fixed algorithm parameters, which speeds up some operations | |
| * including key generation and signing. <em>At this time, this interface | |
| * supports initializing with a named group.</em> | |
| * | |
| * <P>Also, note that at this time only signature-capable keys may be | |
| * acquired through this interface. Diffie-Hellman keys, used for secure | |
| * key exchange, may be supported later. | |
| * | |
| * @author David Brownell | |
| * @author Hemma Prafullchandra | |
| * @see PKCS10 | |
| * @see X509CertImpl | |
| */ | |
| public final class CertAndKeyGen { | |
| /** | |
| * Creates a CertAndKeyGen object for a particular key type | |
| * and signature algorithm. | |
| * | |
| * @param keyType type of key, e.g. "RSA", "DSA" | |
| * @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA", | |
| * "MD2WithRSA", "SHAwithDSA". If set to null, a default | |
| * algorithm matching the private key will be chosen after | |
| * the first keypair is generated. | |
| * @exception NoSuchAlgorithmException on unrecognized algorithms. | |
| */ | |
| public CertAndKeyGen (String keyType, String sigAlg) | |
| throws NoSuchAlgorithmException | |
| { | |
| keyGen = KeyPairGenerator.getInstance(keyType); | |
| this.sigAlg = sigAlg; | |
| this.keyType = keyType; | |
| } | |
| /** | |
| * @see #CertAndKeyGen(String, String, String, PrivateKey, X500Name) | |
| */ | |
| public CertAndKeyGen (String keyType, String sigAlg, String providerName) | |
| throws NoSuchAlgorithmException, NoSuchProviderException | |
| { | |
| this(keyType, sigAlg, providerName, null, null); | |
| } | |
| /** | |
| * Creates a CertAndKeyGen object for a particular key type, | |
| * signature algorithm, and provider. The newly generated cert will | |
| * be signed by the signer's private key when it is provided. | |
| * | |
| * @param keyType type of key, e.g. "RSA", "DSA", "X25519", "DH", etc. | |
| * @param sigAlg name of the signature algorithm, e.g. "SHA384WithRSA", | |
| * "SHA256withDSA", etc. If set to null, a default | |
| * algorithm matching the private key or signer's private | |
| * key will be chosen after the first keypair is generated. | |
| * @param providerName name of the provider | |
| * @param signerPrivateKey (optional) signer's private key | |
| * @param signerSubjectName (optional) signer's subject name | |
| * @exception NoSuchAlgorithmException on unrecognized algorithms. | |
| * @exception NoSuchProviderException on unrecognized providers. | |
| */ | |
| public CertAndKeyGen(String keyType, String sigAlg, String providerName, | |
| PrivateKey signerPrivateKey, X500Name signerSubjectName) | |
| throws NoSuchAlgorithmException, NoSuchProviderException | |
| { | |
| if (providerName == null) { | |
| keyGen = KeyPairGenerator.getInstance(keyType); | |
| } else { | |
| try { | |
| keyGen = KeyPairGenerator.getInstance(keyType, providerName); | |
| } catch (Exception e) { | |
| // try first available provider instead | |
| keyGen = KeyPairGenerator.getInstance(keyType); | |
| } | |
| } | |
| this.sigAlg = sigAlg; | |
| this.keyType = keyType; | |
| this.signerPrivateKey = signerPrivateKey; | |
| this.signerSubjectName = signerSubjectName; | |
| this.signerFlag = signerPrivateKey != null; | |
| } | |
| /** | |
| * Sets the source of random numbers used when generating keys. | |
| * If you do not provide one, a system default facility is used. | |
| * You may wish to provide your own source of random numbers | |
| * to get a reproducible sequence of keys and signatures, or | |
| * because you may be able to take advantage of strong sources | |
| * of randomness/entropy in your environment. | |
| */ | |
| public void setRandom (SecureRandom generator) | |
| { | |
| prng = generator; | |
| } | |
| public void generate(String name) { | |
| try { | |
| if (prng == null) { | |
| prng = new SecureRandom(); | |
| } | |
| try { | |
| keyGen.initialize(new NamedParameterSpec(name), prng); | |
| } catch (InvalidAlgorithmParameterException e) { | |
| if (keyType.equalsIgnoreCase("EC")) { | |
| // EC has another NamedParameterSpec | |
| keyGen.initialize(new ECGenParameterSpec(name), prng); | |
| } else { | |
| throw e; | |
| } | |
| } | |
| } catch (Exception e) { | |
| throw new IllegalArgumentException(e.getMessage(), e); | |
| } | |
| generateInternal(); | |
| } | |
| // want "public void generate (X509Certificate)" ... inherit DSA/D-H param | |
| public void generate(int keyBits) { | |
| if (keyBits != -1) { | |
| try { | |
| if (prng == null) { | |
| prng = new SecureRandom(); | |
| } | |
| keyGen.initialize(keyBits, prng); | |
| } catch (Exception e) { | |
| throw new IllegalArgumentException(e.getMessage(), e); | |
| } | |
| } | |
| generateInternal(); | |
| } | |
| /** | |
| * Generates a random public/private key pair. | |
| * | |
| * <P>Note that not all public key algorithms are currently | |
| * supported for use in X.509 certificates. If the algorithm | |
| * you specified does not produce X.509 compatible keys, an | |
| * invalid key exception is thrown. | |
| * | |
| * @exception IllegalArgumentException if the environment does not | |
| * provide X.509 public keys for this signature algorithm. | |
| */ | |
| private void generateInternal() { | |
| KeyPair pair = keyGen.generateKeyPair(); | |
| publicKey = pair.getPublic(); | |
| privateKey = pair.getPrivate(); | |
| // publicKey's format must be X.509 otherwise | |
| // the whole CertGen part of this class is broken. | |
| if (!"X.509".equalsIgnoreCase(publicKey.getFormat())) { | |
| throw new IllegalArgumentException("Public key format is " | |
| + publicKey.getFormat() + ", must be X.509"); | |
| } | |
| if (sigAlg == null) { | |
| if (signerFlag) { | |
| sigAlg = SignatureUtil.getDefaultSigAlgForKey(signerPrivateKey); | |
| if (sigAlg == null) { | |
| throw new IllegalArgumentException( | |
| "Cannot derive signature algorithm from " | |
| + signerPrivateKey.getAlgorithm()); | |
| } | |
| } else { | |
| sigAlg = SignatureUtil.getDefaultSigAlgForKey(privateKey); | |
| if (sigAlg == null) { | |
| throw new IllegalArgumentException( | |
| "Cannot derive signature algorithm from " | |
| + privateKey.getAlgorithm()); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Returns the public key of the generated key pair if it is of type | |
| * <code>X509Key</code>, or null if the public key is of a different type. | |
| * | |
| * XXX Note: This behaviour is needed for backwards compatibility. | |
| * What this method really should return is the public key of the | |
| * generated key pair, regardless of whether it is an instance of | |
| * <code>X509Key</code>. Accordingly, the return type of this method | |
| * should be <code>PublicKey</code>. | |
| */ | |
| public X509Key getPublicKey() | |
| { | |
| if (!(publicKey instanceof X509Key)) { | |
| return null; | |
| } | |
| return (X509Key)publicKey; | |
| } | |
| /** | |
| * Always returns the public key of the generated key pair. Used | |
| * by KeyTool only. | |
| * | |
| * The publicKey is not necessarily to be an instance of | |
| * X509Key in some JCA/JCE providers, for example SunPKCS11. | |
| */ | |
| public PublicKey getPublicKeyAnyway() { | |
| return publicKey; | |
| } | |
| /** | |
| * Returns the private key of the generated key pair. | |
| * | |
| * <P><STRONG><em>Be extremely careful when handling private keys. | |
| * When private keys are not kept secret, they lose their ability | |
| * to securely authenticate specific entities ... that is a huge | |
| * security risk!</em></STRONG> | |
| */ | |
| public PrivateKey getPrivateKey () | |
| { | |
| return privateKey; | |
| } | |
| /** | |
| * Returns a self-signed X.509v3 certificate for the public key. | |
| * The certificate is immediately valid. No extensions. | |
| * | |
| * <P>Such certificates normally are used to identify a "Certificate | |
| * Authority" (CA). Accordingly, they will not always be accepted by | |
| * other parties. However, such certificates are also useful when | |
| * you are bootstrapping your security infrastructure, or deploying | |
| * system prototypes. | |
| * | |
| * @param myname X.500 name of the subject (who is also the issuer) | |
| * @param firstDate the issue time of the certificate | |
| * @param validity how long the certificate should be valid, in seconds | |
| * @exception CertificateException on certificate handling errors. | |
| * @exception InvalidKeyException on key handling errors. | |
| * @exception SignatureException on signature handling errors. | |
| * @exception NoSuchAlgorithmException on unrecognized algorithms. | |
| * @exception NoSuchProviderException on unrecognized providers. | |
| */ | |
| public X509Certificate getSelfCertificate ( | |
| X500Name myname, Date firstDate, long validity) | |
| throws CertificateException, InvalidKeyException, SignatureException, | |
| NoSuchAlgorithmException, NoSuchProviderException | |
| { | |
| return getSelfCertificate(myname, firstDate, validity, null); | |
| } | |
| // Like above, plus a CertificateExtensions argument, which can be null. | |
| // Create a self-signed certificate, or a certificate that is signed by | |
| // a signer when the signer's private key is provided. | |
| public X509Certificate getSelfCertificate (X500Name myname, Date firstDate, | |
| long validity, CertificateExtensions ext) | |
| throws CertificateException, InvalidKeyException, SignatureException, | |
| NoSuchAlgorithmException, NoSuchProviderException | |
| { | |
| X509CertImpl cert; | |
| Date lastDate; | |
| try { | |
| lastDate = new Date (); | |
| lastDate.setTime (firstDate.getTime () + validity * 1000); | |
| Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC")); | |
| c.setTime(lastDate); | |
| if (c.get(Calendar.YEAR) > 9999) { | |
| throw new CertificateException("Validity period ends at calendar year " + | |
| c.get(Calendar.YEAR) + " which is greater than 9999"); | |
| } | |
| CertificateValidity interval = | |
| new CertificateValidity(firstDate,lastDate); | |
| X509CertInfo info = new X509CertInfo(); | |
| // Add all mandatory attributes | |
| info.setVersion(new CertificateVersion(CertificateVersion.V3)); | |
| if (prng == null) { | |
| prng = new SecureRandom(); | |
| } | |
| info.setSerialNumber(CertificateSerialNumber.newRandom64bit(prng)); | |
| info.setSubject(myname); | |
| info.setKey(new CertificateX509Key(publicKey)); | |
| info.setValidity(interval); | |
| if (signerFlag) { | |
| // use signer's subject name to set the issuer name | |
| info.setIssuer(signerSubjectName); | |
| } else { | |
| info.setIssuer(myname); | |
| } | |
| if (ext != null) info.setExtensions(ext); | |
| if (signerFlag) { | |
| // use signer's private key to sign | |
| cert = X509CertImpl.newSigned(info, signerPrivateKey, sigAlg); | |
| } else { | |
| cert = X509CertImpl.newSigned(info, privateKey, sigAlg); | |
| } | |
| return cert; | |
| } catch (IOException e) { | |
| throw new CertificateEncodingException("getSelfCert: " + | |
| e.getMessage(), e); | |
| } | |
| } | |
| // Keep the old method | |
| public X509Certificate getSelfCertificate (X500Name myname, long validity) | |
| throws CertificateException, InvalidKeyException, SignatureException, | |
| NoSuchAlgorithmException, NoSuchProviderException | |
| { | |
| return getSelfCertificate(myname, new Date(), validity); | |
| } | |
| private SecureRandom prng; | |
| private final String keyType; | |
| private String sigAlg; | |
| private KeyPairGenerator keyGen; | |
| private PublicKey publicKey; | |
| private PrivateKey privateKey; | |
| private boolean signerFlag; | |
| private PrivateKey signerPrivateKey; | |
| private X500Name signerSubjectName; | |
| } |