提交 65e38ffc 编写于 作者: A asaha

Merge

...@@ -720,6 +720,7 @@ bd40efd56b4544ff9048d2f7be4cf108b281a6f3 jdk8u151-b02 ...@@ -720,6 +720,7 @@ bd40efd56b4544ff9048d2f7be4cf108b281a6f3 jdk8u151-b02
0efdf2c7a21464e5f3d89474ffdfe81db61031fd jdk8u151-b05 0efdf2c7a21464e5f3d89474ffdfe81db61031fd jdk8u151-b05
c6c870e267de694bc85dc4af23a648824063f95b jdk8u151-b06 c6c870e267de694bc85dc4af23a648824063f95b jdk8u151-b06
84b0fbbfb8d664031a9f5283f79b13b98714cc7f jdk8u151-b07 84b0fbbfb8d664031a9f5283f79b13b98714cc7f jdk8u151-b07
8fd79358682edc86abaac1c839486834410be74b jdk8u151-b08
1442bc728814af451e2dd1a6719a64485d27e3a0 jdk8u122-b00 1442bc728814af451e2dd1a6719a64485d27e3a0 jdk8u122-b00
f6030acfa5aec0e64d45adfac69b9e7e5c12bc74 jdk8u122-b01 f6030acfa5aec0e64d45adfac69b9e7e5c12bc74 jdk8u122-b01
6b072c3a6db7ab06804c91aab77431799dfb5d47 jdk8u122-b02 6b072c3a6db7ab06804c91aab77431799dfb5d47 jdk8u122-b02
......
...@@ -167,7 +167,8 @@ public class PKCS10 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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() + "...");
......
...@@ -26,7 +26,10 @@ ...@@ -26,7 +26,10 @@
package sun.security.tools.keytool; package sun.security.tools.keytool;
import java.io.*; import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
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;
...@@ -64,6 +67,9 @@ import java.security.cert.X509CRLEntry; ...@@ -64,6 +67,9 @@ import java.security.cert.X509CRLEntry;
import java.security.cert.X509CRLSelector; import java.security.cert.X509CRLSelector;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import java.util.Base64; import java.util.Base64;
import sun.security.util.DisabledAlgorithmConstraints;
import sun.security.util.KeyUtil;
import sun.security.util.ObjectIdentifier; import sun.security.util.ObjectIdentifier;
import sun.security.pkcs10.PKCS10; import sun.security.pkcs10.PKCS10;
import sun.security.pkcs10.PKCS10Attribute; import sun.security.pkcs10.PKCS10Attribute;
...@@ -147,6 +153,7 @@ public final class Main { ...@@ -147,6 +153,7 @@ public final class Main {
private boolean kssave = false; private boolean kssave = false;
private boolean noprompt = false; private boolean noprompt = false;
private boolean trustcacerts = false; private boolean trustcacerts = false;
private boolean nowarn = false;
private boolean protectedPath = false; private boolean protectedPath = false;
private boolean srcprotectedPath = false; private boolean srcprotectedPath = false;
private CertificateFactory cf = null; private CertificateFactory cf = null;
...@@ -159,6 +166,21 @@ public final class Main { ...@@ -159,6 +166,21 @@ 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<>();
// In-place importkeystore is special.
// A backup is needed, and no need to prompt for deststorepass.
private boolean inplaceImport = false;
private String inplaceBackupName = null;
// Warnings on weak algorithms etc
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,
...@@ -317,7 +339,7 @@ public final class Main { ...@@ -317,7 +339,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 =
...@@ -353,6 +375,7 @@ public final class Main { ...@@ -353,6 +375,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, ' ');
...@@ -420,12 +443,10 @@ public final class Main { ...@@ -420,12 +443,10 @@ public final class Main {
command = IMPORTCERT; command = IMPORTCERT;
} else if (collator.compare(flags, "-importpassword") == 0) { } else if (collator.compare(flags, "-importpassword") == 0) {
command = IMPORTPASS; command = IMPORTPASS;
} } else if (collator.compare(flags, "-help") == 0) {
/*
* Help
*/
else if (collator.compare(flags, "-help") == 0) {
help = true; help = true;
} else if (collator.compare(flags, "-nowarn") == 0) {
nowarn = true;
} }
/* /*
...@@ -713,18 +734,34 @@ public final class Main { ...@@ -713,18 +734,34 @@ public final class Main {
("New.password.must.be.at.least.6.characters")); ("New.password.must.be.at.least.6.characters"));
} }
// Set this before inplaceImport check so we can compare name.
if (ksfname == null) {
ksfname = System.getProperty("user.home") + File.separator
+ ".keystore";
}
KeyStore srcKeyStore = null;
if (command == IMPORTKEYSTORE) {
inplaceImport = inplaceImportCheck();
if (inplaceImport) {
// We load srckeystore first so we have srcstorePass that
// can be assigned to storePass
srcKeyStore = loadSourceKeyStore();
if (storePass == null) {
storePass = srcstorePass;
}
}
}
// Check if keystore exists. // Check if keystore exists.
// If no keystore has been specified at the command line, try to use // If no keystore has been specified at the command line, try to use
// the default, which is located in $HOME/.keystore. // the default, which is located in $HOME/.keystore.
// If the command is "genkey", "identitydb", "import", or "printcert", // If the command is "genkey", "identitydb", "import", or "printcert",
// it is OK not to have a keystore. // it is OK not to have a keystore.
if (isKeyStoreRelated(command)) {
if (ksfname == null) {
ksfname = System.getProperty("user.home") + File.separator
+ ".keystore";
}
if (!nullStream) { // DO NOT open the existing keystore if this is an in-place import.
// The keystore should be created as brand new.
if (isKeyStoreRelated(command) && !nullStream && !inplaceImport) {
try { try {
ksfile = new File(ksfname); ksfile = new File(ksfname);
// Check if keystore file is empty // Check if keystore file is empty
...@@ -746,7 +783,6 @@ public final class Main { ...@@ -746,7 +783,6 @@ public final class Main {
} }
} }
} }
}
if ((command == KEYCLONE || command == CHANGEALIAS) if ((command == KEYCLONE || command == CHANGEALIAS)
&& dest == null) { && dest == null) {
...@@ -792,7 +828,11 @@ public final class Main { ...@@ -792,7 +828,11 @@ public final class Main {
* Null stream keystores are loaded later. * Null stream keystores are loaded later.
*/ */
if (!nullStream) { if (!nullStream) {
if (inplaceImport) {
keyStore.load(null, storePass);
} else {
keyStore.load(ksStream, storePass); keyStore.load(ksStream, storePass);
}
if (ksStream != null) { if (ksStream != null) {
ksStream.close(); ksStream.close();
} }
...@@ -920,6 +960,13 @@ public final class Main { ...@@ -920,6 +960,13 @@ public final class Main {
cf = CertificateFactory.getInstance("X509"); cf = CertificateFactory.getInstance("X509");
} }
// -trustcacerts can only be specified on -importcert.
// Reset it so that warnings on CA cert will remain for
// -printcert, etc.
if (command != IMPORTCERT) {
trustcacerts = false;
}
if (trustcacerts) { if (trustcacerts) {
caks = KeyStoreUtil.getCacertsKeyStore(); caks = KeyStoreUtil.getCacertsKeyStore();
} }
...@@ -1021,7 +1068,11 @@ public final class Main { ...@@ -1021,7 +1068,11 @@ public final class Main {
} }
} }
} else if (command == IMPORTKEYSTORE) { } else if (command == IMPORTKEYSTORE) {
doImportKeyStore(); // When not in-place import, srcKeyStore is not loaded yet.
if (srcKeyStore == null) {
srcKeyStore = loadSourceKeyStore();
}
doImportKeyStore(srcKeyStore);
kssave = true; kssave = true;
} else if (command == KEYCLONE) { } else if (command == KEYCLONE) {
keyPassNew = newPass; keyPassNew = newPass;
...@@ -1060,8 +1111,13 @@ public final class Main { ...@@ -1060,8 +1111,13 @@ public final class Main {
doChangeKeyPasswd(alias); doChangeKeyPasswd(alias);
kssave = true; kssave = true;
} else if (command == LIST) { } else if (command == LIST) {
if (storePass == null
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
printNoIntegrityWarning();
}
if (alias != null) { if (alias != null) {
doPrintEntry(alias, out, true); doPrintEntry(rb.getString("the.certificate"), alias, out);
} else { } else {
doPrintEntries(out); doPrintEntries(out);
} }
...@@ -1147,6 +1203,65 @@ public final class Main { ...@@ -1147,6 +1203,65 @@ public final class Main {
} }
} }
} }
if (isKeyStoreRelated(command)
&& !token && !nullStream && ksfname != null) {
// JKS storetype warning on the final result keystore
File f = new File(ksfname);
if (f.exists()) {
// Read the first 4 bytes to determine
// if we're dealing with JKS/JCEKS type store
String realType = keyStoreType(f);
if (realType.equalsIgnoreCase("JKS")
|| realType.equalsIgnoreCase("JCEKS")) {
boolean allCerts = true;
for (String a : Collections.list(keyStore.aliases())) {
if (!keyStore.entryInstanceOf(
a, TrustedCertificateEntry.class)) {
allCerts = false;
break;
}
}
// Don't warn for "cacerts" style keystore.
if (!allCerts) {
weakWarnings.add(String.format(
rb.getString("jks.storetype.warning"),
realType, ksfname));
}
}
if (inplaceImport) {
String realSourceStoreType =
keyStoreType(new File(inplaceBackupName));
String format =
realType.equalsIgnoreCase(realSourceStoreType) ?
rb.getString("backup.keystore.warning") :
rb.getString("migrate.keystore.warning");
weakWarnings.add(
String.format(format,
srcksfname,
realSourceStoreType,
inplaceBackupName,
realType));
}
}
}
}
private String keyStoreType(File f) throws IOException {
int MAGIC = 0xfeedfeed;
int JCEKS_MAGIC = 0xcececece;
try (DataInputStream dis = new DataInputStream(
new FileInputStream(f))) {
int xMagic = dis.readInt();
if (xMagic == MAGIC) {
return "JKS";
} else if (xMagic == JCEKS_MAGIC) {
return "JCEKS";
} else {
return "Non JKS/JCEKS";
}
}
} }
/** /**
...@@ -1158,6 +1273,12 @@ public final class Main { ...@@ -1158,6 +1273,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);
...@@ -1211,6 +1332,8 @@ public final class Main { ...@@ -1211,6 +1332,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));
...@@ -1240,6 +1363,9 @@ public final class Main { ...@@ -1240,6 +1363,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)
...@@ -1290,6 +1416,7 @@ public final class Main { ...@@ -1290,6 +1416,7 @@ public final class Main {
} else { } else {
out.write(crl.getEncodedInternal()); out.write(crl.getEncodedInternal());
} }
checkWeak(rb.getString("the.generated.crl"), crl, privateKey);
} }
/** /**
...@@ -1336,6 +1463,8 @@ public final class Main { ...@@ -1336,6 +1463,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);
} }
/** /**
...@@ -1359,7 +1488,7 @@ public final class Main { ...@@ -1359,7 +1488,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;
...@@ -1379,6 +1508,7 @@ public final class Main { ...@@ -1379,6 +1508,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);
} }
/** /**
...@@ -1640,6 +1770,7 @@ public final class Main { ...@@ -1640,6 +1770,7 @@ public final class Main {
if (keyPass == null) { if (keyPass == null) {
keyPass = promptForKeyPass(alias, null, storePass); keyPass = promptForKeyPass(alias, null, storePass);
} }
checkWeak(rb.getString("the.generated.certificate"), chain[0]);
keyStore.setKeyEntry(alias, privKey, keyPass, chain); keyStore.setKeyEntry(alias, privKey, keyPass, chain);
} }
...@@ -1722,15 +1853,9 @@ public final class Main { ...@@ -1722,15 +1853,9 @@ 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)
boolean printWarning)
throws Exception throws Exception
{ {
if (storePass == null && printWarning
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
printWarning();
}
if (keyStore.containsAlias(alias) == false) { if (keyStore.containsAlias(alias) == false) {
MessageFormat form = new MessageFormat MessageFormat form = new MessageFormat
(rb.getString("Alias.alias.does.not.exist")); (rb.getString("Alias.alias.does.not.exist"));
...@@ -1799,12 +1924,14 @@ public final class Main { ...@@ -1799,12 +1924,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.SHA1.") + (rb.getString("Certificate.fingerprint.SHA1.") +
getCertFingerPrint("SHA1", chain[0])); getCertFingerPrint("SHA1", chain[0]));
checkWeak(label, chain[0]);
} }
} }
} else if (keyStore.entryInstanceOf(alias, } else if (keyStore.entryInstanceOf(alias,
...@@ -1827,19 +1954,49 @@ public final class Main { ...@@ -1827,19 +1954,49 @@ public final class Main {
out.println(rb.getString("Certificate.fingerprint.SHA1.") out.println(rb.getString("Certificate.fingerprint.SHA1.")
+ getCertFingerPrint("SHA1", cert)); + getCertFingerPrint("SHA1", cert));
} }
checkWeak(label, cert);
} else { } else {
out.println(rb.getString("Unknown.Entry.Type")); out.println(rb.getString("Unknown.Entry.Type"));
} }
} }
boolean inplaceImportCheck() throws Exception {
if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
return false;
}
if (srcksfname != null) {
File srcksfile = new File(srcksfname);
if (srcksfile.exists() && srcksfile.length() == 0) {
throw new Exception(rb.getString
("Source.keystore.file.exists.but.is.empty.") +
srcksfname);
}
if (srcksfile.getCanonicalFile()
.equals(new File(ksfname).getCanonicalFile())) {
return true;
} else {
// Informational, especially if destkeystore is not
// provided, which default to ~/.keystore.
System.err.println(String.format(rb.getString(
"importing.keystore.status"), srcksfname, ksfname));
return false;
}
} else {
throw new Exception(rb.getString
("Please.specify.srckeystore"));
}
}
/** /**
* Load the srckeystore from a stream, used in -importkeystore * Load the srckeystore from a stream, used in -importkeystore
* @returns the src KeyStore * @returns the src KeyStore
*/ */
KeyStore loadSourceKeyStore() throws Exception { KeyStore loadSourceKeyStore() throws Exception {
boolean isPkcs11 = false;
InputStream is = null; InputStream is = null;
File srcksfile = null;
if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) || if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) ||
KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { KeyStoreUtil.isWindowsKeyStore(srcstoretype)) {
...@@ -1849,20 +2006,9 @@ public final class Main { ...@@ -1849,20 +2006,9 @@ public final class Main {
System.err.println(); System.err.println();
tinyHelp(); tinyHelp();
} }
isPkcs11 = true;
} else { } else {
if (srcksfname != null) { srcksfile = new File(srcksfname);
File srcksfile = new File(srcksfname);
if (srcksfile.exists() && srcksfile.length() == 0) {
throw new Exception(rb.getString
("Source.keystore.file.exists.but.is.empty.") +
srcksfname);
}
is = new FileInputStream(srcksfile); is = new FileInputStream(srcksfile);
} else {
throw new Exception(rb.getString
("Please.specify.srckeystore"));
}
} }
KeyStore store; KeyStore store;
...@@ -1903,7 +2049,7 @@ public final class Main { ...@@ -1903,7 +2049,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
...@@ -1923,17 +2069,32 @@ public final class Main { ...@@ -1923,17 +2069,32 @@ public final class Main {
* keep alias unchanged if no name conflict, otherwise, prompt. * keep alias unchanged if no name conflict, otherwise, prompt.
* keep keypass unchanged for keys * keep keypass unchanged for keys
*/ */
private void doImportKeyStore() throws Exception { private void doImportKeyStore(KeyStore srcKS) throws Exception {
if (alias != null) { if (alias != null) {
doImportKeyStoreSingle(loadSourceKeyStore(), alias); doImportKeyStoreSingle(srcKS, alias);
} else { } else {
if (dest != null || srckeyPass != null) { if (dest != null || srckeyPass != null) {
throw new Exception(rb.getString( throw new Exception(rb.getString(
"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified")); "if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified"));
} }
doImportKeyStoreAll(loadSourceKeyStore()); doImportKeyStoreAll(srcKS);
}
if (inplaceImport) {
// Backup to file.old or file.old2...
// The keystore is not rewritten yet now.
for (int n = 1; /* forever */; n++) {
inplaceBackupName = srcksfname + ".old" + (n == 1 ? "" : n);
File bkFile = new File(inplaceBackupName);
if (!bkFile.exists()) {
Files.copy(Paths.get(srcksfname), bkFile.toPath());
break;
}
}
} }
/* /*
* Information display rule of -importkeystore * Information display rule of -importkeystore
* 1. inside single, shows failure * 1. inside single, shows failure
...@@ -1994,6 +2155,10 @@ public final class Main { ...@@ -1994,6 +2155,10 @@ public final class Main {
} }
try { try {
Certificate c = srckeystore.getCertificate(alias);
if (c != null) {
checkWeak("<" + newAlias + ">", c);
}
keyStore.setEntry(newAlias, entry, pp); keyStore.setEntry(newAlias, entry, pp);
// Place the check so that only successful imports are blocked. // Place the check so that only successful imports are blocked.
// For example, we don't block a failed SecretEntry import. // For example, we don't block a failed SecretEntry import.
...@@ -2047,13 +2212,6 @@ public final class Main { ...@@ -2047,13 +2212,6 @@ public final class Main {
private void doPrintEntries(PrintStream out) private void doPrintEntries(PrintStream out)
throws Exception throws Exception
{ {
if (storePass == null
&& !KeyStoreUtil.isWindowsKeyStore(storetype)) {
printWarning();
} else {
out.println();
}
out.println(rb.getString("Keystore.type.") + keyStore.getType()); out.println(rb.getString("Keystore.type.") + keyStore.getType());
out.println(rb.getString("Keystore.provider.") + out.println(rb.getString("Keystore.provider.") +
keyStore.getProvider().getName()); keyStore.getProvider().getName());
...@@ -2072,7 +2230,7 @@ public final class Main { ...@@ -2072,7 +2230,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, false); 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
...@@ -2222,19 +2380,28 @@ public final class Main { ...@@ -2222,19 +2380,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();
} }
} }
...@@ -2246,18 +2413,26 @@ public final class Main { ...@@ -2246,18 +2413,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 {
if (rfc) {
X509CRL xcrl = (X509CRL)crl; X509CRL xcrl = (X509CRL)crl;
if (rfc) {
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);
} }
} }
...@@ -2284,8 +2459,11 @@ public final class Main { ...@@ -2284,8 +2459,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((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) { if (oid.equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) {
...@@ -2308,6 +2486,7 @@ public final class Main { ...@@ -2308,6 +2486,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);
} }
/** /**
...@@ -2347,6 +2526,15 @@ public final class Main { ...@@ -2347,6 +2526,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);
} }
} }
...@@ -2376,7 +2564,11 @@ public final class Main { ...@@ -2376,7 +2564,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");
...@@ -2385,12 +2577,15 @@ public final class Main { ...@@ -2385,12 +2577,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");
...@@ -2399,6 +2594,7 @@ public final class Main { ...@@ -2399,6 +2594,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);
} }
} }
} }
...@@ -2443,6 +2639,7 @@ public final class Main { ...@@ -2443,6 +2639,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();
...@@ -2618,7 +2815,7 @@ public final class Main { ...@@ -2618,7 +2815,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,
...@@ -2655,6 +2852,12 @@ public final class Main { ...@@ -2655,6 +2852,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) {
checkWeak(rb.getString("the.input"), cert);
keyStore.setCertificateEntry(alias, 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 (isSelfSigned(cert)) { if (isSelfSigned(cert)) {
...@@ -2662,11 +2865,6 @@ public final class Main { ...@@ -2662,11 +2865,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);
...@@ -2675,6 +2873,8 @@ public final class Main { ...@@ -2675,6 +2873,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) {
...@@ -2684,6 +2884,8 @@ public final class Main { ...@@ -2684,6 +2884,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."));
} }
...@@ -2691,6 +2893,8 @@ public final class Main { ...@@ -2691,6 +2893,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."));
} }
...@@ -2704,6 +2908,7 @@ public final class Main { ...@@ -2704,6 +2908,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);
...@@ -2715,6 +2920,8 @@ public final class Main { ...@@ -2715,6 +2920,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)) {
...@@ -2853,6 +3060,24 @@ public final class Main { ...@@ -2853,6 +3060,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.
*/ */
...@@ -2878,7 +3103,13 @@ public final class Main { ...@@ -2878,7 +3103,13 @@ 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();
String sigName = cert.getSigAlgName();
// No need to warn about sigalg of a trust anchor
if (!isTrustedCert(cert)) {
sigName = withWeak(sigName);
}
Object[] source = {cert.getSubjectDN().toString(), Object[] source = {cert.getSubjectDN().toString(),
cert.getIssuerDN().toString(), cert.getIssuerDN().toString(),
cert.getSerialNumber().toString(16), cert.getSerialNumber().toString(16),
...@@ -2887,7 +3118,8 @@ public final class Main { ...@@ -2887,7 +3118,8 @@ public final class Main {
getCertFingerPrint("MD5", cert), getCertFingerPrint("MD5", cert),
getCertFingerPrint("SHA1", cert), getCertFingerPrint("SHA1", cert),
getCertFingerPrint("SHA-256", cert), getCertFingerPrint("SHA-256", cert),
cert.getSigAlgName(), sigName,
withWeak(pkey),
cert.getVersion() cert.getVersion()
}; };
out.println(form.format(source)); out.println(form.format(source));
...@@ -2957,12 +3189,12 @@ public final class Main { ...@@ -2957,12 +3189,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 { getSigner(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(); ) {
...@@ -2971,7 +3203,7 @@ public final class Main { ...@@ -2971,7 +3203,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
} }
...@@ -3235,7 +3467,7 @@ public final class Main { ...@@ -3235,7 +3467,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."));
...@@ -3260,6 +3492,9 @@ public final class Main { ...@@ -3260,6 +3492,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
...@@ -3307,9 +3542,11 @@ public final class Main { ...@@ -3307,9 +3542,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 = getSigner(topCert, keyStore);
if (root == null && trustcacerts && caks != null) { if (root == null && trustcacerts && caks != null) {
root = getTrustedSigner(topCert, caks); root = getSigner(topCert, caks);
fromKeyStore = false;
} }
if (root == null) { if (root == null) {
System.err.println(); System.err.println();
...@@ -3318,33 +3555,42 @@ public final class Main { ...@@ -3318,33 +3555,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)
...@@ -3372,30 +3618,37 @@ public final class Main { ...@@ -3372,30 +3618,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;
...@@ -3406,7 +3659,17 @@ public final class Main { ...@@ -3406,7 +3659,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.
...@@ -3414,19 +3677,20 @@ public final class Main { ...@@ -3414,19 +3677,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 (isSelfSigned(certToVerify.snd)) {
if (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;
} }
...@@ -3434,13 +3698,12 @@ public final class Main { ...@@ -3434,13 +3698,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;
} }
...@@ -3489,10 +3752,11 @@ public final class Main { ...@@ -3489,10 +3752,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();
...@@ -3501,13 +3765,20 @@ public final class Main { ...@@ -3501,13 +3765,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);
...@@ -4080,6 +4351,81 @@ public final class Main { ...@@ -4080,6 +4351,81 @@ public final class Main {
return ext; return ext;
} }
private boolean isTrustedCert(Certificate cert) throws KeyStoreException {
if (caks != null && caks.getCertificateAlias(cert) != null) {
return true;
} else {
String inKS = keyStore.getCertificateAlias(cert);
return inKS != null && keyStore.isCertificateEntry(inKS);
}
}
private void checkWeak(String label, String sigAlg, Key key) {
if (sigAlg != null && !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)
throws KeyStoreException {
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);
}
}
}
private void checkWeak(String label, Certificate cert)
throws KeyStoreException {
if (cert instanceof X509Certificate) {
X509Certificate xc = (X509Certificate)cert;
// No need to check the sigalg of a trust anchor
String sigAlg = isTrustedCert(cert) ? null : xc.getSigAlgName();
checkWeak(label, sigAlg, 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.
*/ */
......
...@@ -345,8 +345,6 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -345,8 +345,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 MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t Signature algorithm name: {8}\n\t Version: {9}"},
{"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.",
...@@ -413,16 +411,12 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -413,16 +411,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: "},
...@@ -431,7 +425,38 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -431,7 +425,38 @@ 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 MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\nSignature algorithm name: {8}\nSubject Public Key Algorithm: {9}\nVersion: {10}"},
{"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."},
{"jks.storetype.warning", "The %1$s keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using \"keytool -importkeystore -srckeystore %2$s -destkeystore %2$s -deststoretype pkcs12\"."},
{"migrate.keystore.warning", "Migrated \"%1$s\" to %4$s. The %2$s keystore is backed up as \"%3$s\"."},
{"backup.keystore.warning", "The original keystore \"%1$s\" is backed up as \"%3$s\"..."},
{"importing.keystore.status", "Importing keystore %1$s to %2$s..."},
}; };
......
...@@ -536,10 +536,15 @@ public class X509CRLImpl extends X509CRL implements DerEncoder { ...@@ -536,10 +536,15 @@ 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) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append("X.509 CRL v" + (version+1) + "\n"); sb.append("X.509 CRL v" + (version+1) + "\n");
if (sigAlgId != null) if (sigAlgId != null)
sb.append("Signature Algorithm: " + sigAlgId.toString() + sb.append("Signature Algorithm: " + name.toString() +
", OID=" + (sigAlgId.getOID()).toString() + "\n"); ", OID=" + (sigAlgId.getOID()).toString() + "\n");
if (issuer != null) if (issuer != null)
sb.append("Issuer: " + issuer.toString() + "\n"); sb.append("Issuer: " + issuer.toString() + "\n");
......
/*
* 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 8177569 8182879
* @summary keytool should print out warnings when reading or generating
* cert/cert req using weak algorithms
* @library /lib/testlibrary
* @run main/othervm/timeout=600 -Duser.language=en -Duser.country=US WeakAlg
*/
import jdk.testlibrary.Asserts;
import jdk.testlibrary.SecurityTools;
import jdk.testlibrary.OutputAnalyzer;
import sun.security.tools.KeyStoreUtil;
import sun.security.util.DisabledAlgorithmConstraints;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
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 {
static String sep = File.separator;
static String cacerts_location = System.getProperty("java.home") +
sep + "lib" + sep + "security" + sep + "cacerts";
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");
jksTypeCheck();
checkInplaceImportKeyStore();
}
static void jksTypeCheck() throws Exception {
rm("ks");
rm("ks2");
kt("-genkeypair -alias a -storetype pkcs12 -dname CN=A")
.shouldNotContain("Warning:");
kt("-list")
.shouldNotContain("Warning:");
kt("-list -storetype jks") // no warning if PKCS12 used as JKS
.shouldNotContain("Warning:");
kt("-exportcert -alias a -file a.crt")
.shouldNotContain("Warning:");
// warn if migrating to JKS
importkeystore("ks", "ks2", "-deststoretype jks")
.shouldContain("JKS keystore uses a proprietary format");
rm("ks");
rm("ks2");
rm("ks3");
// no warning if all certs
kt("-importcert -alias b -file a.crt -storetype jks -noprompt")
.shouldNotContain("Warning:");
kt("-genkeypair -alias a -dname CN=A")
.shouldContain("JKS keystore uses a proprietary format");
kt("-list")
.shouldContain("JKS keystore uses a proprietary format");
kt("-exportcert -alias a -file a.crt")
.shouldContain("JKS keystore uses a proprietary format");
kt("-printcert -file a.crt") // no warning if keystore not touched
.shouldNotContain("Warning:");
kt("-certreq -alias a -file a.req")
.shouldContain("JKS keystore uses a proprietary format");
kt("-printcertreq -file a.req") // no warning if keystore not touched
.shouldNotContain("Warning:");
// Earlier than JDK 9 defaults to JKS
importkeystore("ks", "ks2", "")
.shouldContain("Warning:");
importkeystore("ks", "ks3", "-deststoretype pkcs12")
.shouldNotContain("Warning:");
rm("ks");
kt("-genkeypair -alias a -dname CN=A -storetype jceks")
.shouldContain("JCEKS keystore uses a proprietary format");
kt("-list -storetype jceks")
.shouldContain("JCEKS keystore uses a proprietary format");
kt("-importcert -alias b -file a.crt -noprompt -storetype jceks")
.shouldContain("JCEKS keystore uses a proprietary format");
kt("-exportcert -alias a -file a.crt -storetype jceks")
.shouldContain("JCEKS keystore uses a proprietary format");
kt("-printcert -file a.crt")
.shouldNotContain("Warning:");
kt("-certreq -alias a -file a.req -storetype jceks")
.shouldContain("JCEKS keystore uses a proprietary format");
kt("-printcertreq -file a.req")
.shouldNotContain("Warning:");
kt("-genseckey -alias c -keyalg AES -keysize 128 -storetype jceks")
.shouldContain("JCEKS keystore uses a proprietary format");
}
static void checkImportKeyStore() throws Exception {
rm("ks2");
rm("ks3");
importkeystore("ks", "ks2", "")
.shouldContain("3 entries successfully imported")
.shouldContain("Warning")
.shouldMatch("<b>.*512-bit RSA key.*risk")
.shouldMatch("<a>.*MD5withRSA.*risk");
importkeystore("ks", "ks3", "-srcalias a")
.shouldContain("Warning")
.shouldMatch("<a>.*MD5withRSA.*risk");
}
static void checkInplaceImportKeyStore() throws Exception {
rm("ks");
genkeypair("a", "");
// Same type backup
importkeystore("ks", "ks", "")
.shouldContain("Warning:")
.shouldMatch(".*ks.old");
importkeystore("ks", "ks", "")
.shouldContain("Warning:")
.shouldMatch(".*ks.old2");
importkeystore("ks", "ks", "-srcstoretype jks") // it knows real type
.shouldContain("Warning:")
.shouldMatch(".*ks.old3");
String cPath = new File("ks").getCanonicalPath();
importkeystore("ks", cPath, "")
.shouldContain("Warning:")
.shouldMatch(".*ks.old4");
// Migration
importkeystore("ks", "ks", "-deststoretype jks")
.shouldContain("Warning:")
.shouldContain("JKS keystore uses a proprietary format")
.shouldMatch("The original.*ks.old5");
KeyStore test_ks = KeyStore.getInstance("JKS");
test_ks.load(new FileInputStream(new File("ks")),
"changeit".toCharArray());
Asserts.assertEQ(
test_ks.getType(), "JKS");
importkeystore("ks", "ks", "-deststoretype PKCS12")
.shouldContain("Warning:")
.shouldNotContain("proprietary format")
.shouldMatch("Migrated.*Non.*JKS.*ks.old6");
test_ks = KeyStore.getInstance("PKCS12");
test_ks.load(new FileInputStream(new File("ks")),
"changeit".toCharArray());
Asserts.assertEQ(
test_ks.getType(), "PKCS12");
test_ks = KeyStore.getInstance("JKS");
test_ks.load(new FileInputStream(new File("ks.old6")),
"changeit".toCharArray());
Asserts.assertEQ(
test_ks.getType(), "JKS");
// One password prompt is enough for migration
kt0("-importkeystore -srckeystore ks -destkeystore ks", "changeit")
.shouldMatch("backed.*ks.old7");
// But three if importing to a different keystore
rm("ks2");
kt0("-importkeystore -srckeystore ks -destkeystore ks2",
"changeit")
.shouldContain("Keystore password is too short");
kt0("-importkeystore -srckeystore ks -destkeystore ks2",
"changeit", "changeit", "changeit")
.shouldContain("Importing keystore ks to ks2...")
.shouldNotContain("original")
.shouldNotContain("Migrated");
}
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]");
// JDK-8177569: no warning for sigalg of trusted cert
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) {
// The following 2 commands still have a warning on why not using
// the -cacerts option directly.
kt("-list -keystore " + cacerts_location)
.shouldNotContain("risk");
kt("-list -v -keystore " + cacerts_location)
.shouldNotContain("risk");
// -printcert will always show warnings
kt("-printcert -file ca.cert")
.shouldContain("name: " + weakSigAlgCA + " (weak)")
.shouldContain("Warning")
.shouldMatch("The certificate.*" + weakSigAlgCA + ".*risk");
kt("-printcert -file ca.cert -trustcacerts") // -trustcacerts useless
.shouldContain("name: " + weakSigAlgCA + " (weak)")
.shouldContain("Warning")
.shouldMatch("The certificate.*" + weakSigAlgCA + ".*risk");
// Importing with -trustcacerts ignore CA cert's sig alg
kt("-delete -alias d");
kt("-importcert -alias d -trustcacerts -file ca.cert", "no")
.shouldContain("Certificate already exists in system-wide CA")
.shouldNotContain("risk")
.shouldContain("Do you still want to add it to your own keystore?");
kt("-importcert -alias d -trustcacerts -file ca.cert -noprompt")
.shouldNotContain("risk")
.shouldNotContain("[no]");
// but not without -trustcacerts
kt("-delete -alias d");
kt("-importcert -alias d -file ca.cert", "no")
.shouldContain("name: " + weakSigAlgCA + " (weak)")
.shouldContain("Warning")
.shouldMatch("The input.*" + weakSigAlgCA + ".*risk")
.shouldContain("Trust this certificate?");
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();
certreq("c", "");
gencert("a-c", "");
kt("-importcert -alias c -file a-c.cert")
.shouldContain("Warning")
.shouldMatch("Issuer <a>.*MD5withRSA.*risk");
// JDK-8177569: no warning for sigalg of trusted cert
reStore();
// Change a into a TrustedCertEntry
kt("-exportcert -alias a -file a.cert");
kt("-delete -alias a");
kt("-importcert -alias a -file a.cert -noprompt");
kt("-list -alias a -v")
.shouldNotContain("weak")
.shouldNotContain("Warning");
// This time a is trusted and no warning on its weak sig alg
kt("-importcert -alias c -file a-c.cert")
.shouldNotContain("Warning");
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);
}
}
static OutputAnalyzer kt(String cmd, String... input) {
return kt0("-keystore ks -storepass changeit " +
"-keypass changeit " + cmd, input);
}
// Fast keytool execution by directly calling its main() method
static OutputAnalyzer kt0(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;
String sout;
String serr;
try {
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);
sout = new String(bout.toByteArray());
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 importkeystore(String src, String dest,
String options) {
return kt0("-importkeystore "
+ "-srckeystore " + src + " -destkeystore " + dest
+ " -srcstorepass changeit -deststorepass changeit " + options);
}
static OutputAnalyzer genkeypair(String alias, String options) {
return kt("-genkeypair -alias " + alias + " -dname CN=" + alias
+ " -keyalg RSA -storetype PKCS12 " + 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));
}
}
#
# 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 8029659
# @summary Keytool, print key algorithm of certificate or key entry
#
if [ "${TESTJAVA}" = "" ] ; then
JAVAC_CMD=`which javac`
TESTJAVA=`dirname $JAVAC_CMD`/..
fi
TESTTOOLVMOPTS="$TESTTOOLVMOPTS -J-Duser.language=en -J-Duser.country=US"
KS=ks
KEYTOOL="$TESTJAVA/bin/keytool ${TESTTOOLVMOPTS} -keystore ks -storepass changeit -keypass changeit"
rm $KS 2> /dev/null
$KEYTOOL -genkeypair -alias ca -dname CN=CA -keyalg EC || exit 1
$KEYTOOL -genkeypair -alias user -dname CN=User -keyalg RSA -keysize 1024 || exit 2
$KEYTOOL -certreq -alias user |
$KEYTOOL -gencert -alias ca -rfc -sigalg SHA1withECDSA |
$KEYTOOL -printcert > user.dump || exit 3
cat user.dump | grep "Signature algorithm name:" | grep SHA1withECDSA || exit 4
cat user.dump | grep "Subject Public Key Algorithm:" | grep RSA | grep 1024 || exit 5
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册