8260693: Provide the support for specifying a signer in keytool -genkeypair

Reviewed-by: weijun
This commit is contained in:
Hai-May Chao 2021-04-09 01:59:59 +00:00
parent 77b16739ab
commit 719f95e504
6 changed files with 484 additions and 42 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -74,28 +74,41 @@ public final class CertAndKeyGen {
* @exception NoSuchAlgorithmException on unrecognized algorithms. * @exception NoSuchAlgorithmException on unrecognized algorithms.
*/ */
public CertAndKeyGen (String keyType, String sigAlg) public CertAndKeyGen (String keyType, String sigAlg)
throws NoSuchAlgorithmException throws NoSuchAlgorithmException
{ {
keyGen = KeyPairGenerator.getInstance(keyType); keyGen = KeyPairGenerator.getInstance(keyType);
this.sigAlg = sigAlg; this.sigAlg = sigAlg;
this.keyType = keyType; 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, * Creates a CertAndKeyGen object for a particular key type,
* signature algorithm, and provider. * 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" * @param keyType type of key, e.g. "RSA", "DSA", "X25519", "DH", etc.
* @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA", * @param sigAlg name of the signature algorithm, e.g. "SHA384WithRSA",
* "MD2WithRSA", "SHAwithDSA". If set to null, a default * "SHA256withDSA", etc. If set to null, a default
* algorithm matching the private key will be chosen after * algorithm matching the private key or signer's private
* the first keypair is generated. * key will be chosen after the first keypair is generated.
* @param providerName name of the provider * @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 NoSuchAlgorithmException on unrecognized algorithms.
* @exception NoSuchProviderException on unrecognized providers. * @exception NoSuchProviderException on unrecognized providers.
*/ */
public CertAndKeyGen (String keyType, String sigAlg, String providerName) public CertAndKeyGen(String keyType, String sigAlg, String providerName,
throws NoSuchAlgorithmException, NoSuchProviderException PrivateKey signerPrivateKey, X500Name signerSubjectName)
throws NoSuchAlgorithmException, NoSuchProviderException
{ {
if (providerName == null) { if (providerName == null) {
keyGen = KeyPairGenerator.getInstance(keyType); keyGen = KeyPairGenerator.getInstance(keyType);
@ -109,6 +122,9 @@ public final class CertAndKeyGen {
} }
this.sigAlg = sigAlg; this.sigAlg = sigAlg;
this.keyType = keyType; this.keyType = keyType;
this.signerPrivateKey = signerPrivateKey;
this.signerSubjectName = signerSubjectName;
this.signerFlag = signerPrivateKey != null;
} }
/** /**
@ -187,11 +203,20 @@ public final class CertAndKeyGen {
} }
if (sigAlg == null) { if (sigAlg == null) {
sigAlg = SignatureUtil.getDefaultSigAlgForKey(privateKey); if (signerFlag) {
if (sigAlg == null) { sigAlg = SignatureUtil.getDefaultSigAlgForKey(signerPrivateKey);
throw new IllegalArgumentException( if (sigAlg == null) {
"Cannot derive signature algorithm from " throw new IllegalArgumentException(
+ privateKey.getAlgorithm()); "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());
}
} }
} }
} }
@ -266,6 +291,8 @@ public final class CertAndKeyGen {
} }
// Like above, plus a CertificateExtensions argument, which can be 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, public X509Certificate getSelfCertificate (X500Name myname, Date firstDate,
long validity, CertificateExtensions ext) long validity, CertificateExtensions ext)
throws CertificateException, InvalidKeyException, SignatureException, throws CertificateException, InvalidKeyException, SignatureException,
@ -293,11 +320,21 @@ public final class CertAndKeyGen {
info.set(X509CertInfo.SUBJECT, myname); info.set(X509CertInfo.SUBJECT, myname);
info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey)); info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));
info.set(X509CertInfo.VALIDITY, interval); info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.ISSUER, myname); if (signerFlag) {
// use signer's subject name to set the issuer name
info.set(X509CertInfo.ISSUER, signerSubjectName);
} else {
info.set(X509CertInfo.ISSUER, myname);
}
if (ext != null) info.set(X509CertInfo.EXTENSIONS, ext); if (ext != null) info.set(X509CertInfo.EXTENSIONS, ext);
cert = new X509CertImpl(info); cert = new X509CertImpl(info);
cert.sign(privateKey, sigAlg); if (signerFlag) {
// use signer's private key to sign
cert.sign(signerPrivateKey, sigAlg);
} else {
cert.sign(privateKey, sigAlg);
}
return cert; return cert;
@ -321,4 +358,7 @@ public final class CertAndKeyGen {
private KeyPairGenerator keyGen; private KeyPairGenerator keyGen;
private PublicKey publicKey; private PublicKey publicKey;
private PrivateKey privateKey; private PrivateKey privateKey;
private boolean signerFlag;
private PrivateKey signerPrivateKey;
private X500Name signerSubjectName;
} }

View File

@ -162,6 +162,8 @@ public final class Main {
private String srcstoretype = null; private String srcstoretype = null;
private Set<char[]> passwords = new HashSet<>(); private Set<char[]> passwords = new HashSet<>();
private String startDate = null; private String startDate = null;
private String signerAlias = null;
private char[] signerKeyPass = null;
private boolean tlsInfo = false; private boolean tlsInfo = false;
@ -208,6 +210,7 @@ public final class Main {
GENKEYPAIR("Generates.a.key.pair", GENKEYPAIR("Generates.a.key.pair",
ALIAS, KEYALG, KEYSIZE, CURVENAME, SIGALG, DNAME, ALIAS, KEYALG, KEYSIZE, CURVENAME, SIGALG, DNAME,
STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE, STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE,
SIGNER, SIGNERKEYPASS,
STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER, STOREPASS, STORETYPE, PROVIDERNAME, ADDPROVIDER,
PROVIDERCLASS, PROVIDERPATH, V, PROTECTED), PROVIDERCLASS, PROVIDERPATH, V, PROTECTED),
GENSECKEY("Generates.a.secret.key", GENSECKEY("Generates.a.secret.key",
@ -352,6 +355,8 @@ public final class Main {
PROVIDERPATH("providerpath", "<list>", "provider.classpath"), PROVIDERPATH("providerpath", "<list>", "provider.classpath"),
RFC("rfc", null, "output.in.RFC.style"), RFC("rfc", null, "output.in.RFC.style"),
SIGALG("sigalg", "<alg>", "signature.algorithm.name"), SIGALG("sigalg", "<alg>", "signature.algorithm.name"),
SIGNER("signer", "<alias>", "signer.alias"),
SIGNERKEYPASS("signerkeypass", "<arg>", "signer.key.password"),
SRCALIAS("srcalias", "<alias>", "source.alias"), SRCALIAS("srcalias", "<alias>", "source.alias"),
SRCKEYPASS("srckeypass", "<arg>", "source.key.password"), SRCKEYPASS("srckeypass", "<arg>", "source.key.password"),
SRCKEYSTORE("srckeystore", "<keystore>", "source.keystore.name"), SRCKEYSTORE("srckeystore", "<keystore>", "source.keystore.name"),
@ -603,6 +608,11 @@ public final class Main {
keyAlgName = args[++i]; keyAlgName = args[++i];
} else if (collator.compare(flags, "-sigalg") == 0) { } else if (collator.compare(flags, "-sigalg") == 0) {
sigAlgName = args[++i]; sigAlgName = args[++i];
} else if (collator.compare(flags, "-signer") == 0) {
signerAlias = args[++i];
} else if (collator.compare(flags, "-signerkeypass") == 0) {
signerKeyPass = getPass(modifier, args[++i]);
passwords.add(signerKeyPass);
} else if (collator.compare(flags, "-startdate") == 0) { } else if (collator.compare(flags, "-startdate") == 0) {
startDate = args[++i]; startDate = args[++i];
} else if (collator.compare(flags, "-validity") == 0) { } else if (collator.compare(flags, "-validity") == 0) {
@ -1148,7 +1158,8 @@ public final class Main {
throw new Exception(rb.getString( throw new Exception(rb.getString(
"keyalg.option.missing.error")); "keyalg.option.missing.error"));
} }
doGenKeyPair(alias, dname, keyAlgName, keysize, groupName, sigAlgName); doGenKeyPair(alias, dname, keyAlgName, keysize, groupName, sigAlgName,
signerAlias);
kssave = true; kssave = true;
} else if (command == GENSECKEY) { } else if (command == GENSECKEY) {
if (keyAlgName == null) { if (keyAlgName == null) {
@ -1858,7 +1869,8 @@ public final class Main {
* Creates a new key pair and self-signed certificate. * Creates a new key pair and self-signed certificate.
*/ */
private void doGenKeyPair(String alias, String dname, String keyAlgName, private void doGenKeyPair(String alias, String dname, String keyAlgName,
int keysize, String groupName, String sigAlgName) int keysize, String groupName, String sigAlgName,
String signerAlias)
throws Exception throws Exception
{ {
if (groupName != null) { if (groupName != null) {
@ -1879,6 +1891,14 @@ public final class Main {
keysize = 255; keysize = 255;
} else if ("Ed448".equalsIgnoreCase(keyAlgName)) { } else if ("Ed448".equalsIgnoreCase(keyAlgName)) {
keysize = 448; keysize = 448;
} else if ("XDH".equalsIgnoreCase(keyAlgName)) {
keysize = SecurityProviderConstants.DEF_XEC_KEY_SIZE;
} else if ("X25519".equalsIgnoreCase(keyAlgName)) {
keysize = 255;
} else if ("X448".equalsIgnoreCase(keyAlgName)) {
keysize = 448;
} else if ("DH".equalsIgnoreCase(keyAlgName)) {
keysize = SecurityProviderConstants.DEF_DH_KEY_SIZE;
} }
} else { } else {
if ("EC".equalsIgnoreCase(keyAlgName)) { if ("EC".equalsIgnoreCase(keyAlgName)) {
@ -1900,9 +1920,35 @@ public final class Main {
throw new Exception(form.format(source)); throw new Exception(form.format(source));
} }
CertAndKeyGen keypair = CertAndKeyGen keypair;
new CertAndKeyGen(keyAlgName, sigAlgName, providerName); KeyIdentifier signerSubjectKeyId = null;
if (signerAlias != null) {
PrivateKey signerPrivateKey =
(PrivateKey)recoverKey(signerAlias, storePass, signerKeyPass).fst;
Certificate signerCert = keyStore.getCertificate(signerAlias);
X509CertImpl signerCertImpl;
if (signerCert instanceof X509CertImpl) {
signerCertImpl = (X509CertImpl) signerCert;
} else {
signerCertImpl = new X509CertImpl(signerCert.getEncoded());
}
X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(
X509CertImpl.NAME + "." + X509CertImpl.INFO);
X500Name signerSubjectName = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +
X509CertInfo.DN_NAME);
keypair = new CertAndKeyGen(keyAlgName, sigAlgName, providerName,
signerPrivateKey, signerSubjectName);
signerSubjectKeyId = signerCertImpl.getSubjectKeyId();
if (signerSubjectKeyId == null) {
signerSubjectKeyId = new KeyIdentifier(signerCert.getPublicKey());
}
} else {
keypair = new CertAndKeyGen(keyAlgName, sigAlgName, providerName);
}
// If DN is provided, parse it. Otherwise, prompt the user for it. // If DN is provided, parse it. Otherwise, prompt the user for it.
X500Name x500Name; X500Name x500Name;
@ -1920,34 +1966,55 @@ public final class Main {
keypair.generate(keysize); keypair.generate(keysize);
} }
PrivateKey privKey = keypair.getPrivateKey();
CertificateExtensions ext = createV3Extensions( CertificateExtensions ext = createV3Extensions(
null, null,
null, null,
v3ext, v3ext,
keypair.getPublicKeyAnyway(), keypair.getPublicKeyAnyway(),
null); signerSubjectKeyId);
X509Certificate[] chain = new X509Certificate[1]; PrivateKey privKey = keypair.getPrivateKey();
chain[0] = keypair.getSelfCertificate( X509Certificate newCert = keypair.getSelfCertificate(
x500Name, getStartDate(startDate), validity*24L*60L*60L, ext); x500Name, getStartDate(startDate), validity*24L*60L*60L, ext);
MessageFormat form = new MessageFormat(rb.getString if (signerAlias != null) {
("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for")); MessageFormat form = new MessageFormat(rb.getString
Object[] source = { ("Generating.keysize.bit.keyAlgName.key.pair.and.a.certificate.sigAlgName.issued.by.signerAlias.with.a.validity.of.validality.days.for"));
groupName == null ? keysize : KeyUtil.getKeySize(privKey), Object[] source = {
fullDisplayAlgName(privKey), groupName == null ? keysize : KeyUtil.getKeySize(privKey),
chain[0].getSigAlgName(), fullDisplayAlgName(privKey),
validity, newCert.getSigAlgName(),
x500Name}; signerAlias,
System.err.println(form.format(source)); validity,
x500Name};
System.err.println(form.format(source));
} else {
MessageFormat form = new MessageFormat(rb.getString
("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for"));
Object[] source = {
groupName == null ? keysize : KeyUtil.getKeySize(privKey),
fullDisplayAlgName(privKey),
newCert.getSigAlgName(),
validity,
x500Name};
System.err.println(form.format(source));
}
if (keyPass == null) { if (keyPass == null) {
keyPass = promptForKeyPass(alias, null, storePass); keyPass = promptForKeyPass(alias, null, storePass);
} }
checkWeak(rb.getString("the.generated.certificate"), chain[0]);
keyStore.setKeyEntry(alias, privKey, keyPass, chain); Certificate[] finalChain;
if (signerAlias != null) {
Certificate[] signerChain = keyStore.getCertificateChain(signerAlias);
finalChain = new X509Certificate[signerChain.length + 1];
finalChain[0] = newCert;
System.arraycopy(signerChain, 0, finalChain, 1, signerChain.length);
} else {
finalChain = new Certificate[] { newCert };
}
checkWeak(rb.getString("the.generated.certificate"), finalChain);
keyStore.setKeyEntry(alias, privKey, keyPass, finalChain);
} }
private String ecGroupNameForSize(int size) throws Exception { private String ecGroupNameForSize(int size) throws Exception {
@ -4243,7 +4310,6 @@ public final class Main {
* @param existingEx the original extensions, can be null, used for -selfcert * @param existingEx the original extensions, can be null, used for -selfcert
* @param extstrs -ext values, Read keytool doc * @param extstrs -ext values, Read keytool doc
* @param pkey the public key for the certificate * @param pkey the public key for the certificate
* @param akey the public key for the authority (issuer)
* @param aSubjectKeyId the subject key identifier for the authority (issuer) * @param aSubjectKeyId the subject key identifier for the authority (issuer)
* @return the created CertificateExtensions * @return the created CertificateExtensions
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -162,6 +162,10 @@ public class Resources extends java.util.ListResourceBundle {
"output in RFC style"}, //-rfc "output in RFC style"}, //-rfc
{"signature.algorithm.name", {"signature.algorithm.name",
"signature algorithm name"}, //-sigalg "signature algorithm name"}, //-sigalg
{"signer.alias",
"signer alias"}, //-signer
{"signer.key.password",
"signer key password"}, //-signerkeypass
{"source.alias", {"source.alias",
"source alias"}, //-srcalias "source alias"}, //-srcalias
{"source.key.password", {"source.key.password",
@ -302,6 +306,8 @@ public class Resources extends java.util.ListResourceBundle {
"Key pair not generated, alias <{0}> 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", {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for",
"Generating {0} bit {1} key pair and self-signed certificate ({2}) with a validity of {3} days\n\tfor: {4}"}, "Generating {0} bit {1} key pair and self-signed certificate ({2}) with a validity of {3} days\n\tfor: {4}"},
{"Generating.keysize.bit.keyAlgName.key.pair.and.a.certificate.sigAlgName.issued.by.signerAlias.with.a.validity.of.validality.days.for",
"Generating {0} bit {1} key pair and a certificate ({2}) issued by <{3}> with a validity of {4} days\n\tfor: {5}"},
{"Enter.key.password.for.alias.", "Enter key password for <{0}>"}, {"Enter.key.password.for.alias.", "Enter key password for <{0}>"},
{".RETURN.if.same.as.keystore.password.", {".RETURN.if.same.as.keystore.password.",
"\t(RETURN if same as keystore password): "}, "\t(RETURN if same as keystore password): "},
@ -481,7 +487,6 @@ public class Resources extends java.util.ListResourceBundle {
{"backup.keystore.warning", "The original keystore \"%1$s\" is backed up as \"%3$s\"..."}, {"backup.keystore.warning", "The original keystore \"%1$s\" is backed up as \"%3$s\"..."},
{"importing.keystore.status", "Importing keystore %1$s to %2$s..."}, {"importing.keystore.status", "Importing keystore %1$s to %2$s..."},
{"keyalg.option.missing.error", "The -keyalg option must be specified."}, {"keyalg.option.missing.error", "The -keyalg option must be specified."},
{"showinfo.no.option", "Missing option for -showinfo. Try \"keytool -showinfo -tls\"."}, {"showinfo.no.option", "Missing option for -showinfo. Try \"keytool -showinfo -tls\"."},
}; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -34,7 +34,9 @@ import java.security.interfaces.EdECPublicKey;
import java.security.interfaces.RSAKey; import java.security.interfaces.RSAKey;
import java.security.interfaces.DSAKey; import java.security.interfaces.DSAKey;
import java.security.interfaces.DSAParams; import java.security.interfaces.DSAParams;
import java.security.interfaces.XECKey;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec; import java.security.spec.KeySpec;
import java.security.spec.ECParameterSpec; import java.security.spec.ECParameterSpec;
import java.security.spec.InvalidParameterSpecException; import java.security.spec.InvalidParameterSpecException;
@ -97,6 +99,21 @@ public final class KeyUtil {
} else if (key instanceof DHKey) { } else if (key instanceof DHKey) {
DHKey pubk = (DHKey)key; DHKey pubk = (DHKey)key;
size = pubk.getParams().getP().bitLength(); size = pubk.getParams().getP().bitLength();
} else if (key instanceof XECKey) {
XECKey pubk = (XECKey)key;
AlgorithmParameterSpec params = pubk.getParams();
if (params instanceof NamedParameterSpec) {
String name = ((NamedParameterSpec) params).getName();
if (name.equalsIgnoreCase(NamedParameterSpec.X25519.getName())) {
size = 255;
} else if (name.equalsIgnoreCase(NamedParameterSpec.X448.getName())) {
size = 448;
} else {
size = -1;
}
} else {
size = -1;
}
} else if (key instanceof EdECKey) { } else if (key instanceof EdECKey) {
String nc = ((EdECKey) key).getParams().getName(); String nc = ((EdECKey) key).getParams().getName();
if (nc.equalsIgnoreCase(NamedParameterSpec.ED25519.getName())) { if (nc.equalsIgnoreCase(NamedParameterSpec.ED25519.getName())) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -111,6 +111,7 @@ public final class SecurityProviderConstants {
public static final int DEF_DH_KEY_SIZE; public static final int DEF_DH_KEY_SIZE;
public static final int DEF_EC_KEY_SIZE; public static final int DEF_EC_KEY_SIZE;
public static final int DEF_ED_KEY_SIZE; public static final int DEF_ED_KEY_SIZE;
public static final int DEF_XEC_KEY_SIZE;
private static final String KEY_LENGTH_PROP = private static final String KEY_LENGTH_PROP =
"jdk.security.defaultKeySize"; "jdk.security.defaultKeySize";
@ -124,6 +125,7 @@ public final class SecurityProviderConstants {
int dhKeySize = 2048; int dhKeySize = 2048;
int ecKeySize = 256; int ecKeySize = 256;
int edKeySize = 255; int edKeySize = 255;
int xecKeySize = 255;
if (keyLengthStr != null) { if (keyLengthStr != null) {
try { try {
@ -162,6 +164,8 @@ public final class SecurityProviderConstants {
ecKeySize = value; ecKeySize = value;
} else if (algoName.equalsIgnoreCase("EdDSA")) { } else if (algoName.equalsIgnoreCase("EdDSA")) {
edKeySize = value; edKeySize = value;
} else if (algoName.equals("XDH")) {
xecKeySize = value;
} else { } else {
if (debug != null) { if (debug != null) {
debug.println("Ignoring unsupported algo in " + debug.println("Ignoring unsupported algo in " +
@ -189,6 +193,7 @@ public final class SecurityProviderConstants {
DEF_DH_KEY_SIZE = dhKeySize; DEF_DH_KEY_SIZE = dhKeySize;
DEF_EC_KEY_SIZE = ecKeySize; DEF_EC_KEY_SIZE = ecKeySize;
DEF_ED_KEY_SIZE = edKeySize; DEF_ED_KEY_SIZE = edKeySize;
DEF_XEC_KEY_SIZE = xecKeySize;
// Set up aliases with default mappings // Set up aliases with default mappings
// This is needed when the mapping contains non-oid // This is needed when the mapping contains non-oid

View File

@ -0,0 +1,309 @@
/*
* Copyright (c) 2021, 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.
*/
/*
* @test
* @bug 8260693
* @summary Test for keytool -genkeypair with -signer and -signerkeypass options
* @library /test/lib
* @modules java.base/sun.security.util
*/
import jdk.test.lib.SecurityTools;
import jdk.test.lib.process.OutputAnalyzer;
import java.io.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.KeyStore;
import java.security.PublicKey;
import java.util.Arrays;
import sun.security.util.DerValue;
import sun.security.util.KeyUtil;
import sun.security.util.KnownOIDs;
import static sun.security.util.KnownOIDs.*;
public class GenKeyPairSigner {
static OutputAnalyzer kt(String cmd, String ks) throws Exception {
return SecurityTools.keytool("-storepass changeit " + cmd +
" -keystore " + ks);
}
static OutputAnalyzer ktjks(String cmd, String ks, String kpass) throws Exception {
return SecurityTools.keytool("-storepass changeit " + cmd +
" -keystore " + ks + " -storetype jks" + " -keypass " +
kpass);
}
public static void main(String[] args) throws Exception {
testSignerPKCS12();
testSignerJKS();
testSignerOpt();
}
static void testSignerPKCS12() throws Exception {
KeyStore kstore;
X509Certificate cert;
String sigName, pKeyAlg;
PublicKey pKey;
int keyLen;
/*
* The signer alias is stored in the PKCS12 keystore
*/
System.out.println("Testing the signer alias that is stored in the PKCS12 keystore");
System.out.println("Generating a root cert with SubjectKeyIdentifier extension");
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-genkeypair -keyalg EdDSA -alias ca -dname CN=CA -ext bc:c " +
"-ext 2.5.29.14=04:14:00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16:17:18:19")
.shouldContain("Generating 255 bit Ed25519 key pair and self-signed certificate (Ed25519) with a validity of 90 days")
.shouldContain("for: CN=CA")
.shouldHaveExitValue(0);
System.out.println("Generating an XDH cert with -signer option");
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-genkeypair -keyalg XDH -alias e1 -dname CN=E1 -signer ca")
.shouldContain("Generating 255 bit XDH key pair and a certificate (Ed25519) issued by <ca> with a validity of 90 days")
.shouldContain("for: CN=E1")
.shouldHaveExitValue(0);
// examine the resulting cert
kstore = KeyStore.getInstance(new File("ks"), "changeit".toCharArray());
cert = (X509Certificate)kstore.getCertificate("e1");
Certificate[] certChain = kstore.getCertificateChain("e1");
if (certChain.length != 2) {
throw new Exception("Generated cert chain is in error");
}
sigName = cert.getSigAlgName();
if (sigName != "Ed25519") {
throw new Exception("Signature algorithm name is in error");
}
pKey = cert.getPublicKey();
keyLen = KeyUtil.getKeySize(pKey);
if (keyLen != 255) {
throw new Exception("Key size is in error");
}
pKeyAlg = pKey.getAlgorithm();
if (pKeyAlg != "XDH") {
throw new Exception("Subject Public Key Algorithm is in error");
}
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-list -v")
.shouldContain("Alias name: e1")
.shouldContain("Certificate chain length: 2")
.shouldContain("Signature algorithm name: Ed25519")
.shouldContain("Subject Public Key Algorithm: 255-bit XDH key")
.shouldHaveExitValue(0);
// check to make sure that cert's AKID is created from the SKID of the signing cert
byte[] expectedId = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19};
byte[] authorityKeyIdExt = cert.getExtensionValue(
KnownOIDs.AuthorityKeyID.value());
byte[] authorityKeyId = null;
if (authorityKeyIdExt == null) {
throw new Exception("Failed to get AKID extension from the cert");
} else {
try {
authorityKeyId = new DerValue(authorityKeyIdExt).getOctetString();
} catch (IOException e) {
throw new Exception("Failed to get AKID encoded OctetString in the cert");
}
}
authorityKeyId = Arrays.copyOfRange(authorityKeyId, 4, authorityKeyId.length);
if (!Arrays.equals(authorityKeyId, expectedId)) {
throw new Exception("Failed due to AKID mismatch");
}
kt("-genkeypair -keyalg RSA -alias ca2 -dname CN=CA2 -ext bc:c ",
"ks");
System.out.println("Generating an X448 cert with -signer option");
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-genkeypair -keyalg X448 -alias e2 -dname CN=E2 -sigalg SHA384withRSA -signer ca2")
.shouldContain("Generating 448 bit XDH key pair and a certificate (SHA384withRSA) issued by <ca2> with a validity of 90 days")
.shouldContain("for: CN=E2")
.shouldHaveExitValue(0);
// examine the resulting cert
kstore = KeyStore.getInstance(new File("ks"), "changeit".toCharArray());
cert = (X509Certificate)kstore.getCertificate("e2");
sigName = cert.getSigAlgName();
if (sigName != "SHA384withRSA") {
throw new Exception("Signature algorithm name is in error");
}
pKey = cert.getPublicKey();
keyLen = KeyUtil.getKeySize(pKey);
if (keyLen != 448) {
throw new Exception("Key size is in error");
}
pKeyAlg = pKey.getAlgorithm();
if (pKeyAlg != "XDH") {
throw new Exception("Subject Public Key Algorithm is in error");
}
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-list -v")
.shouldContain("Alias name: e2")
.shouldContain("Signature algorithm name: SHA384withRSA")
.shouldContain("Subject Public Key Algorithm: 448-bit XDH key")
.shouldHaveExitValue(0);
kt("-genkeypair -keyalg DSA -alias ca3 -dname CN=CA3 -ext bc:c ",
"ks");
System.out.println("Generating a DH cert with -signer option");
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-genkeypair -keyalg DH -alias e3 -dname CN=E3 -signer ca3")
.shouldContain("Generating 2,048 bit DH key pair and a certificate (SHA256withDSA) issued by <ca3> with a validity of 90 days")
.shouldContain("for: CN=E3")
.shouldHaveExitValue(0);
// examine the resulting cert
kstore = KeyStore.getInstance(new File("ks"), "changeit".toCharArray());
cert = (X509Certificate)kstore.getCertificate("e3");
sigName = cert.getSigAlgName();
if (sigName != "SHA256withDSA") {
throw new Exception("Signature algorithm name is in error");
}
pKey = cert.getPublicKey();
keyLen = KeyUtil.getKeySize(pKey);
if (keyLen != 2048) {
throw new Exception("Key size is in error");
}
pKeyAlg = pKey.getAlgorithm();
if (pKeyAlg != "DH") {
throw new Exception("Subject Public Key Algorithm is in error");
}
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-list -v")
.shouldContain("Alias name: e3")
.shouldContain("Signature algorithm name: SHA256withRSA")
.shouldContain("Subject Public Key Algorithm: 2048-bit DH key")
.shouldHaveExitValue(0);
}
static void testSignerJKS() throws Exception {
KeyStore kstore;
X509Certificate cert;
String sigName, pKeyAlg;
PublicKey pKey;
int keyLen;
/*
* The signer alias is stored in the JKS keystore
* Using JKS keystore here is to test the scenario when the private key
* of the signer entry is protected by a password different from the
* store password, and -signerkeypass option needs to be specified
* along with -signer option.
*/
System.out.println("Testing the signer alias that is stored in the JKS keystore");
ktjks("-genkeypair -keyalg RSA -keysize 1024 -alias ca -dname CN=CA -ext bc:c",
"ksjks", "cakeypass");
System.out.println("Generating an DSA cert with -signer and -signerkeypass options");
SecurityTools.keytool("-keystore ksjks -storepass changeit -storetype jks " +
"-genkeypair -keyalg DSA -keysize 1024 -alias ca1 -dname CN=CA1 " +
"-keypass ca1keypass -signer ca -signerkeypass cakeypass")
.shouldContain("Generating 1,024 bit DSA key pair and a certificate (SHA256withRSA) issued by <ca> with a validity of 90 days")
.shouldContain("for: CN=CA1")
.shouldContain("The generated certificate #1 of 2 uses a 1024-bit DSA key which is considered a security risk")
.shouldContain("The generated certificate #2 of 2 uses a 1024-bit RSA key which is considered a security risk")
.shouldHaveExitValue(0);
System.out.println("Generating an XDH cert with -signer and -signerkeypass options");
SecurityTools.keytool("-keystore ksjks -storepass changeit -storetype jks " +
"-genkeypair -keyalg XDH -alias e1 -dname CN=E1 " +
"-keypass e1keypass -signer ca1 -signerkeypass ca1keypass")
.shouldContain("Generating 255 bit XDH key pair and a certificate (SHA256withDSA) issued by <ca1> with a validity of 90 days")
.shouldContain("for: CN=E1")
.shouldContain("The generated certificate #2 of 3 uses a 1024-bit DSA key which is considered a security risk")
.shouldContain("The generated certificate #3 of 3 uses a 1024-bit RSA key which is considered a security risk")
.shouldHaveExitValue(0);
// examine the resulting cert
kstore = KeyStore.getInstance(new File("ksjks"), "changeit".toCharArray());
cert = (X509Certificate)kstore.getCertificate("e1");
Certificate[] certChain = kstore.getCertificateChain("e1");
if (certChain.length != 3) {
throw new Exception("Generated cert chain is in error");
}
sigName = cert.getSigAlgName();
if (sigName != "SHA256withDSA") {
throw new Exception("Signature algorithm name is in error");
}
pKey = cert.getPublicKey();
keyLen = KeyUtil.getKeySize(pKey);
if (keyLen != 255) {
throw new Exception("Key size is in error");
}
pKeyAlg = pKey.getAlgorithm();
if (pKeyAlg != "XDH") {
throw new Exception("Subject Public Key Algorithm is in error");
}
SecurityTools.keytool("-keystore ksjks -storepass changeit " +
"-list -v")
.shouldContain("Alias name: e1")
.shouldContain("Certificate chain length: 3")
.shouldContain("Signature algorithm name: SHA256withDSA")
.shouldContain("Subject Public Key Algorithm: 255-bit XDH key")
.shouldHaveExitValue(0);
}
static void testSignerOpt() throws Exception {
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-genkeypair -keyalg X25519 -alias e4 -dname CN=E4")
.shouldContain("Cannot derive signature algorithm from XDH")
.shouldHaveExitValue(1);
SecurityTools.keytool("-keystore ks -storepass changeit " +
"-genkeypair -keyalg X448 -alias e4 -dname CN=E4 -signer noca")
.shouldContain("Alias <noca> does not exist")
.shouldHaveExitValue(1);
SecurityTools.keytool("-genkeypair --help")
.shouldContain("-signer <alias> signer alias")
.shouldContain("-signerkeypass <arg> signer key password")
.shouldHaveExitValue(0);
}
}