Path: blob/master/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java
67847 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.tools.keytool;2627import java.io.IOException;28import java.security.cert.X509Certificate;29import java.security.cert.CertificateException;30import java.security.cert.CertificateEncodingException;31import java.security.*;32import java.security.spec.ECGenParameterSpec;33import java.security.spec.NamedParameterSpec;34import java.util.Calendar;35import java.util.Date;36import java.util.GregorianCalendar;37import java.util.TimeZone;3839import sun.security.pkcs10.PKCS10;40import sun.security.util.SignatureUtil;41import sun.security.x509.*;4243/**44* Generate a pair of keys, and provide access to them. This class is45* provided primarily for ease of use.46*47* <P>This provides some simple certificate management functionality.48* Specifically, it allows you to create self-signed X.509 certificates49* as well as PKCS 10 based certificate signing requests.50*51* <P>Keys for some public key signature algorithms have algorithm52* parameters, such as DSS/DSA. Some sites' Certificate Authorities53* adopt fixed algorithm parameters, which speeds up some operations54* including key generation and signing. <em>At this time, this interface55* supports initializing with a named group.</em>56*57* <P>Also, note that at this time only signature-capable keys may be58* acquired through this interface. Diffie-Hellman keys, used for secure59* key exchange, may be supported later.60*61* @author David Brownell62* @author Hemma Prafullchandra63* @see PKCS1064* @see X509CertImpl65*/66public final class CertAndKeyGen {67/**68* Creates a CertAndKeyGen object for a particular key type69* and signature algorithm.70*71* @param keyType type of key, e.g. "RSA", "DSA"72* @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA",73* "MD2WithRSA", "SHAwithDSA". If set to null, a default74* algorithm matching the private key will be chosen after75* the first keypair is generated.76* @exception NoSuchAlgorithmException on unrecognized algorithms.77*/78public CertAndKeyGen (String keyType, String sigAlg)79throws NoSuchAlgorithmException80{81keyGen = KeyPairGenerator.getInstance(keyType);82this.sigAlg = sigAlg;83this.keyType = keyType;84}8586/**87* @see #CertAndKeyGen(String, String, String, PrivateKey, X500Name)88*/89public CertAndKeyGen (String keyType, String sigAlg, String providerName)90throws NoSuchAlgorithmException, NoSuchProviderException91{92this(keyType, sigAlg, providerName, null, null);93}9495/**96* Creates a CertAndKeyGen object for a particular key type,97* signature algorithm, and provider. The newly generated cert will98* be signed by the signer's private key when it is provided.99*100* @param keyType type of key, e.g. "RSA", "DSA", "X25519", "DH", etc.101* @param sigAlg name of the signature algorithm, e.g. "SHA384WithRSA",102* "SHA256withDSA", etc. If set to null, a default103* algorithm matching the private key or signer's private104* key will be chosen after the first keypair is generated.105* @param providerName name of the provider106* @param signerPrivateKey (optional) signer's private key107* @param signerSubjectName (optional) signer's subject name108* @exception NoSuchAlgorithmException on unrecognized algorithms.109* @exception NoSuchProviderException on unrecognized providers.110*/111public CertAndKeyGen(String keyType, String sigAlg, String providerName,112PrivateKey signerPrivateKey, X500Name signerSubjectName)113throws NoSuchAlgorithmException, NoSuchProviderException114{115if (providerName == null) {116keyGen = KeyPairGenerator.getInstance(keyType);117} else {118try {119keyGen = KeyPairGenerator.getInstance(keyType, providerName);120} catch (Exception e) {121// try first available provider instead122keyGen = KeyPairGenerator.getInstance(keyType);123}124}125this.sigAlg = sigAlg;126this.keyType = keyType;127this.signerPrivateKey = signerPrivateKey;128this.signerSubjectName = signerSubjectName;129this.signerFlag = signerPrivateKey != null;130}131132/**133* Sets the source of random numbers used when generating keys.134* If you do not provide one, a system default facility is used.135* You may wish to provide your own source of random numbers136* to get a reproducible sequence of keys and signatures, or137* because you may be able to take advantage of strong sources138* of randomness/entropy in your environment.139*/140public void setRandom (SecureRandom generator)141{142prng = generator;143}144145public void generate(String name) {146try {147if (prng == null) {148prng = new SecureRandom();149}150try {151keyGen.initialize(new NamedParameterSpec(name), prng);152} catch (InvalidAlgorithmParameterException e) {153if (keyType.equalsIgnoreCase("EC")) {154// EC has another NamedParameterSpec155keyGen.initialize(new ECGenParameterSpec(name), prng);156} else {157throw e;158}159}160161} catch (Exception e) {162throw new IllegalArgumentException(e.getMessage());163}164generateInternal();165}166167// want "public void generate (X509Certificate)" ... inherit DSA/D-H param168169public void generate(int keyBits) {170if (keyBits != -1) {171try {172if (prng == null) {173prng = new SecureRandom();174}175keyGen.initialize(keyBits, prng);176177} catch (Exception e) {178throw new IllegalArgumentException(e.getMessage());179}180}181generateInternal();182}183184/**185* Generates a random public/private key pair.186*187* <P>Note that not all public key algorithms are currently188* supported for use in X.509 certificates. If the algorithm189* you specified does not produce X.509 compatible keys, an190* invalid key exception is thrown.191*192* @exception IllegalArgumentException if the environment does not193* provide X.509 public keys for this signature algorithm.194*/195private void generateInternal() {196KeyPair pair = keyGen.generateKeyPair();197publicKey = pair.getPublic();198privateKey = pair.getPrivate();199200// publicKey's format must be X.509 otherwise201// the whole CertGen part of this class is broken.202if (!"X.509".equalsIgnoreCase(publicKey.getFormat())) {203throw new IllegalArgumentException("Public key format is "204+ publicKey.getFormat() + ", must be X.509");205}206207if (sigAlg == null) {208if (signerFlag) {209sigAlg = SignatureUtil.getDefaultSigAlgForKey(signerPrivateKey);210if (sigAlg == null) {211throw new IllegalArgumentException(212"Cannot derive signature algorithm from "213+ signerPrivateKey.getAlgorithm());214}215} else {216sigAlg = SignatureUtil.getDefaultSigAlgForKey(privateKey);217if (sigAlg == null) {218throw new IllegalArgumentException(219"Cannot derive signature algorithm from "220+ privateKey.getAlgorithm());221}222}223}224}225226/**227* Returns the public key of the generated key pair if it is of type228* <code>X509Key</code>, or null if the public key is of a different type.229*230* XXX Note: This behaviour is needed for backwards compatibility.231* What this method really should return is the public key of the232* generated key pair, regardless of whether or not it is an instance of233* <code>X509Key</code>. Accordingly, the return type of this method234* should be <code>PublicKey</code>.235*/236public X509Key getPublicKey()237{238if (!(publicKey instanceof X509Key)) {239return null;240}241return (X509Key)publicKey;242}243244/**245* Always returns the public key of the generated key pair. Used246* by KeyTool only.247*248* The publicKey is not necessarily to be an instance of249* X509Key in some JCA/JCE providers, for example SunPKCS11.250*/251public PublicKey getPublicKeyAnyway() {252return publicKey;253}254255/**256* Returns the private key of the generated key pair.257*258* <P><STRONG><em>Be extremely careful when handling private keys.259* When private keys are not kept secret, they lose their ability260* to securely authenticate specific entities ... that is a huge261* security risk!</em></STRONG>262*/263public PrivateKey getPrivateKey ()264{265return privateKey;266}267268/**269* Returns a self-signed X.509v3 certificate for the public key.270* The certificate is immediately valid. No extensions.271*272* <P>Such certificates normally are used to identify a "Certificate273* Authority" (CA). Accordingly, they will not always be accepted by274* other parties. However, such certificates are also useful when275* you are bootstrapping your security infrastructure, or deploying276* system prototypes.277*278* @param myname X.500 name of the subject (who is also the issuer)279* @param firstDate the issue time of the certificate280* @param validity how long the certificate should be valid, in seconds281* @exception CertificateException on certificate handling errors.282* @exception InvalidKeyException on key handling errors.283* @exception SignatureException on signature handling errors.284* @exception NoSuchAlgorithmException on unrecognized algorithms.285* @exception NoSuchProviderException on unrecognized providers.286*/287public X509Certificate getSelfCertificate (288X500Name myname, Date firstDate, long validity)289throws CertificateException, InvalidKeyException, SignatureException,290NoSuchAlgorithmException, NoSuchProviderException291{292return getSelfCertificate(myname, firstDate, validity, null);293}294295// Like above, plus a CertificateExtensions argument, which can be null.296// Create a self-signed certificate, or a certificate that is signed by297// a signer when the signer's private key is provided.298public X509Certificate getSelfCertificate (X500Name myname, Date firstDate,299long validity, CertificateExtensions ext)300throws CertificateException, InvalidKeyException, SignatureException,301NoSuchAlgorithmException, NoSuchProviderException302{303X509CertImpl cert;304Date lastDate;305306try {307lastDate = new Date ();308lastDate.setTime (firstDate.getTime () + validity * 1000);309Calendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC"));310c.setTime(lastDate);311if (c.get(Calendar.YEAR) > 9999) {312throw new CertificateException("Validity period ends at calendar year " +313c.get(Calendar.YEAR) + " which is greater than 9999");314}315316CertificateValidity interval =317new CertificateValidity(firstDate,lastDate);318319X509CertInfo info = new X509CertInfo();320// Add all mandatory attributes321info.set(X509CertInfo.VERSION,322new CertificateVersion(CertificateVersion.V3));323if (prng == null) {324prng = new SecureRandom();325}326info.set(X509CertInfo.SERIAL_NUMBER,327CertificateSerialNumber.newRandom64bit(prng));328info.set(X509CertInfo.SUBJECT, myname);329info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));330info.set(X509CertInfo.VALIDITY, interval);331if (signerFlag) {332// use signer's subject name to set the issuer name333info.set(X509CertInfo.ISSUER, signerSubjectName);334} else {335info.set(X509CertInfo.ISSUER, myname);336}337if (ext != null) info.set(X509CertInfo.EXTENSIONS, ext);338339cert = new X509CertImpl(info);340if (signerFlag) {341// use signer's private key to sign342cert.sign(signerPrivateKey, sigAlg);343} else {344cert.sign(privateKey, sigAlg);345}346347return cert;348349} catch (IOException e) {350throw new CertificateEncodingException("getSelfCert: " +351e.getMessage());352}353}354355// Keep the old method356public X509Certificate getSelfCertificate (X500Name myname, long validity)357throws CertificateException, InvalidKeyException, SignatureException,358NoSuchAlgorithmException, NoSuchProviderException359{360return getSelfCertificate(myname, new Date(), validity);361}362363private SecureRandom prng;364private String keyType;365private String sigAlg;366private KeyPairGenerator keyGen;367private PublicKey publicKey;368private PrivateKey privateKey;369private boolean signerFlag;370private PrivateKey signerPrivateKey;371private X500Name signerSubjectName;372}373374375