8171319: keytool should print out warnings when reading or generating cert/cert req using weak algorithms
Reviewed-by: coffeys
This commit is contained in:
parent
080a88360a
commit
6b3143e831
@ -167,7 +167,8 @@ public class PKCS10 {
|
|||||||
// key and signature algorithm we found.
|
// key and signature algorithm we found.
|
||||||
//
|
//
|
||||||
try {
|
try {
|
||||||
sig = Signature.getInstance(id.getName());
|
sigAlg = id.getName();
|
||||||
|
sig = Signature.getInstance(sigAlg);
|
||||||
sig.initVerify(subjectPublicKeyInfo);
|
sig.initVerify(subjectPublicKeyInfo);
|
||||||
sig.update(data);
|
sig.update(data);
|
||||||
if (!sig.verify(sigData))
|
if (!sig.verify(sigData))
|
||||||
@ -218,6 +219,7 @@ public class PKCS10 {
|
|||||||
signature.update(certificateRequestInfo, 0,
|
signature.update(certificateRequestInfo, 0,
|
||||||
certificateRequestInfo.length);
|
certificateRequestInfo.length);
|
||||||
sig = signature.sign();
|
sig = signature.sign();
|
||||||
|
sigAlg = signature.getAlgorithm();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build guts of SIGNED macro
|
* Build guts of SIGNED macro
|
||||||
@ -250,6 +252,11 @@ public class PKCS10 {
|
|||||||
public PublicKey getSubjectPublicKeyInfo()
|
public PublicKey getSubjectPublicKeyInfo()
|
||||||
{ return subjectPublicKeyInfo; }
|
{ return subjectPublicKeyInfo; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the signature algorithm.
|
||||||
|
*/
|
||||||
|
public String getSigAlg() { return sigAlg; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the additional attributes requested.
|
* Returns the additional attributes requested.
|
||||||
*/
|
*/
|
||||||
@ -348,6 +355,7 @@ public class PKCS10 {
|
|||||||
|
|
||||||
private X500Name subject;
|
private X500Name subject;
|
||||||
private PublicKey subjectPublicKeyInfo;
|
private PublicKey subjectPublicKeyInfo;
|
||||||
|
private String sigAlg;
|
||||||
private PKCS10Attributes attributeSet;
|
private PKCS10Attributes attributeSet;
|
||||||
private byte[] encoded; // signed
|
private byte[] encoded; // signed
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ import sun.security.util.Debug;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* BasicChecker is a PKIXCertPathChecker that checks the basic information
|
* 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.
|
* name chaining.
|
||||||
*
|
*
|
||||||
* @since 1.4
|
* @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
|
* checks on the certificate using its internal state. This method does
|
||||||
* not remove any critical extensions from the Collection.
|
* not remove any critical extensions from the Collection.
|
||||||
*
|
*
|
||||||
@ -141,7 +141,7 @@ class BasicChecker extends PKIXCertPathChecker {
|
|||||||
X509Certificate currCert = (X509Certificate)cert;
|
X509Certificate currCert = (X509Certificate)cert;
|
||||||
|
|
||||||
if (!sigOnly) {
|
if (!sigOnly) {
|
||||||
verifyTimestamp(currCert);
|
verifyValidity(currCert);
|
||||||
verifyNameChaining(currCert);
|
verifyNameChaining(currCert);
|
||||||
}
|
}
|
||||||
verifySignature(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
|
throws CertPathValidatorException
|
||||||
{
|
{
|
||||||
String msg = "timestamp";
|
String msg = "validity";
|
||||||
if (debug != null)
|
if (debug != null)
|
||||||
debug.println("---checking " + msg + ":" + date.toString() + "...");
|
debug.println("---checking " + msg + ":" + date.toString() + "...");
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ package sun.security.tools.keytool;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.security.CodeSigner;
|
import java.security.CodeSigner;
|
||||||
|
import java.security.CryptoPrimitive;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
@ -156,6 +157,7 @@ public final class Main {
|
|||||||
private boolean protectedPath = false;
|
private boolean protectedPath = false;
|
||||||
private boolean srcprotectedPath = false;
|
private boolean srcprotectedPath = false;
|
||||||
private boolean cacerts = false;
|
private boolean cacerts = false;
|
||||||
|
private boolean nowarn = false;
|
||||||
private CertificateFactory cf = null;
|
private CertificateFactory cf = null;
|
||||||
private KeyStore caks = null; // "cacerts" keystore
|
private KeyStore caks = null; // "cacerts" keystore
|
||||||
private char[] srcstorePass = null;
|
private char[] srcstorePass = null;
|
||||||
@ -166,6 +168,16 @@ public final class Main {
|
|||||||
private List<String> ids = new ArrayList<>(); // used in GENCRL
|
private List<String> ids = new ArrayList<>(); // used in GENCRL
|
||||||
private List<String> v3ext = new ArrayList<>();
|
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 {
|
enum Command {
|
||||||
CERTREQ("Generates.a.certificate.request",
|
CERTREQ("Generates.a.certificate.request",
|
||||||
ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME,
|
ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME,
|
||||||
@ -351,7 +363,7 @@ public final class Main {
|
|||||||
private static final String NONE = "NONE";
|
private static final String NONE = "NONE";
|
||||||
private static final String P11KEYSTORE = "PKCS11";
|
private static final String P11KEYSTORE = "PKCS11";
|
||||||
private static final String P12KEYSTORE = "PKCS12";
|
private static final String P12KEYSTORE = "PKCS12";
|
||||||
private final String keyAlias = "mykey";
|
private static final String keyAlias = "mykey";
|
||||||
|
|
||||||
// for i18n
|
// for i18n
|
||||||
private static final java.util.ResourceBundle rb =
|
private static final java.util.ResourceBundle rb =
|
||||||
@ -387,6 +399,7 @@ public final class Main {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
printWeakWarnings(false);
|
||||||
for (char[] pass : passwords) {
|
for (char[] pass : passwords) {
|
||||||
if (pass != null) {
|
if (pass != null) {
|
||||||
Arrays.fill(pass, ' ');
|
Arrays.fill(pass, ' ');
|
||||||
@ -476,6 +489,8 @@ public final class Main {
|
|||||||
help = true;
|
help = true;
|
||||||
} else if (collator.compare(flags, "-conf") == 0) {
|
} else if (collator.compare(flags, "-conf") == 0) {
|
||||||
i++;
|
i++;
|
||||||
|
} else if (collator.compare(flags, "-nowarn") == 0) {
|
||||||
|
nowarn = true;
|
||||||
} else if (collator.compare(flags, "-keystore") == 0) {
|
} else if (collator.compare(flags, "-keystore") == 0) {
|
||||||
ksfname = args[++i];
|
ksfname = args[++i];
|
||||||
if (new File(ksfname).getCanonicalPath().equals(
|
if (new File(ksfname).getCanonicalPath().equals(
|
||||||
@ -1152,11 +1167,11 @@ public final class Main {
|
|||||||
} else if (command == LIST) {
|
} else if (command == LIST) {
|
||||||
if (storePass == null
|
if (storePass == null
|
||||||
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
|
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
|
||||||
printWarning();
|
printNoIntegrityWarning();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
doPrintEntry(alias, out);
|
doPrintEntry(rb.getString("the.certificate"), alias, out);
|
||||||
} else {
|
} else {
|
||||||
doPrintEntries(out);
|
doPrintEntries(out);
|
||||||
}
|
}
|
||||||
@ -1253,6 +1268,12 @@ public final class Main {
|
|||||||
throws Exception {
|
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);
|
Certificate signerCert = keyStore.getCertificate(alias);
|
||||||
byte[] encoded = signerCert.getEncoded();
|
byte[] encoded = signerCert.getEncoded();
|
||||||
X509CertImpl signerCertImpl = new X509CertImpl(encoded);
|
X509CertImpl signerCertImpl = new X509CertImpl(encoded);
|
||||||
@ -1306,6 +1327,8 @@ public final class Main {
|
|||||||
byte[] rawReq = Pem.decode(new String(sb));
|
byte[] rawReq = Pem.decode(new String(sb));
|
||||||
PKCS10 req = new PKCS10(rawReq);
|
PKCS10 req = new PKCS10(rawReq);
|
||||||
|
|
||||||
|
checkWeak(rb.getString("the.certificate.request"), req);
|
||||||
|
|
||||||
info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
|
info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
|
||||||
info.set(X509CertInfo.SUBJECT,
|
info.set(X509CertInfo.SUBJECT,
|
||||||
dname==null?req.getSubjectName():new X500Name(dname));
|
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)
|
private void doGenCRL(PrintStream out)
|
||||||
@ -1385,6 +1411,7 @@ public final class Main {
|
|||||||
} else {
|
} else {
|
||||||
out.write(crl.getEncodedInternal());
|
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
|
// Sign the request and base-64 encode it
|
||||||
request.encodeAndSign(subject, signature);
|
request.encodeAndSign(subject, signature);
|
||||||
request.print(out);
|
request.print(out);
|
||||||
|
|
||||||
|
checkWeak(rb.getString("the.generated.certificate.request"), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1454,7 +1483,7 @@ public final class Main {
|
|||||||
{
|
{
|
||||||
if (storePass == null
|
if (storePass == null
|
||||||
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
|
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
|
||||||
printWarning();
|
printNoIntegrityWarning();
|
||||||
}
|
}
|
||||||
if (alias == null) {
|
if (alias == null) {
|
||||||
alias = keyAlias;
|
alias = keyAlias;
|
||||||
@ -1474,6 +1503,7 @@ public final class Main {
|
|||||||
throw new Exception(form.format(source));
|
throw new Exception(form.format(source));
|
||||||
}
|
}
|
||||||
dumpCert(cert, out);
|
dumpCert(cert, out);
|
||||||
|
checkWeak(rb.getString("the.certificate"), cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1729,6 +1759,8 @@ public final class Main {
|
|||||||
keyPass = promptForKeyPass(alias, null, storePass);
|
keyPass = promptForKeyPass(alias, null, storePass);
|
||||||
}
|
}
|
||||||
keyStore.setKeyEntry(alias, privKey, keyPass, chain);
|
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.
|
* Prints a single keystore entry.
|
||||||
*/
|
*/
|
||||||
private void doPrintEntry(String alias, PrintStream out)
|
private void doPrintEntry(String label, String alias, PrintStream out)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
if (keyStore.containsAlias(alias) == false) {
|
if (keyStore.containsAlias(alias) == false) {
|
||||||
@ -1881,12 +1913,14 @@ public final class Main {
|
|||||||
} else {
|
} else {
|
||||||
dumpCert(chain[i], out);
|
dumpCert(chain[i], out);
|
||||||
}
|
}
|
||||||
|
checkWeak(label, chain[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Print the digest of the user cert only
|
// Print the digest of the user cert only
|
||||||
out.println
|
out.println
|
||||||
(rb.getString("Certificate.fingerprint.SHA.256.") +
|
(rb.getString("Certificate.fingerprint.SHA.256.") +
|
||||||
getCertFingerPrint("SHA-256", chain[0]));
|
getCertFingerPrint("SHA-256", chain[0]));
|
||||||
|
checkWeak(label, chain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (keyStore.entryInstanceOf(alias,
|
} else if (keyStore.entryInstanceOf(alias,
|
||||||
@ -1909,6 +1943,7 @@ public final class Main {
|
|||||||
out.println(rb.getString("Certificate.fingerprint.SHA.256.")
|
out.println(rb.getString("Certificate.fingerprint.SHA.256.")
|
||||||
+ getCertFingerPrint("SHA-256", cert));
|
+ getCertFingerPrint("SHA-256", cert));
|
||||||
}
|
}
|
||||||
|
checkWeak(label, cert);
|
||||||
} else {
|
} else {
|
||||||
out.println(rb.getString("Unknown.Entry.Type"));
|
out.println(rb.getString("Unknown.Entry.Type"));
|
||||||
}
|
}
|
||||||
@ -1992,7 +2027,7 @@ public final class Main {
|
|||||||
|
|
||||||
if (srcstorePass == null
|
if (srcstorePass == null
|
||||||
&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
|
&& !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
|
||||||
// anti refactoring, copied from printWarning(),
|
// anti refactoring, copied from printNoIntegrityWarning(),
|
||||||
// but change 2 lines
|
// but change 2 lines
|
||||||
System.err.println();
|
System.err.println();
|
||||||
System.err.println(rb.getString
|
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."));
|
"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;
|
return 1;
|
||||||
} catch (KeyStoreException kse) {
|
} catch (KeyStoreException kse) {
|
||||||
Object[] source2 = {alias, kse.toString()};
|
Object[] source2 = {alias, kse.toString()};
|
||||||
@ -2154,7 +2193,7 @@ public final class Main {
|
|||||||
for (Enumeration<String> e = keyStore.aliases();
|
for (Enumeration<String> e = keyStore.aliases();
|
||||||
e.hasMoreElements(); ) {
|
e.hasMoreElements(); ) {
|
||||||
String alias = e.nextElement();
|
String alias = e.nextElement();
|
||||||
doPrintEntry(alias, out);
|
doPrintEntry("<" + alias + ">", alias, out);
|
||||||
if (verbose || rfc) {
|
if (verbose || rfc) {
|
||||||
out.println(rb.getString("NEWLINE"));
|
out.println(rb.getString("NEWLINE"));
|
||||||
out.println(rb.getString
|
out.println(rb.getString
|
||||||
@ -2300,19 +2339,28 @@ public final class Main {
|
|||||||
for (CRL crl: loadCRLs(src)) {
|
for (CRL crl: loadCRLs(src)) {
|
||||||
printCRL(crl, out);
|
printCRL(crl, out);
|
||||||
String issuer = null;
|
String issuer = null;
|
||||||
|
Certificate signer = null;
|
||||||
if (caks != null) {
|
if (caks != null) {
|
||||||
issuer = verifyCRL(caks, crl);
|
issuer = verifyCRL(caks, crl);
|
||||||
if (issuer != null) {
|
if (issuer != null) {
|
||||||
|
signer = caks.getCertificate(issuer);
|
||||||
out.printf(rb.getString(
|
out.printf(rb.getString(
|
||||||
"verified.by.s.in.s"), issuer, "cacerts");
|
"verified.by.s.in.s.weak"),
|
||||||
|
issuer,
|
||||||
|
"cacerts",
|
||||||
|
withWeak(signer.getPublicKey()));
|
||||||
out.println();
|
out.println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (issuer == null && keyStore != null) {
|
if (issuer == null && keyStore != null) {
|
||||||
issuer = verifyCRL(keyStore, crl);
|
issuer = verifyCRL(keyStore, crl);
|
||||||
if (issuer != null) {
|
if (issuer != null) {
|
||||||
|
signer = keyStore.getCertificate(issuer);
|
||||||
out.printf(rb.getString(
|
out.printf(rb.getString(
|
||||||
"verified.by.s.in.s"), issuer, "keystore");
|
"verified.by.s.in.s.weak"),
|
||||||
|
issuer,
|
||||||
|
"keystore",
|
||||||
|
withWeak(signer.getPublicKey()));
|
||||||
out.println();
|
out.println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2324,18 +2372,26 @@ public final class Main {
|
|||||||
out.println(rb.getString
|
out.println(rb.getString
|
||||||
("STARNN"));
|
("STARNN"));
|
||||||
}
|
}
|
||||||
|
checkWeak(rb.getString("the.crl"), crl, signer == null ? null : signer.getPublicKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printCRL(CRL crl, PrintStream out)
|
private void printCRL(CRL crl, PrintStream out)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
X509CRL xcrl = (X509CRL)crl;
|
||||||
if (rfc) {
|
if (rfc) {
|
||||||
X509CRL xcrl = (X509CRL)crl;
|
|
||||||
out.println("-----BEGIN X509 CRL-----");
|
out.println("-----BEGIN X509 CRL-----");
|
||||||
out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));
|
out.println(Base64.getMimeEncoder(64, CRLF).encodeToString(xcrl.getEncoded()));
|
||||||
out.println("-----END X509 CRL-----");
|
out.println("-----END X509 CRL-----");
|
||||||
} else {
|
} 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)));
|
PKCS10 req = new PKCS10(Pem.decode(new String(sb)));
|
||||||
|
|
||||||
PublicKey pkey = req.getSubjectPublicKeyInfo();
|
PublicKey pkey = req.getSubjectPublicKeyInfo();
|
||||||
out.printf(rb.getString("PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key."),
|
out.printf(rb.getString("PKCS.10.with.weak"),
|
||||||
req.getSubjectName(), pkey.getFormat(), pkey.getAlgorithm());
|
req.getSubjectName(),
|
||||||
|
pkey.getFormat(),
|
||||||
|
withWeak(pkey),
|
||||||
|
withWeak(req.getSigAlg()));
|
||||||
for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {
|
for (PKCS10Attribute attr: req.getAttributes().getAttributes()) {
|
||||||
ObjectIdentifier oid = attr.getAttributeId();
|
ObjectIdentifier oid = attr.getAttributeId();
|
||||||
if (oid.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {
|
if (oid.equals(PKCS9Attribute.EXTENSION_REQUEST_OID)) {
|
||||||
@ -2386,6 +2445,7 @@ public final class Main {
|
|||||||
if (debug) {
|
if (debug) {
|
||||||
out.println(req); // Just to see more, say, public key length...
|
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)) {
|
if (i < (certs.length-1)) {
|
||||||
out.println();
|
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();
|
||||||
out.println(rb.getString("Signature."));
|
out.println(rb.getString("Signature."));
|
||||||
out.println();
|
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;
|
X509Certificate x = (X509Certificate)cert;
|
||||||
if (rfc) {
|
if (rfc) {
|
||||||
out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
|
out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
|
||||||
@ -2467,12 +2540,15 @@ public final class Main {
|
|||||||
printX509Cert(x, out);
|
printX509Cert(x, out);
|
||||||
}
|
}
|
||||||
out.println();
|
out.println();
|
||||||
|
checkWeak(oneInMany(rb.getString("the.certificate"), cc++, certs.size()), x);
|
||||||
}
|
}
|
||||||
Timestamp ts = signer.getTimestamp();
|
Timestamp ts = signer.getTimestamp();
|
||||||
if (ts != null) {
|
if (ts != null) {
|
||||||
out.println(rb.getString("Timestamp."));
|
out.println(rb.getString("Timestamp."));
|
||||||
out.println();
|
out.println();
|
||||||
for (Certificate cert: ts.getSignerCertPath().getCertificates()) {
|
certs = ts.getSignerCertPath().getCertificates();
|
||||||
|
cc = 0;
|
||||||
|
for (Certificate cert: certs) {
|
||||||
X509Certificate x = (X509Certificate)cert;
|
X509Certificate x = (X509Certificate)cert;
|
||||||
if (rfc) {
|
if (rfc) {
|
||||||
out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
|
out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n");
|
||||||
@ -2481,6 +2557,7 @@ public final class Main {
|
|||||||
printX509Cert(x, out);
|
printX509Cert(x, out);
|
||||||
}
|
}
|
||||||
out.println();
|
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);
|
printX509Cert((X509Certificate)cert, out);
|
||||||
out.println();
|
out.println();
|
||||||
}
|
}
|
||||||
|
checkWeak(oneInMany(rb.getString("the.certificate"), i, chain.size()), cert);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -2698,7 +2776,7 @@ public final class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now store the newly established chain in the keystore. The new
|
// 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) {
|
if (newChain != null) {
|
||||||
keyStore.setKeyEntry(alias, privKey,
|
keyStore.setKeyEntry(alias, privKey,
|
||||||
(keyPass != null) ? keyPass : storePass,
|
(keyPass != null) ? keyPass : storePass,
|
||||||
@ -2735,6 +2813,12 @@ public final class Main {
|
|||||||
throw new Exception(rb.getString("Input.not.an.X.509.certificate"));
|
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
|
// if certificate is self-signed, make sure it verifies
|
||||||
boolean selfSigned = false;
|
boolean selfSigned = false;
|
||||||
if (KeyStoreUtil.isSelfSigned(cert)) {
|
if (KeyStoreUtil.isSelfSigned(cert)) {
|
||||||
@ -2742,11 +2826,6 @@ public final class Main {
|
|||||||
selfSigned = true;
|
selfSigned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noprompt) {
|
|
||||||
keyStore.setCertificateEntry(alias, cert);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if cert already exists in keystore
|
// check if cert already exists in keystore
|
||||||
String reply = null;
|
String reply = null;
|
||||||
String trustalias = keyStore.getCertificateAlias(cert);
|
String trustalias = keyStore.getCertificateAlias(cert);
|
||||||
@ -2755,6 +2834,8 @@ public final class Main {
|
|||||||
("Certificate.already.exists.in.keystore.under.alias.trustalias."));
|
("Certificate.already.exists.in.keystore.under.alias.trustalias."));
|
||||||
Object[] source = {trustalias};
|
Object[] source = {trustalias};
|
||||||
System.err.println(form.format(source));
|
System.err.println(form.format(source));
|
||||||
|
checkWeak(rb.getString("the.input"), cert);
|
||||||
|
printWeakWarnings(true);
|
||||||
reply = getYesNoReply
|
reply = getYesNoReply
|
||||||
(rb.getString("Do.you.still.want.to.add.it.no."));
|
(rb.getString("Do.you.still.want.to.add.it.no."));
|
||||||
} else if (selfSigned) {
|
} else if (selfSigned) {
|
||||||
@ -2764,6 +2845,8 @@ public final class Main {
|
|||||||
("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));
|
("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias."));
|
||||||
Object[] source = {trustalias};
|
Object[] source = {trustalias};
|
||||||
System.err.println(form.format(source));
|
System.err.println(form.format(source));
|
||||||
|
checkWeak(rb.getString("the.input"), cert);
|
||||||
|
printWeakWarnings(true);
|
||||||
reply = getYesNoReply
|
reply = getYesNoReply
|
||||||
(rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no."));
|
(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
|
// Print the cert and ask user if they really want to add
|
||||||
// it to their keystore
|
// it to their keystore
|
||||||
printX509Cert(cert, System.out);
|
printX509Cert(cert, System.out);
|
||||||
|
checkWeak(rb.getString("the.input"), cert);
|
||||||
|
printWeakWarnings(true);
|
||||||
reply = getYesNoReply
|
reply = getYesNoReply
|
||||||
(rb.getString("Trust.this.certificate.no."));
|
(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 to establish trust chain
|
||||||
try {
|
try {
|
||||||
Certificate[] chain = establishCertChain(null, cert);
|
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
|
// Print the cert and ask user if they really want to add it to
|
||||||
// their keystore
|
// their keystore
|
||||||
printX509Cert(cert, System.out);
|
printX509Cert(cert, System.out);
|
||||||
|
checkWeak(rb.getString("the.input"), cert);
|
||||||
|
printWeakWarnings(true);
|
||||||
reply = getYesNoReply
|
reply = getYesNoReply
|
||||||
(rb.getString("Trust.this.certificate.no."));
|
(rb.getString("Trust.this.certificate.no."));
|
||||||
if ("YES".equals(reply)) {
|
if ("YES".equals(reply)) {
|
||||||
@ -2933,6 +3021,24 @@ public final class Main {
|
|||||||
return keyPass;
|
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.
|
* Prints a certificate in a human readable format.
|
||||||
*/
|
*/
|
||||||
@ -2941,7 +3047,7 @@ public final class Main {
|
|||||||
{
|
{
|
||||||
|
|
||||||
MessageFormat form = new MessageFormat
|
MessageFormat form = new MessageFormat
|
||||||
(rb.getString(".PATTERN.printX509Cert"));
|
(rb.getString(".PATTERN.printX509Cert.with.weak"));
|
||||||
PublicKey pkey = cert.getPublicKey();
|
PublicKey pkey = cert.getPublicKey();
|
||||||
Object[] source = {cert.getSubjectDN().toString(),
|
Object[] source = {cert.getSubjectDN().toString(),
|
||||||
cert.getIssuerDN().toString(),
|
cert.getIssuerDN().toString(),
|
||||||
@ -2950,10 +3056,9 @@ public final class Main {
|
|||||||
cert.getNotAfter().toString(),
|
cert.getNotAfter().toString(),
|
||||||
getCertFingerPrint("SHA-1", cert),
|
getCertFingerPrint("SHA-1", cert),
|
||||||
getCertFingerPrint("SHA-256", cert),
|
getCertFingerPrint("SHA-256", cert),
|
||||||
cert.getSigAlgName(),
|
withWeak(cert.getSigAlgName()),
|
||||||
pkey.getAlgorithm(),
|
withWeak(pkey),
|
||||||
KeyUtil.getKeySize(pkey),
|
cert.getVersion()
|
||||||
cert.getVersion(),
|
|
||||||
};
|
};
|
||||||
out.println(form.format(source));
|
out.println(form.format(source));
|
||||||
|
|
||||||
@ -3003,12 +3108,12 @@ public final class Main {
|
|||||||
* @param ks the keystore to search with, not null
|
* @param ks the keystore to search with, not null
|
||||||
* @return <code>cert</code> itself if it's already inside <code>ks</code>,
|
* @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 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)
|
private static Pair<String,Certificate>
|
||||||
throws Exception {
|
getTrustedSigner(Certificate cert, KeyStore ks) throws Exception {
|
||||||
if (ks.getCertificateAlias(cert) != null) {
|
if (ks.getCertificateAlias(cert) != null) {
|
||||||
return cert;
|
return new Pair<>("", cert);
|
||||||
}
|
}
|
||||||
for (Enumeration<String> aliases = ks.aliases();
|
for (Enumeration<String> aliases = ks.aliases();
|
||||||
aliases.hasMoreElements(); ) {
|
aliases.hasMoreElements(); ) {
|
||||||
@ -3017,7 +3122,7 @@ public final class Main {
|
|||||||
if (trustedCert != null) {
|
if (trustedCert != null) {
|
||||||
try {
|
try {
|
||||||
cert.verify(trustedCert.getPublicKey());
|
cert.verify(trustedCert.getPublicKey());
|
||||||
return trustedCert;
|
return new Pair<>(name, trustedCert);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Not verified, skip to the next one
|
// Not verified, skip to the next one
|
||||||
}
|
}
|
||||||
@ -3281,7 +3386,7 @@ public final class Main {
|
|||||||
/**
|
/**
|
||||||
* Prints warning about missing integrity check.
|
* Prints warning about missing integrity check.
|
||||||
*/
|
*/
|
||||||
private void printWarning() {
|
private void printNoIntegrityWarning() {
|
||||||
System.err.println();
|
System.err.println();
|
||||||
System.err.println(rb.getString
|
System.err.println(rb.getString
|
||||||
(".WARNING.WARNING.WARNING."));
|
(".WARNING.WARNING.WARNING."));
|
||||||
@ -3306,6 +3411,9 @@ public final class Main {
|
|||||||
Certificate[] replyCerts)
|
Certificate[] replyCerts)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
|
checkWeak(rb.getString("reply"), replyCerts);
|
||||||
|
|
||||||
// order the certs in the reply (bottom-up).
|
// order the certs in the reply (bottom-up).
|
||||||
// we know that all certs in the reply are of type X.509, because
|
// we know that all certs in the reply are of type X.509, because
|
||||||
// we parsed them using an X.509 certificate factory
|
// 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?
|
// do we trust the cert at the top?
|
||||||
Certificate topCert = replyCerts[replyCerts.length-1];
|
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) {
|
if (root == null && trustcacerts && caks != null) {
|
||||||
root = getTrustedSigner(topCert, caks);
|
root = getTrustedSigner(topCert, caks);
|
||||||
|
fromKeyStore = false;
|
||||||
}
|
}
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
System.err.println();
|
System.err.println();
|
||||||
@ -3369,33 +3479,42 @@ public final class Main {
|
|||||||
printX509Cert((X509Certificate)topCert, System.out);
|
printX509Cert((X509Certificate)topCert, System.out);
|
||||||
System.err.println();
|
System.err.println();
|
||||||
System.err.print(rb.getString(".is.not.trusted."));
|
System.err.print(rb.getString(".is.not.trusted."));
|
||||||
|
printWeakWarnings(true);
|
||||||
String reply = getYesNoReply
|
String reply = getYesNoReply
|
||||||
(rb.getString("Install.reply.anyway.no."));
|
(rb.getString("Install.reply.anyway.no."));
|
||||||
if ("NO".equals(reply)) {
|
if ("NO".equals(reply)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (root != topCert) {
|
if (root.snd != topCert) {
|
||||||
// append the root CA cert to the chain
|
// append the root CA cert to the chain
|
||||||
Certificate[] tmpCerts =
|
Certificate[] tmpCerts =
|
||||||
new Certificate[replyCerts.length+1];
|
new Certificate[replyCerts.length+1];
|
||||||
System.arraycopy(replyCerts, 0, tmpCerts, 0,
|
System.arraycopy(replyCerts, 0, tmpCerts, 0,
|
||||||
replyCerts.length);
|
replyCerts.length);
|
||||||
tmpCerts[tmpCerts.length-1] = root;
|
tmpCerts[tmpCerts.length-1] = root.snd;
|
||||||
replyCerts = tmpCerts;
|
replyCerts = tmpCerts;
|
||||||
|
checkWeak(String.format(rb.getString(fromKeyStore ?
|
||||||
|
"alias.in.keystore" :
|
||||||
|
"alias.in.cacerts"),
|
||||||
|
root.fst),
|
||||||
|
root.snd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return replyCerts;
|
return replyCerts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Establishes a certificate chain (using trusted certificates in the
|
* 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.
|
* and ending at a self-signed certificate found in the keystore.
|
||||||
*
|
*
|
||||||
* @param userCert the user certificate of the alias
|
* @param userCert optional existing certificate, mostly likely be the
|
||||||
* @param certToVerify the single certificate provided in the reply
|
* 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,
|
private Certificate[] establishCertChain(Certificate userCert,
|
||||||
Certificate certToVerify)
|
Certificate certToVerify)
|
||||||
@ -3423,30 +3542,37 @@ public final class Main {
|
|||||||
// Use the subject distinguished name as the key into the hash table.
|
// Use the subject distinguished name as the key into the hash table.
|
||||||
// All certificates associated with the same subject distinguished
|
// All certificates associated with the same subject distinguished
|
||||||
// name are stored in the same hash table entry as a vector.
|
// 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) {
|
if (keyStore.size() > 0) {
|
||||||
certs = new Hashtable<Principal, Vector<Certificate>>(11);
|
certs = new Hashtable<>(11);
|
||||||
keystorecerts2Hashtable(keyStore, certs);
|
keystorecerts2Hashtable(keyStore, certs);
|
||||||
}
|
}
|
||||||
if (trustcacerts) {
|
if (trustcacerts) {
|
||||||
if (caks!=null && caks.size()>0) {
|
if (caks!=null && caks.size()>0) {
|
||||||
if (certs == null) {
|
if (certs == null) {
|
||||||
certs = new Hashtable<Principal, Vector<Certificate>>(11);
|
certs = new Hashtable<>(11);
|
||||||
}
|
}
|
||||||
keystorecerts2Hashtable(caks, certs);
|
keystorecerts2Hashtable(caks, certs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start building chain
|
// start building chain
|
||||||
Vector<Certificate> chain = new Vector<>(2);
|
Vector<Pair<String,X509Certificate>> chain = new Vector<>(2);
|
||||||
if (buildChain((X509Certificate)certToVerify, chain, certs)) {
|
if (buildChain(
|
||||||
Certificate[] newChain = new Certificate[chain.size()];
|
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
|
// buildChain() returns chain with self-signed root-cert first and
|
||||||
// user-cert last, so we need to invert the chain before we store
|
// user-cert last, so we need to invert the chain before we store
|
||||||
// it
|
// it
|
||||||
int j=0;
|
int j=0;
|
||||||
for (int i=chain.size()-1; i>=0; i--) {
|
for (int i=chain.size()-1; i>=0; i--) {
|
||||||
newChain[j] = chain.elementAt(i);
|
newChain[j] = chain.elementAt(i).snd;
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
return newChain;
|
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 certToVerify the cert that needs to be verified.
|
||||||
* @param chain the chain that's being built.
|
* @param chain the chain that's being built.
|
||||||
@ -3465,19 +3601,20 @@ public final class Main {
|
|||||||
*
|
*
|
||||||
* @return true if successful, false otherwise.
|
* @return true if successful, false otherwise.
|
||||||
*/
|
*/
|
||||||
private boolean buildChain(X509Certificate certToVerify,
|
private boolean buildChain(Pair<String,X509Certificate> certToVerify,
|
||||||
Vector<Certificate> chain,
|
Vector<Pair<String,X509Certificate>> chain,
|
||||||
Hashtable<Principal, Vector<Certificate>> certs) {
|
Hashtable<Principal, Vector<Pair<String,X509Certificate>>> certs) {
|
||||||
Principal issuer = certToVerify.getIssuerDN();
|
if (KeyStoreUtil.isSelfSigned(certToVerify.snd)) {
|
||||||
if (KeyStoreUtil.isSelfSigned(certToVerify)) {
|
|
||||||
// reached self-signed root cert;
|
// reached self-signed root cert;
|
||||||
// no verification needed because it's trusted.
|
// no verification needed because it's trusted.
|
||||||
chain.addElement(certToVerify);
|
chain.addElement(certToVerify);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Principal issuer = certToVerify.snd.getIssuerDN();
|
||||||
|
|
||||||
// Get the issuer's certificate(s)
|
// Get the issuer's certificate(s)
|
||||||
Vector<Certificate> vec = certs.get(issuer);
|
Vector<Pair<String,X509Certificate>> vec = certs.get(issuer);
|
||||||
if (vec == null) {
|
if (vec == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3485,13 +3622,12 @@ public final class Main {
|
|||||||
// Try out each certificate in the vector, until we find one
|
// Try out each certificate in the vector, until we find one
|
||||||
// whose public key verifies the signature of the certificate
|
// whose public key verifies the signature of the certificate
|
||||||
// in question.
|
// in question.
|
||||||
for (Enumeration<Certificate> issuerCerts = vec.elements();
|
for (Enumeration<Pair<String,X509Certificate>> issuerCerts = vec.elements();
|
||||||
issuerCerts.hasMoreElements(); ) {
|
issuerCerts.hasMoreElements(); ) {
|
||||||
X509Certificate issuerCert
|
Pair<String,X509Certificate> issuerCert = issuerCerts.nextElement();
|
||||||
= (X509Certificate)issuerCerts.nextElement();
|
PublicKey issuerPubKey = issuerCert.snd.getPublicKey();
|
||||||
PublicKey issuerPubKey = issuerCert.getPublicKey();
|
|
||||||
try {
|
try {
|
||||||
certToVerify.verify(issuerPubKey);
|
certToVerify.snd.verify(issuerPubKey);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3541,10 +3677,11 @@ public final class Main {
|
|||||||
/**
|
/**
|
||||||
* Stores the (leaf) certificates of a keystore in a hashtable.
|
* Stores the (leaf) certificates of a keystore in a hashtable.
|
||||||
* All certs belonging to the same CA are stored in a vector that
|
* 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,
|
private void keystorecerts2Hashtable(KeyStore ks,
|
||||||
Hashtable<Principal, Vector<Certificate>> hash)
|
Hashtable<Principal, Vector<Pair<String,X509Certificate>>> hash)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
for (Enumeration<String> aliases = ks.aliases();
|
for (Enumeration<String> aliases = ks.aliases();
|
||||||
@ -3553,13 +3690,20 @@ public final class Main {
|
|||||||
Certificate cert = ks.getCertificate(alias);
|
Certificate cert = ks.getCertificate(alias);
|
||||||
if (cert != null) {
|
if (cert != null) {
|
||||||
Principal subjectDN = ((X509Certificate)cert).getSubjectDN();
|
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) {
|
if (vec == null) {
|
||||||
vec = new Vector<Certificate>();
|
vec = new Vector<>();
|
||||||
vec.addElement(cert);
|
vec.addElement(pair);
|
||||||
} else {
|
} else {
|
||||||
if (!vec.contains(cert)) {
|
if (!vec.contains(pair)) {
|
||||||
vec.addElement(cert);
|
vec.addElement(pair);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash.put(subjectDN, vec);
|
hash.put(subjectDN, vec);
|
||||||
@ -4157,6 +4301,67 @@ public final class Main {
|
|||||||
return result;
|
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.
|
* Prints the usage of this tool.
|
||||||
*/
|
*/
|
||||||
|
@ -360,8 +360,6 @@ public class Resources extends java.util.ListResourceBundle {
|
|||||||
{"Enter.alias.name.", "Enter alias name: "},
|
{"Enter.alias.name.", "Enter alias name: "},
|
||||||
{".RETURN.if.same.as.for.otherAlias.",
|
{".RETURN.if.same.as.for.otherAlias.",
|
||||||
"\t(RETURN if same as for <{0}>)"},
|
"\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 your first and last name?"},
|
"What is your first and last name?"},
|
||||||
{"What.is.the.name.of.your.organizational.unit.",
|
{"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",
|
||||||
"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",
|
||||||
"WARNING: not verified. Make sure -keystore is correct."},
|
"WARNING: not verified. Make sure -keystore is correct."},
|
||||||
|
|
||||||
{"Extensions.", "Extensions: "},
|
{"Extensions.", "Extensions: "},
|
||||||
{".Empty.value.", "(Empty value)"},
|
{".Empty.value.", "(Empty value)"},
|
||||||
{"Extension.Request.", "Extension Request:"},
|
{"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.keyUsage.type.", "Unknown keyUsage type: "},
|
||||||
{"Unknown.extendedkeyUsage.type.", "Unknown extendedkeyUsage type: "},
|
{"Unknown.extendedkeyUsage.type.", "Unknown extendedkeyUsage type: "},
|
||||||
{"Unknown.AccessDescription.type.", "Unknown AccessDescription 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. "},
|
"This extension cannot be marked as critical. "},
|
||||||
{"Odd.number.of.hex.digits.found.", "Odd number of hex digits found: "},
|
{"Odd.number.of.hex.digits.found.", "Odd number of hex digits found: "},
|
||||||
{"Unknown.extension.type.", "Unknown extension type: "},
|
{"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."},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -536,13 +536,18 @@ public class X509CRLImpl extends X509CRL implements DerEncoder {
|
|||||||
* @return value of this CRL in a printable form.
|
* @return value of this CRL in a printable form.
|
||||||
*/
|
*/
|
||||||
public String toString() {
|
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();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("X.509 CRL v")
|
sb.append("X.509 CRL v")
|
||||||
.append(version+1)
|
.append(version+1)
|
||||||
.append('\n');
|
.append('\n');
|
||||||
if (sigAlgId != null)
|
if (sigAlgId != null)
|
||||||
sb.append("Signature Algorithm: ")
|
sb.append("Signature Algorithm: ")
|
||||||
.append(sigAlgId)
|
.append(name)
|
||||||
.append(", OID=")
|
.append(", OID=")
|
||||||
.append(sigAlgId.getOID())
|
.append(sigAlgId.getOID())
|
||||||
.append('\n');
|
.append('\n');
|
||||||
|
@ -43,6 +43,7 @@ import java.util.List;
|
|||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
import jdk.test.lib.SecurityTools;
|
||||||
import jdk.testlibrary.*;
|
import jdk.testlibrary.*;
|
||||||
import jdk.testlibrary.JarUtils;
|
import jdk.testlibrary.JarUtils;
|
||||||
import sun.security.pkcs.ContentInfo;
|
import sun.security.pkcs.ContentInfo;
|
||||||
@ -66,6 +67,7 @@ import sun.security.x509.X500Name;
|
|||||||
* java.base/sun.security.util
|
* java.base/sun.security.util
|
||||||
* java.base/sun.security.tools.keytool
|
* java.base/sun.security.tools.keytool
|
||||||
* @library /lib/testlibrary
|
* @library /lib/testlibrary
|
||||||
|
* @library /test/lib
|
||||||
* @run main/timeout=600 TimestampCheck
|
* @run main/timeout=600 TimestampCheck
|
||||||
*/
|
*/
|
||||||
public class TimestampCheck {
|
public class TimestampCheck {
|
||||||
@ -457,6 +459,18 @@ public class TimestampCheck {
|
|||||||
verify(file, "-J-Djava.security.debug=jar")
|
verify(file, "-J-Djava.security.debug=jar")
|
||||||
.shouldHaveExitValue(0)
|
.shouldHaveExitValue(0)
|
||||||
.shouldMatch("SignatureException:.*disabled");
|
.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 {
|
static void checkHalfWeak(String file) throws Throwable {
|
||||||
|
557
jdk/test/sun/security/tools/keytool/WeakAlg.java
Normal file
557
jdk/test/sun/security/tools/keytool/WeakAlg.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user