8171319: keytool should print out warnings when reading or generating cert/cert req using weak algorithms

Reviewed-by: coffeys
This commit is contained in:
Weijun Wang 2017-03-07 22:42:11 +08:00
parent 080a88360a
commit 6b3143e831
7 changed files with 891 additions and 81 deletions

View File

@ -167,7 +167,8 @@ public class PKCS10 {
// key and signature algorithm we found.
//
try {
sig = Signature.getInstance(id.getName());
sigAlg = id.getName();
sig = Signature.getInstance(sigAlg);
sig.initVerify(subjectPublicKeyInfo);
sig.update(data);
if (!sig.verify(sigData))
@ -218,6 +219,7 @@ public class PKCS10 {
signature.update(certificateRequestInfo, 0,
certificateRequestInfo.length);
sig = signature.sign();
sigAlg = signature.getAlgorithm();
/*
* Build guts of SIGNED macro
@ -250,6 +252,11 @@ public class PKCS10 {
public PublicKey getSubjectPublicKeyInfo()
{ return subjectPublicKeyInfo; }
/**
* Returns the signature algorithm.
*/
public String getSigAlg() { return sigAlg; }
/**
* Returns the additional attributes requested.
*/
@ -348,6 +355,7 @@ public class PKCS10 {
private X500Name subject;
private PublicKey subjectPublicKeyInfo;
private String sigAlg;
private PKCS10Attributes attributeSet;
private byte[] encoded; // signed
}

View File

@ -51,7 +51,7 @@ import sun.security.util.Debug;
/**
* BasicChecker is a PKIXCertPathChecker that checks the basic information
* on a PKIX certificate, namely the signature, timestamp, and subject/issuer
* on a PKIX certificate, namely the signature, validity, and subject/issuer
* name chaining.
*
* @since 1.4
@ -125,7 +125,7 @@ class BasicChecker extends PKIXCertPathChecker {
}
/**
* Performs the signature, timestamp, and subject/issuer name chaining
* Performs the signature, validity, and subject/issuer name chaining
* checks on the certificate using its internal state. This method does
* not remove any critical extensions from the Collection.
*
@ -141,7 +141,7 @@ class BasicChecker extends PKIXCertPathChecker {
X509Certificate currCert = (X509Certificate)cert;
if (!sigOnly) {
verifyTimestamp(currCert);
verifyValidity(currCert);
verifyNameChaining(currCert);
}
verifySignature(currCert);
@ -177,12 +177,12 @@ class BasicChecker extends PKIXCertPathChecker {
}
/**
* Internal method to verify the timestamp on a certificate
* Internal method to verify the validity on a certificate
*/
private void verifyTimestamp(X509Certificate cert)
private void verifyValidity(X509Certificate cert)
throws CertPathValidatorException
{
String msg = "timestamp";
String msg = "validity";
if (debug != null)
debug.println("---checking " + msg + ":" + date.toString() + "...");

View File

@ -27,6 +27,7 @@ package sun.security.tools.keytool;
import java.io.*;
import java.security.CodeSigner;
import java.security.CryptoPrimitive;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
@ -156,6 +157,7 @@ public final class Main {
private boolean protectedPath = false;
private boolean srcprotectedPath = false;
private boolean cacerts = false;
private boolean nowarn = false;
private CertificateFactory cf = null;
private KeyStore caks = null; // "cacerts" keystore
private char[] srcstorePass = null;
@ -166,6 +168,16 @@ public final class Main {
private List<String> ids = new ArrayList<>(); // used in GENCRL
private List<String> v3ext = new ArrayList<>();
// Warnings on weak algorithms
private List<String> weakWarnings = new ArrayList<>();
private static final DisabledAlgorithmConstraints DISABLED_CHECK =
new DisabledAlgorithmConstraints(
DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
enum Command {
CERTREQ("Generates.a.certificate.request",
ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME,
@ -351,7 +363,7 @@ public final class Main {
private static final String NONE = "NONE";
private static final String P11KEYSTORE = "PKCS11";
private static final String P12KEYSTORE = "PKCS12";
private final String keyAlias = "mykey";
private static final String keyAlias = "mykey";
// for i18n
private static final java.util.ResourceBundle rb =
@ -387,6 +399,7 @@ public final class Main {
throw e;
}
} finally {
printWeakWarnings(false);
for (char[] pass : passwords) {
if (pass != null) {
Arrays.fill(pass, ' ');
@ -476,6 +489,8 @@ public final class Main {
help = true;
} else if (collator.compare(flags, "-conf") == 0) {
i++;
} else if (collator.compare(flags, "-nowarn") == 0) {
nowarn = true;
} else if (collator.compare(flags, "-keystore") == 0) {
ksfname = args[++i];
if (new File(ksfname).getCanonicalPath().equals(
@ -1152,11 +1167,11 @@ public final class Main {
} else if (command == LIST) {
if (storePass == null
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
printWarning();
printNoIntegrityWarning();
}
if (alias != null) {
doPrintEntry(alias, out);
doPrintEntry(rb.getString("the.certificate"), alias, out);
} else {
doPrintEntries(out);
}
@ -1253,6 +1268,12 @@ public final class Main {
throws Exception {
if (keyStore.containsAlias(alias) == false) {
MessageFormat form = new MessageFormat
(rb.getString("Alias.alias.does.not.exist"));
Object[] source = {alias};
throw new Exception(form.format(source));
}
Certificate signerCert = keyStore.getCertificate(alias);
byte[] encoded = signerCert.getEncoded();
X509CertImpl signerCertImpl = new X509CertImpl(encoded);
@ -1306,6 +1327,8 @@ public final class Main {
byte[] rawReq = Pem.decode(new String(sb));
PKCS10 req = new PKCS10(rawReq);
checkWeak(rb.getString("the.certificate.request"), req);
info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
info.set(X509CertInfo.SUBJECT,
dname==null?req.getSubjectName():new X500Name(dname));
@ -1335,6 +1358,9 @@ public final class Main {
}
}
}
checkWeak(rb.getString("the.issuer"), keyStore.getCertificateChain(alias));
checkWeak(rb.getString("the.generated.certificate"), cert);
}
private void doGenCRL(PrintStream out)
@ -1385,6 +1411,7 @@ public final class Main {
} else {
out.write(crl.getEncodedInternal());
}
checkWeak(rb.getString("the.generated.crl"), crl, privateKey);
}
/**
@ -1431,6 +1458,8 @@ public final class Main {
// Sign the request and base-64 encode it
request.encodeAndSign(subject, signature);
request.print(out);
checkWeak(rb.getString("the.generated.certificate.request"), request);
}
/**
@ -1454,7 +1483,7 @@ public final class Main {
{
if (storePass == null
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
printWarning();
printNoIntegrityWarning();
}
if (alias == null) {
alias = keyAlias;
@ -1474,6 +1503,7 @@ public final class Main {
throw new Exception(form.format(source));
}
dumpCert(cert, out);
checkWeak(rb.getString("the.certificate"), cert);
}
/**
@ -1729,6 +1759,8 @@ public final class Main {
keyPass = promptForKeyPass(alias, null, storePass);
}
keyStore.setKeyEntry(alias, privKey, keyPass, chain);
checkWeak(rb.getString("the.generated.certificate"), chain[0]);
}
/**
@ -1810,7 +1842,7 @@ public final class Main {
/**
* Prints a single keystore entry.
*/
private void doPrintEntry(String alias, PrintStream out)
private void doPrintEntry(String label, String alias, PrintStream out)
throws Exception
{
if (keyStore.containsAlias(alias) == false) {
@ -1881,12 +1913,14 @@ public final class Main {
} else {
dumpCert(chain[i], out);
}
checkWeak(label, chain[i]);
}
} else {
// Print the digest of the user cert only
out.println
(rb.getString("Certificate.fingerprint.SHA.256.") +
getCertFingerPrint("SHA-256", chain[0]));
checkWeak(label, chain);
}
}
} else if (keyStore.entryInstanceOf(alias,
@ -1909,6 +1943,7 @@ public final class Main {
out.println(rb.getString("Certificate.fingerprint.SHA.256.")
+ getCertFingerPrint("SHA-256", cert));
}
checkWeak(label, cert);
} else {
out.println(rb.getString("Unknown.Entry.Type"));
}
@ -1992,7 +2027,7 @@ public final class Main {
if (srcstorePass == null
&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
// anti refactoring, copied from printWarning(),
// anti refactoring, copied from printNoIntegrityWarning(),
// but change 2 lines
System.err.println();
System.err.println(rb.getString
@ -2092,6 +2127,10 @@ public final class Main {
"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified."));
}
}
Certificate c = srckeystore.getCertificate(alias);
if (c != null) {
checkWeak("<" + newAlias + ">", c);
}
return 1;
} catch (KeyStoreException kse) {
Object[] source2 = {alias, kse.toString()};
@ -2154,7 +2193,7 @@ public final class Main {
for (Enumeration<String> e = keyStore.aliases();
e.hasMoreElements(); ) {
String alias = e.nextElement();
doPrintEntry(alias, out);
doPrintEntry("<" + alias + ">", alias, out);
if (verbose || rfc) {
out.println(rb.getString("NEWLINE"));
out.println(rb.getString
@ -2300,19 +2339,28 @@ public final class Main {
for (CRL crl: loadCRLs(src)) {
printCRL(crl, out);
String issuer = null;
Certificate signer = null;
if (caks != null) {
issuer = verifyCRL(caks, crl);
if (issuer != null) {
signer = caks.getCertificate(issuer);
out.printf(rb.getString(
"verified.by.s.in.s"), issuer, "cacerts");
"verified.by.s.in.s.weak"),
issuer,
"cacerts",
withWeak(signer.getPublicKey()));
out.println();
}
}
if (issuer == null && keyStore != null) {
issuer = verifyCRL(keyStore, crl);
if (issuer != null) {
signer = keyStore.getCertificate(issuer);
out.printf(rb.getString(
"verified.by.s.in.s"), issuer, "keystore");
"verified.by.s.in.s.weak"),
issuer,
"keystore",
withWeak(signer.getPublicKey()));
out.println();
}
}
@ -2324,18 +2372,26 @@ public final class Main {
out.println(rb.getString
("STARNN"));
}
checkWeak(rb.getString("the.crl"), crl, signer == null ? null : signer.getPublicKey());
}
}
private void printCRL(CRL crl, PrintStream out)
throws Exception {
X509CRL xcrl = (X509CRL)crl;
if (rfc) {
X509CRL xcrl = (X509CRL)crl;
out.println("-----BEGIN X509 CRL-----");
out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));
out.println("-----END X509 CRL-----");
} else {
out.println(crl.toString());
String s;
if (crl instanceof X509CRLImpl) {
X509CRLImpl x509crl = (X509CRLImpl) crl;
s = x509crl.toStringWithAlgName(withWeak("" + x509crl.getSigAlgId()));
} else {
s = crl.toString();
}
out.println(s);
}
}
@ -2362,8 +2418,11 @@ public final class Main {
PKCS10 req = new PKCS10(Pem.decode(new String(sb)));
PublicKey pkey = req.getSubjectPublicKeyInfo();
out.printf(rb.getString("PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key."),
req.getSubjectName(), pkey.getFormat(), pkey.getAlgorithm());
out.printf(rb.getString("PKCS.10.with.weak"),
req.getSubjectName(),
pkey.getFormat(),
withWeak(pkey),
withWeak(req.getSigAlg()));
for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {
ObjectIdentifier oid = attr.getAttributeId();
if (oid.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {
@ -2386,6 +2445,7 @@ public final class Main {
if (debug) {
out.println(req); // Just to see more, say, public key length...
}
checkWeak(rb.getString("the.certificate.request"), req);
}
/**
@ -2425,6 +2485,15 @@ public final class Main {
if (i < (certs.length-1)) {
out.println();
}
checkWeak(oneInMany(rb.getString("the.certificate"), i, certs.length), x509Cert);
}
}
private static String oneInMany(String label, int i, int num) {
if (num == 1) {
return label;
} else {
return String.format(rb.getString("one.in.many"), label, i+1, num);
}
}
@ -2458,7 +2527,11 @@ public final class Main {
out.println();
out.println(rb.getString("Signature."));
out.println();
for (Certificate cert: signer.getSignerCertPath().getCertificates()) {
List<? extends Certificate> certs
= signer.getSignerCertPath().getCertificates();
int cc = 0;
for (Certificate cert: certs) {
X509Certificate x = (X509Certificate)cert;
if (rfc) {
out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
@ -2467,12 +2540,15 @@ public final class Main {
printX509Cert(x, out);
}
out.println();
checkWeak(oneInMany(rb.getString("the.certificate"), cc++, certs.size()), x);
}
Timestamp ts = signer.getTimestamp();
if (ts != null) {
out.println(rb.getString("Timestamp."));
out.println();
for (Certificate cert: ts.getSignerCertPath().getCertificates()) {
certs = ts.getSignerCertPath().getCertificates();
cc = 0;
for (Certificate cert: certs) {
X509Certificate x = (X509Certificate)cert;
if (rfc) {
out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
@ -2481,6 +2557,7 @@ public final class Main {
printX509Cert(x, out);
}
out.println();
checkWeak(oneInMany(rb.getString("the.tsa.certificate"), cc++, certs.size()), x);
}
}
}
@ -2523,6 +2600,7 @@ public final class Main {
printX509Cert((X509Certificate)cert, out);
out.println();
}
checkWeak(oneInMany(rb.getString("the.certificate"), i, chain.size()), cert);
} catch (Exception e) {
if (debug) {
e.printStackTrace();
@ -2698,7 +2776,7 @@ public final class Main {
}
// Now store the newly established chain in the keystore. The new
// chain replaces the old one.
// chain replaces the old one. The chain can be null if user chooses no.
if (newChain != null) {
keyStore.setKeyEntry(alias, privKey,
(keyPass != null) ? keyPass : storePass,
@ -2735,6 +2813,12 @@ public final class Main {
throw new Exception(rb.getString("Input.not.an.X.509.certificate"));
}
if (noprompt) {
keyStore.setCertificateEntry(alias, cert);
checkWeak(rb.getString("the.input"), cert);
return true;
}
// if certificate is self-signed, make sure it verifies
boolean selfSigned = false;
if (KeyStoreUtil.isSelfSigned(cert)) {
@ -2742,11 +2826,6 @@ public final class Main {
selfSigned = true;
}
if (noprompt) {
keyStore.setCertificateEntry(alias, cert);
return true;
}
// check if cert already exists in keystore
String reply = null;
String trustalias = keyStore.getCertificateAlias(cert);
@ -2755,6 +2834,8 @@ public final class Main {
("Certificate.already.exists.in.keystore.under.alias.trustalias."));
Object[] source = {trustalias};
System.err.println(form.format(source));
checkWeak(rb.getString("the.input"), cert);
printWeakWarnings(true);
reply = getYesNoReply
(rb.getString("Do.you.still.want.to.add.it.no."));
} else if (selfSigned) {
@ -2764,6 +2845,8 @@ public final class Main {
("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));
Object[] source = {trustalias};
System.err.println(form.format(source));
checkWeak(rb.getString("the.input"), cert);
printWeakWarnings(true);
reply = getYesNoReply
(rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));
}
@ -2771,6 +2854,8 @@ public final class Main {
// Print the cert and ask user if they really want to add
// it to their keystore
printX509Cert(cert, System.out);
checkWeak(rb.getString("the.input"), cert);
printWeakWarnings(true);
reply = getYesNoReply
(rb.getString("Trust.this.certificate.no."));
}
@ -2784,6 +2869,7 @@ public final class Main {
}
}
// Not found in this keystore and not self-signed
// Try to establish trust chain
try {
Certificate[] chain = establishCertChain(null, cert);
@ -2795,6 +2881,8 @@ public final class Main {
// Print the cert and ask user if they really want to add it to
// their keystore
printX509Cert(cert, System.out);
checkWeak(rb.getString("the.input"), cert);
printWeakWarnings(true);
reply = getYesNoReply
(rb.getString("Trust.this.certificate.no."));
if ("YES".equals(reply)) {
@ -2933,6 +3021,24 @@ public final class Main {
return keyPass;
}
private String withWeak(String alg) {
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, alg, null)) {
return alg;
} else {
return String.format(rb.getString("with.weak"), alg);
}
}
private String withWeak(PublicKey key) {
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
return String.format(rb.getString("key.bit"),
KeyUtil.getKeySize(key), key.getAlgorithm());
} else {
return String.format(rb.getString("key.bit.weak"),
KeyUtil.getKeySize(key), key.getAlgorithm());
}
}
/**
* Prints a certificate in a human readable format.
*/
@ -2941,7 +3047,7 @@ public final class Main {
{
MessageFormat form = new MessageFormat
(rb.getString(".PATTERN.printX509Cert"));
(rb.getString(".PATTERN.printX509Cert.with.weak"));
PublicKey pkey = cert.getPublicKey();
Object[] source = {cert.getSubjectDN().toString(),
cert.getIssuerDN().toString(),
@ -2950,10 +3056,9 @@ public final class Main {
cert.getNotAfter().toString(),
getCertFingerPrint("SHA-1", cert),
getCertFingerPrint("SHA-256", cert),
cert.getSigAlgName(),
pkey.getAlgorithm(),
KeyUtil.getKeySize(pkey),
cert.getVersion(),
withWeak(cert.getSigAlgName()),
withWeak(pkey),
cert.getVersion()
};
out.println(form.format(source));
@ -3003,12 +3108,12 @@ public final class Main {
* @param ks the keystore to search with, not null
* @return <code>cert</code> itself if it's already inside <code>ks</code>,
* or a certificate inside <code>ks</code> who signs <code>cert</code>,
* or null otherwise.
* or null otherwise. A label is added.
*/
private static Certificate getTrustedSigner(Certificate cert, KeyStore ks)
throws Exception {
private static Pair<String,Certificate>
getTrustedSigner(Certificate cert, KeyStore ks) throws Exception {
if (ks.getCertificateAlias(cert) != null) {
return cert;
return new Pair<>("", cert);
}
for (Enumeration<String> aliases = ks.aliases();
aliases.hasMoreElements(); ) {
@ -3017,7 +3122,7 @@ public final class Main {
if (trustedCert != null) {
try {
cert.verify(trustedCert.getPublicKey());
return trustedCert;
return new Pair<>(name, trustedCert);
} catch (Exception e) {
// Not verified, skip to the next one
}
@ -3281,7 +3386,7 @@ public final class Main {
/**
* Prints warning about missing integrity check.
*/
private void printWarning() {
private void printNoIntegrityWarning() {
System.err.println();
System.err.println(rb.getString
(".WARNING.WARNING.WARNING."));
@ -3306,6 +3411,9 @@ public final class Main {
Certificate[] replyCerts)
throws Exception
{
checkWeak(rb.getString("reply"), replyCerts);
// order the certs in the reply (bottom-up).
// we know that all certs in the reply are of type X.509, because
// we parsed them using an X.509 certificate factory
@ -3358,9 +3466,11 @@ public final class Main {
// do we trust the cert at the top?
Certificate topCert = replyCerts[replyCerts.length-1];
Certificate root = getTrustedSigner(topCert, keyStore);
boolean fromKeyStore = true;
Pair<String,Certificate> root = getTrustedSigner(topCert, keyStore);
if (root == null && trustcacerts && caks != null) {
root = getTrustedSigner(topCert, caks);
fromKeyStore = false;
}
if (root == null) {
System.err.println();
@ -3369,33 +3479,42 @@ public final class Main {
printX509Cert((X509Certificate)topCert, System.out);
System.err.println();
System.err.print(rb.getString(".is.not.trusted."));
printWeakWarnings(true);
String reply = getYesNoReply
(rb.getString("Install.reply.anyway.no."));
if ("NO".equals(reply)) {
return null;
}
} else {
if (root != topCert) {
if (root.snd != topCert) {
// append the root CA cert to the chain
Certificate[] tmpCerts =
new Certificate[replyCerts.length+1];
System.arraycopy(replyCerts, 0, tmpCerts, 0,
replyCerts.length);
tmpCerts[tmpCerts.length-1] = root;
tmpCerts[tmpCerts.length-1] = root.snd;
replyCerts = tmpCerts;
checkWeak(String.format(rb.getString(fromKeyStore ?
"alias.in.keystore" :
"alias.in.cacerts"),
root.fst),
root.snd);
}
}
return replyCerts;
}
/**
* Establishes a certificate chain (using trusted certificates in the
* keystore), starting with the user certificate
* keystore and cacerts), starting with the reply (certToVerify)
* and ending at a self-signed certificate found in the keystore.
*
* @param userCert the user certificate of the alias
* @param certToVerify the single certificate provided in the reply
* @param userCert optional existing certificate, mostly likely be the
* original self-signed cert created by -genkeypair.
* It must have the same public key as certToVerify
* but cannot be the same cert.
* @param certToVerify the starting certificate to build the chain
* @returns the established chain, might be null if user decides not
*/
private Certificate[] establishCertChain(Certificate userCert,
Certificate certToVerify)
@ -3423,30 +3542,37 @@ public final class Main {
// Use the subject distinguished name as the key into the hash table.
// All certificates associated with the same subject distinguished
// name are stored in the same hash table entry as a vector.
Hashtable<Principal, Vector<Certificate>> certs = null;
Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs = null;
if (keyStore.size() > 0) {
certs = new Hashtable<Principal, Vector<Certificate>>(11);
certs = new Hashtable<>(11);
keystorecerts2Hashtable(keyStore, certs);
}
if (trustcacerts) {
if (caks!=null && caks.size()>0) {
if (certs == null) {
certs = new Hashtable<Principal, Vector<Certificate>>(11);
certs = new Hashtable<>(11);
}
keystorecerts2Hashtable(caks, certs);
}
}
// start building chain
Vector<Certificate> chain = new Vector<>(2);
if (buildChain((X509Certificate)certToVerify, chain, certs)) {
Certificate[] newChain = new Certificate[chain.size()];
Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);
if (buildChain(
new Pair<>(rb.getString("the.input"),
(X509Certificate) certToVerify),
chain, certs)) {
for (Pair<String,X509Certificate> p : chain) {
checkWeak(p.fst, p.snd);
}
Certificate[] newChain =
new Certificate[chain.size()];
// buildChain() returns chain with self-signed root-cert first and
// user-cert last, so we need to invert the chain before we store
// it
int j=0;
for (int i=chain.size()-1; i>=0; i--) {
newChain[j] = chain.elementAt(i);
newChain[j] = chain.elementAt(i).snd;
j++;
}
return newChain;
@ -3457,7 +3583,17 @@ public final class Main {
}
/**
* Recursively tries to establish chain from pool of trusted certs.
* Recursively tries to establish chain from pool of certs starting from
* certToVerify until a self-signed cert is found, and fill the certs found
* into chain. Each cert in the chain signs the next one.
*
* This method is able to recover from an error, say, if certToVerify
* is signed by certA but certA has no issuer in certs and itself is not
* self-signed, the method can try another certB that also signs
* certToVerify and look for signer of certB, etc, etc.
*
* Each cert in chain comes with a label showing its origin. The label is
* used in the warning message when the cert is considered a risk.
*
* @param certToVerify the cert that needs to be verified.
* @param chain the chain that's being built.
@ -3465,19 +3601,20 @@ public final class Main {
*
* @return true if successful, false otherwise.
*/
private boolean buildChain(X509Certificate certToVerify,
Vector<Certificate> chain,
Hashtable<Principal, Vector<Certificate>> certs) {
Principal issuer = certToVerify.getIssuerDN();
if (KeyStoreUtil.isSelfSigned(certToVerify)) {
private boolean buildChain(Pair<String,X509Certificate> certToVerify,
Vector<Pair<String,X509Certificate>> chain,
Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {
if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {
// reached self-signed root cert;
// no verification needed because it's trusted.
chain.addElement(certToVerify);
return true;
}
Principal issuer = certToVerify.snd.getIssuerDN();
// Get the issuer's certificate(s)
Vector<Certificate> vec = certs.get(issuer);
Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);
if (vec == null) {
return false;
}
@ -3485,13 +3622,12 @@ public final class Main {
// Try out each certificate in the vector, until we find one
// whose public key verifies the signature of the certificate
// in question.
for (Enumeration<Certificate> issuerCerts = vec.elements();
issuerCerts.hasMoreElements(); ) {
X509Certificate issuerCert
= (X509Certificate)issuerCerts.nextElement();
PublicKey issuerPubKey = issuerCert.getPublicKey();
for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();
issuerCerts.hasMoreElements(); ) {
Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();
PublicKey issuerPubKey = issuerCert.snd.getPublicKey();
try {
certToVerify.verify(issuerPubKey);
certToVerify.snd.verify(issuerPubKey);
} catch (Exception e) {
continue;
}
@ -3541,10 +3677,11 @@ public final class Main {
/**
* Stores the (leaf) certificates of a keystore in a hashtable.
* All certs belonging to the same CA are stored in a vector that
* in turn is stored in the hashtable, keyed by the CA's subject DN
* in turn is stored in the hashtable, keyed by the CA's subject DN.
* Each cert comes with a string label that shows its origin and alias.
*/
private void keystorecerts2Hashtable(KeyStore ks,
Hashtable<Principal, Vector<Certificate>> hash)
Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)
throws Exception {
for (Enumeration<String> aliases = ks.aliases();
@ -3553,13 +3690,20 @@ public final class Main {
Certificate cert = ks.getCertificate(alias);
if (cert != null) {
Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
Vector<Certificate> vec = hash.get(subjectDN);
Pair<String,X509Certificate> pair = new Pair<>(
String.format(
rb.getString(ks == caks ?
"alias.in.cacerts" :
"alias.in.keystore"),
alias),
(X509Certificate)cert);
Vector<Pair<String,X509Certificate>> vec = hash.get(subjectDN);
if (vec == null) {
vec = new Vector<Certificate>();
vec.addElement(cert);
vec = new Vector<>();
vec.addElement(pair);
} else {
if (!vec.contains(cert)) {
vec.addElement(cert);
if (!vec.contains(pair)) {
vec.addElement(pair);
}
}
hash.put(subjectDN, vec);
@ -4157,6 +4301,67 @@ public final class Main {
return result;
}
private void checkWeak(String label, String sigAlg, Key key) {
if (!DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, sigAlg, null)) {
weakWarnings.add(String.format(
rb.getString("whose.sigalg.risk"), label, sigAlg));
}
if (key != null && !DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
weakWarnings.add(String.format(
rb.getString("whose.key.risk"),
label,
String.format(rb.getString("key.bit"),
KeyUtil.getKeySize(key), key.getAlgorithm())));
}
}
private void checkWeak(String label, Certificate[] certs) {
for (int i = 0; i < certs.length; i++) {
Certificate cert = certs[i];
if (cert instanceof X509Certificate) {
X509Certificate xc = (X509Certificate)cert;
String fullLabel = label;
if (certs.length > 1) {
fullLabel = oneInMany(label, i, certs.length);
}
checkWeak(fullLabel, xc.getSigAlgName(), xc.getPublicKey());
}
}
}
private void checkWeak(String label, Certificate cert) {
if (cert instanceof X509Certificate) {
X509Certificate xc = (X509Certificate)cert;
checkWeak(label, xc.getSigAlgName(), xc.getPublicKey());
}
}
private void checkWeak(String label, PKCS10 p10) {
checkWeak(label, p10.getSigAlg(), p10.getSubjectPublicKeyInfo());
}
private void checkWeak(String label, CRL crl, Key key) {
if (crl instanceof X509CRLImpl) {
X509CRLImpl impl = (X509CRLImpl)crl;
checkWeak(label, impl.getSigAlgName(), key);
}
}
private void printWeakWarnings(boolean newLine) {
if (!weakWarnings.isEmpty() && !nowarn) {
System.err.println("\nWarning:");
for (String warning : weakWarnings) {
System.err.println(warning);
}
if (newLine) {
// When calling before a yes/no prompt, add a new line
System.err.println();
}
}
weakWarnings.clear();
}
/**
* Prints the usage of this tool.
*/

View File

@ -360,8 +360,6 @@ public class Resources extends java.util.ListResourceBundle {
{"Enter.alias.name.", "Enter alias name: "},
{".RETURN.if.same.as.for.otherAlias.",
"\t(RETURN if same as for <{0}>)"},
{".PATTERN.printX509Cert",
"Owner: {0}\nIssuer: {1}\nSerial number: {2}\nValid from: {3} until: {4}\nCertificate fingerprints:\n\t SHA1: {5}\n\t SHA256: {6}\nSignature algorithm name: {7}\nSubject Public Key Algorithm: {8} ({9,number,#})\nVersion: {10}"},
{"What.is.your.first.and.last.name.",
"What is your first and last name?"},
{"What.is.the.name.of.your.organizational.unit.",
@ -428,16 +426,12 @@ public class Resources extends java.util.ListResourceBundle {
{"Please.provide.keysize.for.secret.key.generation",
"Please provide -keysize for secret key generation"},
{"verified.by.s.in.s", "Verified by %s in %s"},
{"warning.not.verified.make.sure.keystore.is.correct",
"WARNING: not verified. Make sure -keystore is correct."},
{"Extensions.", "Extensions: "},
{".Empty.value.", "(Empty value)"},
{"Extension.Request.", "Extension Request:"},
{"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.",
"PKCS #10 Certificate Request (Version 1.0)\n" +
"Subject: %s\nPublic Key: %s format %s key\n"},
{"Unknown.keyUsage.type.", "Unknown keyUsage type: "},
{"Unknown.extendedkeyUsage.type.", "Unknown extendedkeyUsage type: "},
{"Unknown.AccessDescription.type.", "Unknown AccessDescription type: "},
@ -446,7 +440,34 @@ public class Resources extends java.util.ListResourceBundle {
"This extension cannot be marked as critical. "},
{"Odd.number.of.hex.digits.found.", "Odd number of hex digits found: "},
{"Unknown.extension.type.", "Unknown extension type: "},
{"command.{0}.is.ambiguous.", "command {0} is ambiguous:"}
{"command.{0}.is.ambiguous.", "command {0} is ambiguous:"},
// 8171319: keytool should print out warnings when reading or
// generating cert/cert req using weak algorithms
{"the.certificate.request", "The certificate request"},
{"the.issuer", "The issuer"},
{"the.generated.certificate", "The generated certificate"},
{"the.generated.crl", "The generated CRL"},
{"the.generated.certificate.request", "The generated certificate request"},
{"the.certificate", "The certificate"},
{"the.crl", "The CRL"},
{"the.tsa.certificate", "The TSA certificate"},
{"the.input", "The input"},
{"reply", "Reply"},
{"one.in.many", "%s #%d of %d"},
{"alias.in.cacerts", "Issuer <%s> in cacerts"},
{"alias.in.keystore", "Issuer <%s>"},
{"with.weak", "%s (weak)"},
{"key.bit", "%d-bit %s key"},
{"key.bit.weak", "%d-bit %s key (weak)"},
{".PATTERN.printX509Cert.with.weak",
"Owner: {0}\nIssuer: {1}\nSerial number: {2}\nValid from: {3} until: {4}\nCertificate fingerprints:\n\t SHA1: {5}\n\t SHA256: {6}\nSignature algorithm name: {7}\nSubject Public Key Algorithm: {8}\nVersion: {9}"},
{"PKCS.10.with.weak",
"PKCS #10 Certificate Request (Version 1.0)\n" +
"Subject: %s\nFormat: %s\nPublic Key: %s\nSignature algorithm: %s\n"},
{"verified.by.s.in.s.weak", "Verified by %s in %s with a %s"},
{"whose.sigalg.risk", "%s uses the %s signature algorithm which is considered a security risk."},
{"whose.key.risk", "%s uses a %s which is considered a security risk."},
};

View File

@ -536,13 +536,18 @@ public class X509CRLImpl extends X509CRL implements DerEncoder {
* @return value of this CRL in a printable form.
*/
public String toString() {
return toStringWithAlgName("" + sigAlgId);
}
// Specifically created for keytool to append a (weak) label to sigAlg
public String toStringWithAlgName(String name) {
StringBuilder sb = new StringBuilder();
sb.append("X.509 CRL v")
.append(version+1)
.append('\n');
if (sigAlgId != null)
sb.append("Signature Algorithm: ")
.append(sigAlgId)
.append(name)
.append(", OID=")
.append(sigAlgId.getOID())
.append('\n');

View File

@ -43,6 +43,7 @@ import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import jdk.test.lib.SecurityTools;
import jdk.testlibrary.*;
import jdk.testlibrary.JarUtils;
import sun.security.pkcs.ContentInfo;
@ -66,6 +67,7 @@ import sun.security.x509.X500Name;
* java.base/sun.security.util
* java.base/sun.security.tools.keytool
* @library /lib/testlibrary
* @library /test/lib
* @run main/timeout=600 TimestampCheck
*/
public class TimestampCheck {
@ -457,6 +459,18 @@ public class TimestampCheck {
verify(file, "-J-Djava.security.debug=jar")
.shouldHaveExitValue(0)
.shouldMatch("SignatureException:.*disabled");
// For 8171319: keytool should print out warnings when reading or
// generating cert/cert req using weak algorithms.
// Must call keytool the command, otherwise doPrintCert() might not
// be able to reset "jdk.certpath.disabledAlgorithms".
String sout = SecurityTools.keytool("-printcert -jarfile weak.jar")
.stderrShouldContain("The TSA certificate uses a 512-bit RSA key" +
" which is considered a security risk.")
.getStdout();
if (sout.indexOf("weak", sout.indexOf("Timestamp:")) < 0) {
throw new RuntimeException("timestamp not weak: " + sout);
}
}
static void checkHalfWeak(String file) throws Throwable {

View File

@ -0,0 +1,557 @@
/*
* Copyright (c) 2017, 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 8171319
* @summary keytool should print out warnings when reading or generating
* cert/cert req using weak algorithms
* @library /test/lib
* @modules java.base/sun.security.tools.keytool
* java.base/sun.security.tools
* java.base/sun.security.util
* @run main/othervm/timeout=600 -Duser.language=en -Duser.country=US WeakAlg
*/
import jdk.test.lib.SecurityTools;
import jdk.test.lib.process.OutputAnalyzer;
import sun.security.tools.KeyStoreUtil;
import sun.security.util.DisabledAlgorithmConstraints;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.CryptoPrimitive;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WeakAlg {
public static void main(String[] args) throws Throwable {
rm("ks");
// -genkeypair, and -printcert, -list -alias, -exportcert
// (w/ different formats)
checkGenKeyPair("a", "-keyalg RSA -sigalg MD5withRSA", "MD5withRSA");
checkGenKeyPair("b", "-keyalg RSA -keysize 512", "512-bit RSA key");
checkGenKeyPair("c", "-keyalg RSA", null);
kt("-list")
.shouldContain("Warning:")
.shouldMatch("<a>.*MD5withRSA.*risk")
.shouldMatch("<b>.*512-bit RSA key.*risk");
kt("-list -v")
.shouldContain("Warning:")
.shouldMatch("<a>.*MD5withRSA.*risk")
.shouldContain("MD5withRSA (weak)")
.shouldMatch("<b>.*512-bit RSA key.*risk")
.shouldContain("512-bit RSA key (weak)");
// Multiple warnings for multiple cert in -printcert or -list or -exportcert
// -certreq, -printcertreq, -gencert
checkCertReq("a", "", null);
gencert("c-a", "")
.shouldNotContain("Warning"); // new sigalg is not weak
gencert("c-a", "-sigalg MD2withRSA")
.shouldContain("Warning:")
.shouldMatch("The generated certificate.*MD2withRSA.*risk");
checkCertReq("a", "-sigalg MD5withRSA", "MD5withRSA");
gencert("c-a", "")
.shouldContain("Warning:")
.shouldMatch("The certificate request.*MD5withRSA.*risk");
gencert("c-a", "-sigalg MD2withRSA")
.shouldContain("Warning:")
.shouldMatch("The certificate request.*MD5withRSA.*risk")
.shouldMatch("The generated certificate.*MD2withRSA.*risk");
checkCertReq("b", "", "512-bit RSA key");
gencert("c-b", "")
.shouldContain("Warning:")
.shouldMatch("The certificate request.*512-bit RSA key.*risk")
.shouldMatch("The generated certificate.*512-bit RSA key.*risk");
checkCertReq("c", "", null);
gencert("a-c", "")
.shouldContain("Warning:")
.shouldMatch("The issuer.*MD5withRSA.*risk");
// but the new cert is not weak
kt("-printcert -file a-c.cert")
.shouldNotContain("Warning")
.shouldNotContain("weak");
gencert("b-c", "")
.shouldContain("Warning:")
.shouldMatch("The issuer.*512-bit RSA key.*risk");
// -importcert
checkImport();
// -importkeystore
checkImportKeyStore();
// -gencrl, -printcrl
checkGenCRL("a", "", null);
checkGenCRL("a", "-sigalg MD5withRSA", "MD5withRSA");
checkGenCRL("b", "", "512-bit RSA key");
checkGenCRL("c", "", null);
kt("-delete -alias b");
kt("-printcrl -file b.crl")
.shouldContain("WARNING: not verified");
}
static void checkImportKeyStore() throws Exception {
saveStore();
rm("ks");
kt("-importkeystore -srckeystore ks2 -srcstorepass changeit")
.shouldContain("3 entries successfully imported")
.shouldContain("Warning")
.shouldMatch("<b>.*512-bit RSA key.*risk")
.shouldMatch("<a>.*MD5withRSA.*risk");
rm("ks");
kt("-importkeystore -srckeystore ks2 -srcstorepass changeit -srcalias a")
.shouldContain("Warning")
.shouldMatch("<a>.*MD5withRSA.*risk");
reStore();
}
static void checkImport() throws Exception {
saveStore();
// add trusted cert
// cert already in
kt("-importcert -alias d -file a.cert", "no")
.shouldContain("Certificate already exists in keystore")
.shouldContain("Warning")
.shouldMatch("The input.*MD5withRSA.*risk")
.shouldContain("Do you still want to add it?");
kt("-importcert -alias d -file a.cert -noprompt")
.shouldContain("Warning")
.shouldMatch("The input.*MD5withRSA.*risk")
.shouldNotContain("[no]");
// cert is self-signed
kt("-delete -alias a");
kt("-delete -alias d");
kt("-importcert -alias d -file a.cert", "no")
.shouldContain("Warning")
.shouldContain("MD5withRSA (weak)")
.shouldMatch("The input.*MD5withRSA.*risk")
.shouldContain("Trust this certificate?");
kt("-importcert -alias d -file a.cert -noprompt")
.shouldContain("Warning")
.shouldMatch("The input.*MD5withRSA.*risk")
.shouldNotContain("[no]");
// cert is self-signed cacerts
String weakSigAlgCA = null;
KeyStore ks = KeyStoreUtil.getCacertsKeyStore();
if (ks != null) {
DisabledAlgorithmConstraints disabledCheck =
new DisabledAlgorithmConstraints(
DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS);
Set<CryptoPrimitive> sigPrimitiveSet = Collections
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
for (String s : Collections.list(ks.aliases())) {
if (ks.isCertificateEntry(s)) {
X509Certificate c = (X509Certificate)ks.getCertificate(s);
String sigAlg = c.getSigAlgName();
if (!disabledCheck.permits(sigPrimitiveSet, sigAlg, null)) {
weakSigAlgCA = sigAlg;
Files.write(Paths.get("ca.cert"),
ks.getCertificate(s).getEncoded());
break;
}
}
}
}
if (weakSigAlgCA != null) {
kt("-delete -alias d");
kt("-importcert -alias d -trustcacerts -file ca.cert", "no")
.shouldContain("Certificate already exists in system-wide CA")
.shouldContain("Warning")
.shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
.shouldContain("Do you still want to add it to your own keystore?");
kt("-importcert -alias d -file ca.cert -noprompt")
.shouldContain("Warning")
.shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
.shouldNotContain("[no]");
}
// a non self-signed weak cert
reStore();
certreq("b", "");
gencert("c-b", "");
kt("-importcert -alias d -file c-b.cert") // weak only, no prompt
.shouldContain("Warning")
.shouldNotContain("512-bit RSA key (weak)")
.shouldMatch("The input.*512-bit RSA key.*risk")
.shouldNotContain("[no]");
kt("-delete -alias b");
kt("-delete -alias c");
kt("-delete -alias d");
kt("-importcert -alias d -file c-b.cert", "no") // weak and not trusted
.shouldContain("Warning")
.shouldContain("512-bit RSA key (weak)")
.shouldMatch("The input.*512-bit RSA key.*risk")
.shouldContain("Trust this certificate?");
kt("-importcert -alias d -file c-b.cert -noprompt")
.shouldContain("Warning")
.shouldMatch("The input.*512-bit RSA key.*risk")
.shouldNotContain("[no]");
// a non self-signed strong cert
reStore();
certreq("a", "");
gencert("c-a", "");
kt("-importcert -alias d -file c-a.cert") // trusted
.shouldNotContain("Warning")
.shouldNotContain("[no]");
kt("-delete -alias a");
kt("-delete -alias c");
kt("-delete -alias d");
kt("-importcert -alias d -file c-a.cert", "no") // not trusted
.shouldNotContain("Warning")
.shouldContain("Trust this certificate?");
kt("-importcert -alias d -file c-a.cert -noprompt")
.shouldNotContain("Warning")
.shouldNotContain("[no]");
// install reply
reStore();
gencert("a-b", "");
gencert("b-c", "");
// Full chain with root
cat("a-a-b-c.cert", "b-c.cert", "a-b.cert", "a.cert");
kt("-importcert -alias c -file a-a-b-c.cert") // only weak
.shouldContain("Warning")
.shouldMatch("Reply #2 of 3.*512-bit RSA key.*risk")
.shouldMatch("Reply #3 of 3.*MD5withRSA.*risk")
.shouldNotContain("[no]");
// Without root
cat("a-b-c.cert", "b-c.cert", "a-b.cert");
kt("-importcert -alias c -file a-b-c.cert") // only weak
.shouldContain("Warning")
.shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
.shouldMatch("Issuer <a>.*MD5withRSA.*risk")
.shouldNotContain("[no]");
reStore();
gencert("b-a", "");
kt("-importcert -alias a -file b-a.cert")
.shouldContain("Warning")
.shouldMatch("Issuer <b>.*512-bit RSA key.*risk")
.shouldNotContain("[no]");
kt("-importcert -alias a -file c-a.cert")
.shouldNotContain("Warning");
kt("-importcert -alias b -file c-b.cert")
.shouldContain("Warning")
.shouldMatch("The input.*512-bit RSA key.*risk")
.shouldNotContain("[no]");
reStore();
gencert("b-a", "");
cat("c-b-a.cert", "b-a.cert", "c-b.cert");
kt("-printcert -file c-b-a.cert")
.shouldContain("Warning")
.shouldMatch("The certificate #2 of 2.*512-bit RSA key.*risk");
kt("-delete -alias b");
kt("-importcert -alias a -file c-b-a.cert")
.shouldContain("Warning")
.shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
.shouldNotContain("[no]");
kt("-delete -alias c");
kt("-importcert -alias a -file c-b-a.cert", "no")
.shouldContain("Top-level certificate in reply:")
.shouldContain("512-bit RSA key (weak)")
.shouldContain("Warning")
.shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
.shouldContain("Install reply anyway?");
kt("-importcert -alias a -file c-b-a.cert -noprompt")
.shouldContain("Warning")
.shouldMatch("Reply #2 of 2.*512-bit RSA key.*risk")
.shouldNotContain("[no]");
reStore();
}
private static void cat(String dest, String... src) throws IOException {
System.out.println("---------------------------------------------");
System.out.printf("$ cat ");
ByteArrayOutputStream bout = new ByteArrayOutputStream();
for (String s : src) {
System.out.printf(s + " ");
bout.write(Files.readAllBytes(Paths.get(s)));
}
Files.write(Paths.get(dest), bout.toByteArray());
System.out.println("> " + dest);
}
static void checkGenCRL(String alias, String options, String bad) {
OutputAnalyzer oa = kt("-gencrl -alias " + alias
+ " -id 1 -file " + alias + ".crl " + options);
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldMatch("The generated CRL.*" + bad + ".*risk");
}
oa = kt("-printcrl -file " + alias + ".crl");
if (bad == null) {
oa.shouldNotContain("Warning")
.shouldContain("Verified by " + alias + " in keystore")
.shouldNotContain("(weak");
} else {
oa.shouldContain("Warning:")
.shouldMatch("The CRL.*" + bad + ".*risk")
.shouldContain("Verified by " + alias + " in keystore")
.shouldContain(bad + " (weak)");
}
}
static void checkCertReq(
String alias, String options, String bad) {
OutputAnalyzer oa = certreq(alias, options);
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldMatch("The generated certificate request.*" + bad + ".*risk");
}
oa = kt("-printcertreq -file " + alias + ".req");
if (bad == null) {
oa.shouldNotContain("Warning")
.shouldNotContain("(weak)");
} else {
oa.shouldContain("Warning")
.shouldMatch("The certificate request.*" + bad + ".*risk")
.shouldContain(bad + " (weak)");
}
}
static void checkGenKeyPair(
String alias, String options, String bad) {
OutputAnalyzer oa = genkeypair(alias, options);
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldMatch("The generated certificate.*" + bad + ".*risk");
}
oa = kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldMatch("The certificate.*" + bad + ".*risk");
}
oa = kt("-exportcert -rfc -alias " + alias + " -file " + alias + ".cert");
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldMatch("The certificate.*" + bad + ".*risk");
}
oa = kt("-printcert -rfc -file " + alias + ".cert");
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldMatch("The certificate.*" + bad + ".*risk");
}
oa = kt("-list -alias " + alias);
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldMatch("The certificate.*" + bad + ".*risk");
}
// With cert content
oa = kt("-printcert -file " + alias + ".cert");
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldContain(bad + " (weak)")
.shouldMatch("The certificate.*" + bad + ".*risk");
}
oa = kt("-list -v -alias " + alias);
if (bad == null) {
oa.shouldNotContain("Warning");
} else {
oa.shouldContain("Warning")
.shouldContain(bad + " (weak)")
.shouldMatch("The certificate.*" + bad + ".*risk");
}
}
// This is slow, but real keytool process is launched.
static OutputAnalyzer kt1(String cmd, String... input) {
cmd = "-keystore ks -storepass changeit " +
"-keypass changeit " + cmd;
System.out.println("---------------------------------------------");
try {
SecurityTools.setResponse(input);
return SecurityTools.keytool(cmd);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
// Fast keytool execution by directly calling its main() method
static OutputAnalyzer kt(String cmd, String... input) {
PrintStream out = System.out;
PrintStream err = System.err;
InputStream ins = System.in;
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ByteArrayOutputStream berr = new ByteArrayOutputStream();
boolean succeed = true;
try {
cmd = "-keystore ks -storepass changeit " +
"-keypass changeit " + cmd;
System.out.println("---------------------------------------------");
System.out.println("$ keytool " + cmd);
System.out.println();
String feed = "";
if (input.length > 0) {
feed = Stream.of(input).collect(Collectors.joining("\n")) + "\n";
}
System.setIn(new ByteArrayInputStream(feed.getBytes()));
System.setOut(new PrintStream(bout));
System.setErr(new PrintStream(berr));
sun.security.tools.keytool.Main.main(
cmd.trim().split("\\s+"));
} catch (Exception e) {
// Might be a normal exception when -debug is on or
// SecurityException (thrown by jtreg) when System.exit() is called
if (!(e instanceof SecurityException)) {
e.printStackTrace();
}
succeed = false;
} finally {
System.setOut(out);
System.setErr(err);
System.setIn(ins);
}
String sout = new String(bout.toByteArray());
String serr = new String(berr.toByteArray());
System.out.println("STDOUT:\n" + sout + "\nSTDERR:\n" + serr);
if (!succeed) {
throw new RuntimeException();
}
return new OutputAnalyzer(sout, serr);
}
static OutputAnalyzer genkeypair(String alias, String options) {
return kt("-genkeypair -alias " + alias + " -dname CN=" + alias
+ " -keyalg RSA -storetype JKS " + options);
}
static OutputAnalyzer certreq(String alias, String options) {
return kt("-certreq -alias " + alias
+ " -file " + alias + ".req " + options);
}
static OutputAnalyzer exportcert(String alias) {
return kt("-exportcert -alias " + alias + " -file " + alias + ".cert");
}
static OutputAnalyzer gencert(String relation, String options) {
int pos = relation.indexOf("-");
String issuer = relation.substring(0, pos);
String subject = relation.substring(pos + 1);
return kt(" -gencert -alias " + issuer + " -infile " + subject
+ ".req -outfile " + relation + ".cert " + options);
}
static void saveStore() throws IOException {
System.out.println("---------------------------------------------");
System.out.println("$ cp ks ks2");
Files.copy(Paths.get("ks"), Paths.get("ks2"),
StandardCopyOption.REPLACE_EXISTING);
}
static void reStore() throws IOException {
System.out.println("---------------------------------------------");
System.out.println("$ cp ks2 ks");
Files.copy(Paths.get("ks2"), Paths.get("ks"),
StandardCopyOption.REPLACE_EXISTING);
}
static void rm(String s) throws IOException {
System.out.println("---------------------------------------------");
System.out.println("$ rm " + s);
Files.deleteIfExists(Paths.get(s));
}
}