# HG changeset patch # User weijun # Date 1542156385 -28800 # Node ID ddcbc20e8c6a329ba24936fab6864b57f26fe5d6 # Parent 3b91496409fce2999811a4270942524f15bb43bc 8213400: Support choosing group name in keytool keypair generation Reviewed-by: apetcher, xuelei diff -r 3b91496409fc -r ddcbc20e8c6a src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java --- a/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java Tue Nov 13 16:17:24 2018 -0800 +++ b/src/java.base/share/classes/sun/security/tools/keytool/CertAndKeyGen.java Wed Nov 14 08:46:25 2018 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2018, 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 @@ -30,6 +30,8 @@ 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.Date; import sun.security.pkcs10.PKCS10; @@ -48,8 +50,7 @@ * parameters, such as DSS/DSA. Some sites' Certificate Authorities * adopt fixed algorithm parameters, which speeds up some operations * including key generation and signing. At this time, this interface - * does not provide a way to provide such algorithm parameters, e.g. - * by providing the CA certificate which includes those parameters. + * supports initializing with a named group. * *

Also, note that at this time only signature-capable keys may be * acquired through this interface. Diffie-Hellman keys, used for secure @@ -77,6 +78,7 @@ { keyGen = KeyPairGenerator.getInstance(keyType); this.sigAlg = sigAlg; + this.keyType = keyType; } /** @@ -106,6 +108,7 @@ } } this.sigAlg = sigAlg; + this.keyType = keyType; } /** @@ -121,41 +124,58 @@ 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()); + } + 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()); + } + } + generateInternal(); + } + /** - * Generates a random public/private key pair, with a given key - * size. Different algorithms provide different degrees of security - * for the same key size, because of the "work factor" involved in - * brute force attacks. As computers become faster, it becomes - * easier to perform such attacks. Small keys are to be avoided. + * Generates a random public/private key pair. * - *

Note that not all values of "keyBits" are valid for all - * algorithms, and not all public key algorithms are currently + *

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. * - * @param keyBits the number of bits in the keys. - * @exception InvalidKeyException if the environment does not + * @exception IllegalArgumentException if the environment does not * provide X.509 public keys for this signature algorithm. */ - public void generate (int keyBits) - throws InvalidKeyException - { - KeyPair pair; - - try { - if (prng == null) { - prng = new SecureRandom(); - } - keyGen.initialize(keyBits, prng); - pair = keyGen.generateKeyPair(); - - } catch (Exception e) { - throw new IllegalArgumentException(e.getMessage()); - } - + private void generateInternal() { + KeyPair pair = keyGen.generateKeyPair(); publicKey = pair.getPublic(); privateKey = pair.getPrivate(); @@ -333,6 +353,7 @@ } private SecureRandom prng; + private String keyType; private String sigAlg; private KeyPairGenerator keyGen; private PublicKey publicKey; diff -r 3b91496409fc -r ddcbc20e8c6a src/java.base/share/classes/sun/security/tools/keytool/Main.java --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java Tue Nov 13 16:17:24 2018 -0800 +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java Wed Nov 14 08:46:25 2018 +0800 @@ -28,13 +28,13 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Path; +import java.security.AlgorithmParameters; import java.security.CodeSigner; import java.security.CryptoPrimitive; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.Key; -import java.security.NoSuchProviderException; import java.security.PublicKey; import java.security.PrivateKey; import java.security.Signature; @@ -68,6 +68,7 @@ import javax.security.auth.x500.X500Principal; import java.util.Base64; +import sun.security.util.ECKeySizeParameterSpec; import sun.security.util.KeyUtil; import sun.security.util.ObjectIdentifier; import sun.security.pkcs10.PKCS10; @@ -116,6 +117,7 @@ private String keyAlgName = null; private boolean verbose = false; private int keysize = -1; + private String groupName = null; private boolean rfc = false; private long validity = (long)90; private String alias = null; @@ -202,7 +204,7 @@ STORETYPE, PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH, V, PROTECTED), GENKEYPAIR("Generates.a.key.pair", - ALIAS, KEYALG, KEYSIZE, SIGALG, DESTALIAS, DNAME, + ALIAS, KEYALG, KEYSIZE, CURVENAME, SIGALG, DESTALIAS, DNAME, STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER, PROVIDERCLASS, PROVIDERPATH, V, PROTECTED), @@ -314,6 +316,7 @@ // in the optionsSet.contains() block in parseArgs(). enum Option { ALIAS("alias", "", "alias.name.of.the.entry.to.process"), + CURVENAME("groupname", "", "groupname.option.help"), DESTALIAS("destalias", "", "destination.alias"), DESTKEYPASS("destkeypass", "", "destination.key.password"), DESTKEYSTORE("destkeystore", "", "destination.keystore.name"), @@ -586,6 +589,8 @@ dname = args[++i]; } else if (collator.compare(flags, "-keysize") == 0) { keysize = Integer.parseInt(args[++i]); + } else if (collator.compare(flags, "-groupname") == 0) { + groupName = args[++i]; } else if (collator.compare(flags, "-keyalg") == 0) { keyAlgName = args[++i]; } else if (collator.compare(flags, "-sigalg") == 0) { @@ -1119,7 +1124,7 @@ if (keyAlgName == null) { keyAlgName = "DSA"; } - doGenKeyPair(alias, dname, keyAlgName, keysize, sigAlgName); + doGenKeyPair(alias, dname, keyAlgName, keysize, groupName, sigAlgName); kssave = true; } else if (command == GENSECKEY) { if (keyAlgName == null) { @@ -1793,16 +1798,28 @@ * Creates a new key pair and self-signed certificate. */ private void doGenKeyPair(String alias, String dname, String keyAlgName, - int keysize, String sigAlgName) + int keysize, String groupName, String sigAlgName) throws Exception { - if (keysize == -1) { - if ("EC".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE; - } else if ("RSA".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE; - } else if ("DSA".equalsIgnoreCase(keyAlgName)) { - keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE; + if (groupName != null) { + if (keysize != -1) { + throw new Exception(rb.getString("groupname.keysize.coexist")); + } + } else { + if (keysize == -1) { + if ("EC".equalsIgnoreCase(keyAlgName)) { + keysize = SecurityProviderConstants.DEF_EC_KEY_SIZE; + } else if ("RSA".equalsIgnoreCase(keyAlgName)) { + keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE; + } else if ("DSA".equalsIgnoreCase(keyAlgName)) { + keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE; + } + } else { + if ("EC".equalsIgnoreCase(keyAlgName)) { + weakWarnings.add(String.format( + rb.getString("deprecate.keysize.for.ec"), + ecGroupNameForSize(keysize))); + } } } @@ -1829,7 +1846,13 @@ x500Name = new X500Name(dname); } - keypair.generate(keysize); + if (groupName != null) { + keypair.generate(groupName); + } else { + // This covers keysize both specified and unspecified + keypair.generate(keysize); + } + PrivateKey privKey = keypair.getPrivateKey(); CertificateExtensions ext = createV3Extensions( @@ -1861,6 +1884,13 @@ keyStore.setKeyEntry(alias, privKey, keyPass, chain); } + private String ecGroupNameForSize(int size) throws Exception { + AlgorithmParameters ap = AlgorithmParameters.getInstance("EC"); + ap.init(new ECKeySizeParameterSpec(size)); + // The following line assumes the toString value is "name (oid)" + return ap.toString().split(" ")[0]; + } + /** * Clones an entry * @param orig original alias diff -r 3b91496409fc -r ddcbc20e8c6a src/java.base/share/classes/sun/security/tools/keytool/Resources.java --- a/src/java.base/share/classes/sun/security/tools/keytool/Resources.java Tue Nov 13 16:17:24 2018 -0800 +++ b/src/java.base/share/classes/sun/security/tools/keytool/Resources.java Wed Nov 14 08:46:25 2018 +0800 @@ -99,6 +99,8 @@ // keytool: help: options {"alias.name.of.the.entry.to.process", "alias name of the entry to process"}, //-alias + {"groupname.option.help", + "Group name. For example, an Elliptic Curve name."}, //-groupname {"destination.alias", "destination alias"}, //-destalias {"destination.key.password", @@ -290,6 +292,10 @@ "Alias <{0}> does not exist"}, {"Alias.alias.has.no.certificate", "Alias <{0}> has no certificate"}, + {"groupname.keysize.coexist", + "Cannot specify both -groupname and -keysize"}, + {"deprecate.keysize.for.ec", + "Specifying -keysize for generating EC keys is deprecated, please use \"-groupname %s\" instead."}, {"Key.pair.not.generated.alias.alias.already.exists", "Key pair not generated, alias <{0}> already exists"}, {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", diff -r 3b91496409fc -r ddcbc20e8c6a test/jdk/sun/security/tools/keytool/GroupName.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/tools/keytool/GroupName.java Wed Nov 14 08:46:25 2018 +0800 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018, 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. + * + * 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. + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.SecurityTools; +import jdk.test.lib.process.OutputAnalyzer; + +import java.io.File; +import java.security.KeyStore; +import java.security.interfaces.ECKey; + +/** + * @test + * @bug 8213400 + * @summary Support choosing group name in keytool keypair generation + * @library /test/lib + */ + +public class GroupName { + + private static final String COMMON = "-keystore ks " + + "-storepass changeit -keypass changeit -debug"; + + public static void main(String[] args) throws Throwable { + gen("a", "-keyalg RSA -groupname secp256r1") + .shouldHaveExitValue(1); + + gen("b", "-keyalg EC") + .shouldHaveExitValue(0) + .shouldNotContain("Specifying -keysize for generating EC keys is deprecated"); + checkCurveName("b", "secp256r1"); + + gen("c", "-keyalg EC -keysize 256") + .shouldHaveExitValue(0) + .shouldContain("Specifying -keysize for generating EC keys is deprecated") + .shouldContain("please use \"-groupname secp256r1\" instead."); + checkCurveName("c", "secp256r1"); + + gen("d", "-keyalg EC -keysize 256 -groupname secp256r1") + .shouldHaveExitValue(1) + .shouldContain("Cannot specify both -groupname and -keysize"); + + gen("e", "-keyalg EC -groupname secp256r1") + .shouldHaveExitValue(0) + .shouldNotContain("Specifying -keysize for generating EC keys is deprecated"); + checkCurveName("e", "secp256r1"); + + gen("f", "-keyalg EC -groupname brainpoolP256r1") + .shouldHaveExitValue(0) + .shouldNotContain("Specifying -keysize for generating EC keys is deprecated"); + checkCurveName("f", "brainpoolP256r1"); + } + + private static void checkCurveName(String a, String name) + throws Exception { + KeyStore ks = KeyStore.getInstance(new File("ks"), "changeit".toCharArray()); + ECKey key = (ECKey)ks.getCertificate(a).getPublicKey(); + // The following check is highly implementation dependent. In OpenJDK, + // params.toString() should contain all alternative names and the OID. + Asserts.assertTrue(key.getParams().toString().contains(name)); + } + + private static OutputAnalyzer kt(String cmd) throws Throwable { + return SecurityTools.keytool(COMMON + " " + cmd); + } + + private static OutputAnalyzer gen(String a, String extra) throws Throwable { + return kt("-genkeypair -alias " + a + " -dname CN=" + a + " " + extra); + } +}