diff --git a/src/share/classes/sun/security/tools/jarsigner/Main.java b/src/share/classes/sun/security/tools/jarsigner/Main.java index 57dc512e64606d4fdf2a757feb82713080083c27..205f72e4a31a8fbaddeeab6cc874716bc6660705 100644 --- a/src/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/share/classes/sun/security/tools/jarsigner/Main.java @@ -29,6 +29,7 @@ import java.io.*; import java.security.cert.CertPathValidatorException; import java.security.cert.PKIXBuilderParameters; import java.util.*; +import java.util.stream.Collectors; import java.util.zip.*; import java.util.jar.*; import java.math.BigInteger; @@ -101,6 +102,7 @@ public class Main { private static final String P11KEYSTORE = "PKCS11"; private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds + private static final long ONE_YEAR = 366*24*60*60*1000L; private static final DisabledAlgorithmConstraints DISABLED_CHECK = new DisabledAlgorithmConstraints( @@ -111,21 +113,21 @@ public class Main { private static final Set SIG_PRIMITIVE_SET = Collections .unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); - // Attention: - // This is the entry that get launched by the security tool jarsigner. - public static void main(String args[]) throws Exception { - Main js = new Main(); - js.run(args); - } - static final String VERSION = "1.0"; static final int IN_KEYSTORE = 0x01; // signer is in keystore static final int IN_SCOPE = 0x02; static final int NOT_ALIAS = 0x04; // alias list is NOT empty and - // signer is not in alias list + // signer is not in alias list static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list + // Attention: + // This is the entry that get launched by the security tool jarsigner. + public static void main(String args[]) throws Exception { + Main js = new Main(); + js.run(args); + } + X509Certificate[] certChain; // signer's cert chain (when composing) PrivateKey privateKey; // private key KeyStore store; // the keystore specified by -keystore @@ -172,8 +174,16 @@ public class Main { // Informational warnings private boolean hasExpiringCert = false; - private boolean noTimestamp = false; - private Date expireDate = new Date(0L); // used in noTimestamp warning + private boolean hasExpiringTsaCert = false; + private boolean noTimestamp = true; + + // Expiration date. The value could be null if signed by a trusted cert. + private Date expireDate = null; + private Date tsaExpireDate = null; + + // If there is a time stamp block inside the PKCS7 block file + boolean hasTimestampBlock = false; + // Severe warnings. @@ -186,6 +196,7 @@ public class Main { private int weakAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg private boolean hasExpiredCert = false; + private boolean hasExpiredTsaCert = false; private boolean notYetValidCert = false; private boolean chainNotValidated = false; private boolean tsaChainNotValidated = false; @@ -203,6 +214,7 @@ public class Main { private boolean seeWeak = false; PKIXBuilderParameters pkixParameters; + Set trustedCerts = new HashSet<>(); public void run(String args[]) { try { @@ -289,8 +301,8 @@ public class Main { if (strict) { int exitCode = 0; - if (weakAlg != 0 || chainNotValidated - || hasExpiredCert || notYetValidCert || signerSelfSigned) { + if (weakAlg != 0 || chainNotValidated || hasExpiredCert + || hasExpiredTsaCert || notYetValidCert || signerSelfSigned) { exitCode |= 4; } if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) { @@ -825,9 +837,6 @@ public class Main { System.out.println(rb.getString("no.manifest.")); } - // If there is a time stamp block inside the PKCS7 block file - boolean hasTimestampBlock = false; - // Even if the verbose option is not specified, all out strings // must be generated so seeWeak can be updated. if (!digestMap.isEmpty() @@ -913,8 +922,9 @@ public class Main { System.out.println(); // If signer is a trusted cert or private entry in user's own - // keystore, it can be self-signed. - if (!aliasNotInStore) { + // keystore, it can be self-signed. Please note aliasNotInStore + // is always false when ~/.keystore is used. + if (!aliasNotInStore && keystore != null) { signerSelfSigned = false; } @@ -934,130 +944,245 @@ public class Main { System.out.println(rb.getString("jar.is.unsigned")); } } else { - boolean warningAppeared = false; - boolean errorAppeared = false; - if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || - notYetValidCert || chainNotValidated || hasExpiredCert || - hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) || - aliasNotInStore || notSignedByAlias || tsaChainNotValidated) { - - if (strict) { - System.out.println(rb.getString("jar.verified.with.signer.errors.")); - System.out.println(); - System.out.println(rb.getString("Error.")); - errorAppeared = true; - } else { - System.out.println(rb.getString("jar.verified.")); - System.out.println(); - System.out.println(rb.getString("Warning.")); - warningAppeared = true; - } + displayMessagesAndResult(false); + } + return; + } catch (Exception e) { + System.out.println(rb.getString("jarsigner.") + e); + if (debug) { + e.printStackTrace(); + } + } finally { // close the resource + if (jf != null) { + jf.close(); + } + } - if (weakAlg != 0) { - // In fact, jarsigner verification did not catch this - // since it has not read the JarFile content itself. - // Everything is done with JarFile API. - } + System.exit(1); + } - if (badKeyUsage) { - System.out.println( - rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); - } + private void displayMessagesAndResult(boolean isSigning) { + String result; + List errors = new ArrayList<>(); + List warnings = new ArrayList<>(); + List info = new ArrayList<>(); + + boolean signerNotExpired = expireDate == null + || expireDate.after(new Date()); + + if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || + notYetValidCert || chainNotValidated || hasExpiredCert || + hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) || + aliasNotInStore || notSignedByAlias || + tsaChainNotValidated || + (hasExpiredTsaCert && !signerNotExpired)) { + + if (strict) { + result = rb.getString(isSigning + ? "jar.signed.with.signer.errors." + : "jar.verified.with.signer.errors."); + } else { + result = rb.getString(isSigning + ? "jar.signed." + : "jar.verified."); + } - if (badExtendedKeyUsage) { - System.out.println( - rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); - } + if (badKeyUsage) { + errors.add(rb.getString(isSigning + ? "The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing." + : "This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); + } - if (badNetscapeCertType) { - System.out.println( - rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); - } + if (badExtendedKeyUsage) { + errors.add(rb.getString(isSigning + ? "The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing." + : "This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); + } - if (hasUnsignedEntry) { - System.out.println(rb.getString( - "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.")); - } - if (hasExpiredCert) { - System.out.println(rb.getString( - "This.jar.contains.entries.whose.signer.certificate.has.expired.")); - } - if (notYetValidCert) { - System.out.println(rb.getString( - "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.")); - } + if (badNetscapeCertType) { + errors.add(rb.getString(isSigning + ? "The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing." + : "This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); + } - if (chainNotValidated) { - System.out.println(String.format( - rb.getString("This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"), - chainNotValidatedReason.getLocalizedMessage())); - } + // only in verifying + if (hasUnsignedEntry) { + errors.add(rb.getString( + "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.")); + } + if (hasExpiredCert) { + errors.add(rb.getString(isSigning + ? "The.signer.certificate.has.expired." + : "This.jar.contains.entries.whose.signer.certificate.has.expired.")); + } + if (notYetValidCert) { + errors.add(rb.getString(isSigning + ? "The.signer.certificate.is.not.yet.valid." + : "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.")); + } - if (tsaChainNotValidated) { - System.out.println(String.format( - rb.getString("This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"), - tsaChainNotValidatedReason.getLocalizedMessage())); - } + if (chainNotValidated) { + errors.add(String.format(rb.getString(isSigning + ? "The.signer.s.certificate.chain.is.invalid.reason.1" + : "This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"), + chainNotValidatedReason.getLocalizedMessage())); + } - if (notSignedByAlias) { - System.out.println( - rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.")); - } + if (hasExpiredTsaCert) { + errors.add(rb.getString("The.timestamp.has.expired.")); + } + if (tsaChainNotValidated) { + errors.add(String.format(rb.getString(isSigning + ? "The.tsa.certificate.chain.is.invalid.reason.1" + : "This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"), + tsaChainNotValidatedReason.getLocalizedMessage())); + } - if (aliasNotInStore) { - System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.")); - } + // only in verifying + if (notSignedByAlias) { + errors.add( + rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.")); + } - if (signerSelfSigned) { - System.out.println(rb.getString( - "This.jar.contains.entries.whose.signer.certificate.is.self.signed.")); - } - } else { - System.out.println(rb.getString("jar.verified.")); + // only in verifying + if (aliasNotInStore) { + errors.add(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.")); + } + + if (signerSelfSigned) { + errors.add(rb.getString(isSigning + ? "The.signer.s.certificate.is.self.signed." + : "This.jar.contains.entries.whose.signer.certificate.is.self.signed.")); + } + + // weakAlg only detected in signing. The jar file is + // now simply treated unsigned in verifying. + if ((weakAlg & 1) == 1) { + errors.add(String.format( + rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), + digestalg, "-digestalg")); + } + + if ((weakAlg & 2) == 2) { + errors.add(String.format( + rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), + sigalg, "-sigalg")); + } + if ((weakAlg & 4) == 4) { + errors.add(String.format( + rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), + tSADigestAlg, "-tsadigestalg")); + } + if ((weakAlg & 8) == 8) { + errors.add(String.format( + rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."), + privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey))); + } + } else { + result = rb.getString(isSigning ? "jar.signed." : "jar.verified."); + } + + if (hasExpiredTsaCert) { + // No need to warn about expiring if already expired + hasExpiringTsaCert = false; + } + + if (hasExpiringCert || + (hasExpiringTsaCert && expireDate != null) || + (noTimestamp && expireDate != null) || + (hasExpiredTsaCert && signerNotExpired)) { + + if (hasExpiredTsaCert && signerNotExpired) { + if (expireDate != null) { + warnings.add(String.format( + rb.getString("The.timestamp.expired.1.but.usable.2"), + tsaExpireDate, + expireDate)); } - if (hasExpiringCert || noTimestamp) { - if (!warningAppeared) { - System.out.println(); - System.out.println(rb.getString("Warning.")); - warningAppeared = true; - } - if (hasExpiringCert) { - System.out.println(rb.getString( - "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.")); - } - if (noTimestamp) { - if (hasTimestampBlock) { - // JarSigner API has not seen the timestamp, - // might have ignored it due to weak alg, etc. - System.out.println( - String.format(rb.getString("bad.timestamp.verifying"), expireDate)); - } else { - System.out.println( - String.format(rb.getString("no.timestamp.verifying"), expireDate)); - } - } + // Reset the flag so exit code is 0 + hasExpiredTsaCert = false; + } + if (hasExpiringCert) { + warnings.add(rb.getString(isSigning + ? "The.signer.certificate.will.expire.within.six.months." + : "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.")); + } + if (hasExpiringTsaCert && expireDate != null) { + if (expireDate.after(tsaExpireDate)) { + warnings.add(String.format(rb.getString( + "The.timestamp.will.expire.within.one.year.on.1.but.2"), tsaExpireDate, expireDate)); + } else { + warnings.add(String.format(rb.getString( + "The.timestamp.will.expire.within.one.year.on.1"), tsaExpireDate)); } - if (warningAppeared || errorAppeared) { - if (! (verbose != null && showcerts)) { - System.out.println(); - System.out.println(rb.getString( - "Re.run.with.the.verbose.and.certs.options.for.more.details.")); - } + } + if (noTimestamp && expireDate != null) { + if (hasTimestampBlock) { + warnings.add(String.format(rb.getString(isSigning + ? "invalid.timestamp.signing" + : "bad.timestamp.verifying"), expireDate)); + } else { + warnings.add(String.format(rb.getString(isSigning + ? "no.timestamp.signing" + : "no.timestamp.verifying"), expireDate)); } } - return; - } catch (Exception e) { - System.out.println(rb.getString("jarsigner.") + e); - if (debug) { - e.printStackTrace(); + } + + System.out.println(result); + if (strict) { + if (!errors.isEmpty()) { + System.out.println(); + System.out.println(rb.getString("Error.")); + errors.forEach(System.out::println); } - } finally { // close the resource - if (jf != null) { - jf.close(); + if (!warnings.isEmpty()) { + System.out.println(); + System.out.println(rb.getString("Warning.")); + warnings.forEach(System.out::println); + } + } else { + if (!errors.isEmpty() || !warnings.isEmpty()) { + System.out.println(); + System.out.println(rb.getString("Warning.")); + errors.forEach(System.out::println); + warnings.forEach(System.out::println); + } + } + if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) { + if (! (verbose != null && showcerts)) { + System.out.println(); + System.out.println(rb.getString( + "Re.run.with.the.verbose.and.certs.options.for.more.details.")); } } - System.exit(1); + if (isSigning || verbose != null) { + // Always print out expireDate, unless expired or expiring. + if (!hasExpiringCert && !hasExpiredCert + && expireDate != null && signerNotExpired) { + info.add(String.format(rb.getString( + "The.signer.certificate.will.expire.on.1."), expireDate)); + } + if (!noTimestamp) { + if (!hasExpiringTsaCert && !hasExpiredTsaCert && tsaExpireDate != null) { + if (signerNotExpired) { + info.add(String.format(rb.getString( + "The.timestamp.will.expire.on.1."), tsaExpireDate)); + } else { + info.add(String.format(rb.getString( + "signer.cert.expired.1.but.timestamp.good.2."), + expireDate, + tsaExpireDate)); + } + } + } + } + + if (!info.isEmpty()) { + System.out.println(); + info.forEach(System.out::println); + } } private String withWeak(String alg, Set primitiveSet) { @@ -1094,8 +1219,9 @@ public class Main { * * Note: no newline character at the end. * - * When isTsCert is true, this method sets global flags like hasExpiredCert, - * notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType. + * This method sets global flags like hasExpiringCert, hasExpiredCert, + * notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType, + * hasExpiringTsaCert, hasExpiredTsaCert. * * @param isTsCert true if c is in the TSA cert chain, false otherwise. * @param checkUsage true to check code signer keyUsage @@ -1124,55 +1250,75 @@ public class Main { if (x509Cert != null) { certStr.append("\n").append(tab).append("["); - Date notAfter = x509Cert.getNotAfter(); - try { - boolean printValidity = true; - if (timestamp == null) { - if (expireDate.getTime() == 0 || expireDate.after(notAfter)) { - expireDate = notAfter; + + if (trustedCerts.contains(x509Cert)) { + certStr.append(rb.getString("trusted.certificate")); + } else { + Date notAfter = x509Cert.getNotAfter(); + try { + boolean printValidity = true; + if (isTsCert) { + if (tsaExpireDate == null || tsaExpireDate.after(notAfter)) { + tsaExpireDate = notAfter; + } + } else { + if (expireDate == null || expireDate.after(notAfter)) { + expireDate = notAfter; + } } - x509Cert.checkValidity(); - // test if cert will expire within six months - if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) { - if (!isTsCert) hasExpiringCert = true; - if (expiringTimeForm == null) { - expiringTimeForm = new MessageFormat( - rb.getString("certificate.will.expire.on")); + if (timestamp == null) { + x509Cert.checkValidity(); + // test if cert will expire within six months (or one year for tsa) + long age = isTsCert ? ONE_YEAR : SIX_MONTHS; + if (notAfter.getTime() < System.currentTimeMillis() + age) { + if (isTsCert) { + hasExpiringTsaCert = true; + } else { + hasExpiringCert = true; + } + if (expiringTimeForm == null) { + expiringTimeForm = new MessageFormat( + rb.getString("certificate.will.expire.on")); + } + Object[] source = {notAfter}; + certStr.append(expiringTimeForm.format(source)); + printValidity = false; } - Object[] source = { notAfter }; - certStr.append(expiringTimeForm.format(source)); - printValidity = false; + } else { + x509Cert.checkValidity(timestamp); } - } else { - x509Cert.checkValidity(timestamp); - } - if (printValidity) { - if (validityTimeForm == null) { - validityTimeForm = new MessageFormat( - rb.getString("certificate.is.valid.from")); + if (printValidity) { + if (validityTimeForm == null) { + validityTimeForm = new MessageFormat( + rb.getString("certificate.is.valid.from")); + } + Object[] source = {x509Cert.getNotBefore(), notAfter}; + certStr.append(validityTimeForm.format(source)); + } + } catch (CertificateExpiredException cee) { + if (isTsCert) { + hasExpiredTsaCert = true; + } else { + hasExpiredCert = true; } - Object[] source = { x509Cert.getNotBefore(), notAfter }; - certStr.append(validityTimeForm.format(source)); - } - } catch (CertificateExpiredException cee) { - if (!isTsCert) hasExpiredCert = true; - if (expiredTimeForm == null) { - expiredTimeForm = new MessageFormat( - rb.getString("certificate.expired.on")); - } - Object[] source = { notAfter }; - certStr.append(expiredTimeForm.format(source)); + if (expiredTimeForm == null) { + expiredTimeForm = new MessageFormat( + rb.getString("certificate.expired.on")); + } + Object[] source = {notAfter}; + certStr.append(expiredTimeForm.format(source)); - } catch (CertificateNotYetValidException cnyve) { - if (!isTsCert) notYetValidCert = true; + } catch (CertificateNotYetValidException cnyve) { + if (!isTsCert) notYetValidCert = true; - if (notYetTimeForm == null) { - notYetTimeForm = new MessageFormat( - rb.getString("certificate.is.not.valid.until")); + if (notYetTimeForm == null) { + notYetTimeForm = new MessageFormat( + rb.getString("certificate.is.not.valid.until")); + } + Object[] source = {x509Cert.getNotBefore()}; + certStr.append(notYetTimeForm.format(source)); } - Object[] source = { x509Cert.getNotBefore() }; - certStr.append(notYetTimeForm.format(source)); } certStr.append("]"); @@ -1638,152 +1784,57 @@ public class Main { // The JarSigner API always accepts the timestamp received. // We need to extract the certs from the signed jar to // validate it. - if (!noTimestamp) { - try (JarFile check = new JarFile(signedJarFile)) { - PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry( - "META-INF/" + sigfile + "." + privateKey.getAlgorithm()))); + try (JarFile check = new JarFile(signedJarFile)) { + PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry( + "META-INF/" + sigfile + "." + privateKey.getAlgorithm()))); + Timestamp ts = null; + try { SignerInfo si = p7.getSignerInfos()[0]; - PKCS7 tsToken = si.getTsToken(); - SignerInfo tsSi = tsToken.getSignerInfos()[0]; - try { - validateCertChain(Validator.VAR_TSA_SERVER, - tsSi.getCertificateChain(tsToken), null); - } catch (Exception e) { - tsaChainNotValidated = true; - tsaChainNotValidatedReason = e; + if (si.getTsToken() != null) { + hasTimestampBlock = true; } + ts = si.getTimestamp(); } catch (Exception e) { - if (debug) { - e.printStackTrace(); - } + tsaChainNotValidated = true; + tsaChainNotValidatedReason = e; + } + // Spaces before the ">>> Signer" and other lines are different + String result = certsAndTSInfo("", " ", Arrays.asList(certChain), ts); + if (verbose != null) { + System.out.println(result); + } + } catch (Exception e) { + if (debug) { + e.printStackTrace(); } } - // no IOException thrown in the follow try clause, so disable - // the try clause. - // try { - if (signedjar == null) { - // attempt an atomic rename. If that fails, - // rename the original jar file, then the signed - // one, then delete the original. - if (!signedJarFile.renameTo(jarFile)) { - File origJar = new File(jarName+".orig"); + if (signedjar == null) { + // attempt an atomic rename. If that fails, + // rename the original jar file, then the signed + // one, then delete the original. + if (!signedJarFile.renameTo(jarFile)) { + File origJar = new File(jarName+".orig"); - if (jarFile.renameTo(origJar)) { - if (signedJarFile.renameTo(jarFile)) { - origJar.delete(); - } else { - MessageFormat form = new MessageFormat(rb.getString - ("attempt.to.rename.signedJarFile.to.jarFile.failed")); - Object[] source = {signedJarFile, jarFile}; - error(form.format(source)); - } + if (jarFile.renameTo(origJar)) { + if (signedJarFile.renameTo(jarFile)) { + origJar.delete(); } else { MessageFormat form = new MessageFormat(rb.getString - ("attempt.to.rename.jarFile.to.origJar.failed")); - Object[] source = {jarFile, origJar}; + ("attempt.to.rename.signedJarFile.to.jarFile.failed")); + Object[] source = {signedJarFile, jarFile}; error(form.format(source)); } - } - } - - boolean warningAppeared = false; - if (weakAlg != 0 || badKeyUsage || badExtendedKeyUsage - || badNetscapeCertType || notYetValidCert - || chainNotValidated || tsaChainNotValidated - || hasExpiredCert || signerSelfSigned) { - if (strict) { - System.out.println(rb.getString("jar.signed.with.signer.errors.")); - System.out.println(); - System.out.println(rb.getString("Error.")); } else { - System.out.println(rb.getString("jar.signed.")); - System.out.println(); - System.out.println(rb.getString("Warning.")); - warningAppeared = true; - } - - if (badKeyUsage) { - System.out.println( - rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); - } - - if (badExtendedKeyUsage) { - System.out.println( - rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); - } - - if (badNetscapeCertType) { - System.out.println( - rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); - } - - if (hasExpiredCert) { - System.out.println( - rb.getString("The.signer.certificate.has.expired.")); - } else if (notYetValidCert) { - System.out.println( - rb.getString("The.signer.certificate.is.not.yet.valid.")); - } - - if (chainNotValidated) { - System.out.println(String.format( - rb.getString("The.signer.s.certificate.chain.is.invalid.reason.1"), - chainNotValidatedReason.getLocalizedMessage())); - } - - if (tsaChainNotValidated) { - System.out.println(String.format( - rb.getString("The.tsa.certificate.chain.is.invalid.reason.1"), - tsaChainNotValidatedReason.getLocalizedMessage())); - } - - if (signerSelfSigned) { - System.out.println( - rb.getString("The.signer.s.certificate.is.self.signed.")); - } - - if ((weakAlg & 1) == 1) { - System.out.println(String.format( - rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), - digestalg, "-digestalg")); - } - - if ((weakAlg & 2) == 2) { - System.out.println(String.format( - rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), - sigalg, "-sigalg")); - } - if ((weakAlg & 4) == 4) { - System.out.println(String.format( - rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), - tSADigestAlg, "-tsadigestalg")); - } - } else { - System.out.println(rb.getString("jar.signed.")); - } - if (hasExpiringCert || noTimestamp) { - if (!warningAppeared) { - System.out.println(); - System.out.println(rb.getString("Warning.")); - } - - if (hasExpiringCert) { - System.out.println( - rb.getString("The.signer.certificate.will.expire.within.six.months.")); - } - - if (noTimestamp) { - System.out.println( - String.format(rb.getString("no.timestamp.signing"), expireDate)); + MessageFormat form = new MessageFormat(rb.getString + ("attempt.to.rename.jarFile.to.origJar.failed")); + Object[] source = {jarFile, origJar}; + error(form.format(source)); } } + } - // no IOException thrown in the above try clause, so disable - // the catch clause. - // } catch(IOException ioe) { - // error(rb.getString("unable.to.sign.jar.")+ioe, ioe); - // } + displayMessagesAndResult(true); } /** @@ -1831,31 +1882,57 @@ public class Main { Map cacheForSignerInfo = new IdentityHashMap<>(); /** - * Returns a string of singer info, with a newline at the end + * Returns a string of signer info, with a newline at the end. + * Called by verifyJar(). */ private String signerInfo(CodeSigner signer, String tab) throws Exception { if (cacheForSignerInfo.containsKey(signer)) { return cacheForSignerInfo.get(signer); } - StringBuilder sb = new StringBuilder(); List certs = signer.getSignerCertPath().getCertificates(); - // display the signature timestamp, if present - Date timestamp; + // signing time is only displayed on verification Timestamp ts = signer.getTimestamp(); + String tsLine = ""; + if (ts != null) { + tsLine = printTimestamp(tab, ts) + "\n"; + } + // Spaces before the ">>> Signer" and other lines are the same. + + String result = certsAndTSInfo(tab, tab, certs, ts); + cacheForSignerInfo.put(signer, tsLine + result); + return result; + } + + /** + * Fills info on certs and timestamp into a StringBuilder, sets + * warning flags (through printCert) and validates cert chains. + * + * @param tab1 spaces before the ">>> Signer" line + * @param tab2 spaces before the other lines + * @param certs the signer cert + * @param ts the timestamp, can be null + * @return the info as a string + */ + private String certsAndTSInfo( + String tab1, + String tab2, + List certs, Timestamp ts) + throws Exception { + + Date timestamp; if (ts != null) { - sb.append(printTimestamp(tab, ts)); - sb.append('\n'); timestamp = ts.getTimestamp(); + noTimestamp = false; } else { timestamp = null; - noTimestamp = true; } // display the certificate(s). The first one is end-entity cert and // its KeyUsage should be checked. boolean first = true; - sb.append(tab).append(rb.getString("...Signer")).append('\n'); + StringBuilder sb = new StringBuilder(); + sb.append(tab1).append(rb.getString("...Signer")).append('\n'); for (Certificate c : certs) { - sb.append(printCert(false, tab, c, timestamp, first)); + sb.append(printCert(false, tab2, c, timestamp, first)); sb.append('\n'); first = false; } @@ -1864,13 +1941,13 @@ public class Main { } catch (Exception e) { chainNotValidated = true; chainNotValidatedReason = e; - sb.append(tab).append(rb.getString(".Invalid.certificate.chain.")) + sb.append(tab2).append(rb.getString(".Invalid.certificate.chain.")) .append(e.getLocalizedMessage()).append("]\n"); } if (ts != null) { - sb.append(tab).append(rb.getString("...TSA")).append('\n'); + sb.append(tab1).append(rb.getString("...TSA")).append('\n'); for (Certificate c : ts.getSignerCertPath().getCertificates()) { - sb.append(printCert(true, tab, c, timestamp, false)); + sb.append(printCert(true, tab2, c, null, false)); sb.append('\n'); } try { @@ -1879,7 +1956,7 @@ public class Main { } catch (Exception e) { tsaChainNotValidated = true; tsaChainNotValidatedReason = e; - sb.append(tab).append(rb.getString(".Invalid.TSA.certificate.chain.")) + sb.append(tab2).append(rb.getString(".Invalid.TSA.certificate.chain.")) .append(e.getLocalizedMessage()).append("]\n"); } } @@ -1887,9 +1964,8 @@ public class Main { && KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) { signerSelfSigned = true; } - String result = sb.toString(); - cacheForSignerInfo.put(signer, result); - return result; + + return sb.toString(); } private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze) @@ -1939,7 +2015,6 @@ public class Main { } try { - Set tas = new HashSet<>(); try { KeyStore caks = KeyStoreUtil.getCacertsKeyStore(); if (caks != null) { @@ -1947,7 +2022,7 @@ public class Main { while (aliases.hasMoreElements()) { String a = aliases.nextElement(); try { - tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null)); + trustedCerts.add((X509Certificate)caks.getCertificate(a)); } catch (Exception e2) { // ignore, when a SecretkeyEntry does not include a cert } @@ -2006,7 +2081,7 @@ public class Main { // PrivateKeyEntry if (store.isCertificateEntry(a) || c.getSubjectDN().equals(c.getIssuerDN())) { - tas.add(new TrustAnchor(c, null)); + trustedCerts.add(c); } } catch (Exception e2) { // ignore, when a SecretkeyEntry does not include a cert @@ -2014,7 +2089,11 @@ public class Main { } } finally { try { - pkixParameters = new PKIXBuilderParameters(tas, null); + pkixParameters = new PKIXBuilderParameters( + trustedCerts.stream() + .map(c -> new TrustAnchor(c, null)) + .collect(Collectors.toSet()), + null); pkixParameters.setRevocationEnabled(false); } catch (InvalidAlgorithmParameterException ex) { // Only if tas is empty @@ -2130,6 +2209,7 @@ public class Main { } } + // Called by signJar(). void getAliasInfo(String alias) throws Exception { Key key = null; @@ -2174,22 +2254,6 @@ public class Main { certChain[i] = (X509Certificate)cs[i]; } - // We don't meant to print anything, the next call - // checks validity and keyUsage etc - printCert(false, "", certChain[0], null, true); - - try { - validateCertChain(Validator.VAR_CODE_SIGNING, - Arrays.asList(certChain), null); - } catch (Exception e) { - chainNotValidated = true; - chainNotValidatedReason = e; - } - - if (KeyStoreUtil.isSelfSigned(certChain[0])) { - signerSelfSigned = true; - } - try { if (!token && keypass == null) key = store.getKey(alias, storepass); @@ -2247,7 +2311,7 @@ public class Main { * @param parameter this might be a timestamp */ void validateCertChain(String variant, List certs, - Object parameter) + Timestamp parameter) throws Exception { try { Validator.getInstance(Validator.TYPE_PKIX, @@ -2261,8 +2325,22 @@ public class Main { } // Exception might be dismissed if another warning flag - // is already set by printCert. This is only done for - // code signing certs. + // is already set by printCert. + + if (variant.equals(Validator.VAR_TSA_SERVER) && + e instanceof ValidatorException) { + // Throw cause if it's CertPathValidatorException, + if (e.getCause() != null && + e.getCause() instanceof CertPathValidatorException) { + e = (Exception) e.getCause(); + Throwable t = e.getCause(); + if ((t instanceof CertificateExpiredException && + hasExpiredTsaCert)) { + // we already have hasExpiredTsaCert + return; + } + } + } if (variant.equals(Validator.VAR_CODE_SIGNING) && e instanceof ValidatorException) { diff --git a/src/share/classes/sun/security/tools/jarsigner/Resources.java b/src/share/classes/sun/security/tools/jarsigner/Resources.java index 7a6ea5cbeb6e171f5fff20cd20c0332121f2fa00..da460cabd604aa42bccadccfa033eaa0469523af 100644 --- a/src/share/classes/sun/security/tools/jarsigner/Resources.java +++ b/src/share/classes/sun/security/tools/jarsigner/Resources.java @@ -219,6 +219,7 @@ public class Resources extends java.util.ListResourceBundle { {"Error.", "Error: "}, {"...Signer", ">>> Signer"}, {"...TSA", ">>> TSA"}, + {"trusted.certificate", "trusted certificate"}, {"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.", "This jar contains unsigned entries which have not been integrity-checked. "}, {"This.jar.contains.entries.whose.signer.certificate.has.expired.", @@ -235,8 +236,16 @@ public class Resources extends java.util.ListResourceBundle { "Re-run with the -verbose and -certs options for more details."}, {"The.signer.certificate.has.expired.", "The signer certificate has expired."}, + {"The.timestamp.expired.1.but.usable.2", + "The timestamp expired on %1$tY-%1$tm-%1$td. However, the JAR will be valid until the signer certificate expires on %2$tY-%2$tm-%2$td."}, + {"The.timestamp.has.expired.", + "The timestamp has expired."}, {"The.signer.certificate.will.expire.within.six.months.", "The signer certificate will expire within six months."}, + {"The.timestamp.will.expire.within.one.year.on.1", + "The timestamp will expire within one year on %1$tY-%1$tm-%1$td."}, + {"The.timestamp.will.expire.within.one.year.on.1.but.2", + "The timestamp will expire within one year on %1$tY-%1$tm-%1$td. However, the JAR will be valid until the signer certificate expires on %2$tY-%2$tm-%2$td."}, {"The.signer.certificate.is.not.yet.valid.", "The signer certificate is not yet valid."}, {"The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.", @@ -267,10 +276,18 @@ public class Resources extends java.util.ListResourceBundle { "This jar contains entries whose TSA certificate chain is invalid. Reason: %s"}, {"no.timestamp.signing", "No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."}, + {"invalid.timestamp.signing", + "The timestamp is invalid. Without a valid timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td)."}, {"no.timestamp.verifying", - "This jar contains signatures that does not include a timestamp. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."}, + "This jar contains signatures that do not include a timestamp. Without a timestamp, users may not be able to validate this jar after any of the signer certificates expire (as early as %1$tY-%1$tm-%1$td)."}, {"bad.timestamp.verifying", "This jar contains signatures that include an invalid timestamp. Without a valid timestamp, users may not be able to validate this jar after any of the signer certificates expire (as early as %1$tY-%1$tm-%1$td).\nRerun jarsigner with -J-Djava.security.debug=jar for more information."}, + {"The.signer.certificate.will.expire.on.1.", + "The signer certificate will expire on %1$tY-%1$tm-%1$td."}, + {"The.timestamp.will.expire.on.1.", + "The timestamp will expire on %1$tY-%1$tm-%1$td."}, + {"signer.cert.expired.1.but.timestamp.good.2.", + "The signer certificate expired on %1$tY-%1$tm-%1$td. However, the JAR will be valid until the timestamp expires on %2$tY-%2$tm-%2$td."}, {"Unknown.password.type.", "Unknown password type: "}, {"Cannot.find.environment.variable.", "Cannot find environment variable: "}, diff --git a/test/sun/security/tools/jarsigner/TimestampCheck.java b/test/sun/security/tools/jarsigner/TimestampCheck.java index ab11a8d58d249cff355cb4890611dc8e4950e172..b5070ce11fb25fb7bab1f7c4f60716ca663622b2 100644 --- a/test/sun/security/tools/jarsigner/TimestampCheck.java +++ b/test/sun/security/tools/jarsigner/TimestampCheck.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,6 +75,7 @@ import jdk.testlibrary.Utils; * java.base/sun.security.util * java.base/sun.security.tools.keytool * @library /lib/testlibrary + * @compile -XDignore.symbol.file TimestampCheck.java * @run main/othervm/timeout=600 TimestampCheck */ public class TimestampCheck { @@ -121,12 +122,12 @@ public class TimestampCheck { */ byte[] sign(byte[] input, String path) throws Exception { DerValue value = new DerValue(input); - System.out.println("\nIncoming Request\n==================="); - System.out.println("Version: " + value.data.getInteger()); + System.out.println("#\n# Incoming Request\n==================="); + System.out.println("# Version: " + value.data.getInteger()); DerValue messageImprint = value.data.getDerValue(); AlgorithmId aid = AlgorithmId.parse( messageImprint.data.getDerValue()); - System.out.println("AlgorithmId: " + aid); + System.out.println("# AlgorithmId: " + aid); ObjectIdentifier policyId = new ObjectIdentifier(defaultPolicyId); BigInteger nonce = null; @@ -134,16 +135,16 @@ public class TimestampCheck { DerValue v = value.data.getDerValue(); if (v.tag == DerValue.tag_Integer) { nonce = v.getBigInteger(); - System.out.println("nonce: " + nonce); + System.out.println("# nonce: " + nonce); } else if (v.tag == DerValue.tag_Boolean) { - System.out.println("certReq: " + v.getBoolean()); + System.out.println("# certReq: " + v.getBoolean()); } else if (v.tag == DerValue.tag_ObjectId) { policyId = v.getOID(); - System.out.println("PolicyID: " + policyId); + System.out.println("# PolicyID: " + policyId); } } - System.out.println("\nResponse\n==================="); + System.out.println("#\n# Response\n==================="); FileInputStream is = new FileInputStream(keystore); KeyStore ks = KeyStore.getInstance("JCEKS"); ks.load(is, "changeit".toCharArray()); @@ -229,10 +230,10 @@ public class TimestampCheck { "1.2.840.113549.1.9.16.1.4"), new DerValue(tstInfo2.toByteArray())); - System.out.println("Signing..."); - System.out.println(new X500Name(signer + System.out.println("# Signing..."); + System.out.println("# " + new X500Name(signer .getIssuerX500Principal().getName())); - System.out.println(signer.getSerialNumber()); + System.out.println("# " + signer.getSerialNumber()); SignerInfo signerInfo = new SignerInfo( new X500Name(signer.getIssuerX500Principal().getName()), @@ -303,8 +304,6 @@ public class TimestampCheck { public static void main(String[] args) throws Throwable { - prepare(); - try (Handler tsa = Handler.init(0, "ks");) { tsa.start(); int port = tsa.getPort(); @@ -313,62 +312,99 @@ public class TimestampCheck { if (args.length == 0) { // Run this test + prepare(); + sign("normal") .shouldNotContain("Warning") + .shouldContain("The signer certificate will expire on") + .shouldContain("The timestamp will expire on") .shouldHaveExitValue(0); verify("normal.jar") .shouldNotContain("Warning") .shouldHaveExitValue(0); + verify("normal.jar", "-verbose") + .shouldNotContain("Warning") + .shouldContain("The signer certificate will expire on") + .shouldContain("The timestamp will expire on") + .shouldHaveExitValue(0); + // Simulate signing at a previous date: // 1. tsold will create a timestamp of 20 days ago. // 2. oldsigner expired 10 days ago. - // jarsigner will show a warning at signing. signVerbose("tsold", "unsigned.jar", "tsold.jar", "oldsigner") - .shouldHaveExitValue(4); + .shouldNotContain("Warning") + .shouldMatch("signer certificate expired on .*. " + + "However, the JAR will be valid") + .shouldHaveExitValue(0); // It verifies perfectly. verify("tsold.jar", "-verbose", "-certs") .shouldNotContain("Warning") + .shouldMatch("signer certificate expired on .*. " + + "However, the JAR will be valid") .shouldHaveExitValue(0); + // No timestamp signVerbose(null, "unsigned.jar", "none.jar", "signer") .shouldContain("is not timestamped") + .shouldContain("The signer certificate will expire on") + .shouldHaveExitValue(0); + + verify("none.jar", "-verbose") + .shouldContain("do not include a timestamp") + .shouldContain("The signer certificate will expire on") .shouldHaveExitValue(0); + // Error cases + signVerbose(null, "unsigned.jar", "badku.jar", "badku") + .shouldContain("KeyUsage extension doesn't allow code signing") .shouldHaveExitValue(8); checkBadKU("badku.jar"); // 8180289: unvalidated TSA cert chain sign("tsnoca") - .shouldContain("TSA certificate chain is invalid") + .shouldContain("The TSA certificate chain is invalid. " + + "Reason: Path does not chain with any of the trust anchors") .shouldHaveExitValue(64); verify("tsnoca.jar", "-verbose", "-certs") .shouldHaveExitValue(64) .shouldContain("jar verified") - .shouldContain("Invalid TSA certificate chain") - .shouldContain("TSA certificate chain is invalid"); + .shouldContain("Invalid TSA certificate chain: " + + "Path does not chain with any of the trust anchors") + .shouldContain("TSA certificate chain is invalid." + + " Reason: Path does not chain with any of the trust anchors"); sign("nononce") + .shouldContain("Nonce missing in timestamp token") .shouldHaveExitValue(1); sign("diffnonce") + .shouldContain("Nonce changed in timestamp token") .shouldHaveExitValue(1); sign("baddigest") + .shouldContain("Digest octets changed in timestamp token") .shouldHaveExitValue(1); sign("diffalg") + .shouldContain("Digest algorithm not") .shouldHaveExitValue(1); + sign("fullchain") .shouldHaveExitValue(0); // Success, 6543440 solved. + sign("tsbad1") + .shouldContain("Certificate is not valid for timestamping") .shouldHaveExitValue(1); sign("tsbad2") + .shouldContain("Certificate is not valid for timestamping") .shouldHaveExitValue(1); sign("tsbad3") + .shouldContain("Certificate is not valid for timestamping") .shouldHaveExitValue(1); sign("nocert") + .shouldContain("Certificate not included in timestamp token") .shouldHaveExitValue(1); sign("policy", "-tsapolicyid", "1.2.3") @@ -376,6 +412,7 @@ public class TimestampCheck { checkTimestamp("policy.jar", "1.2.3", "SHA-256"); sign("diffpolicy", "-tsapolicyid", "1.2.3") + .shouldContain("TSAPolicyID changed in timestamp token") .shouldHaveExitValue(1); sign("sha1alg", "-tsadigestalg", "SHA") @@ -384,11 +421,13 @@ public class TimestampCheck { sign("tsweak", "-digestalg", "MD5", "-sigalg", "MD5withRSA", "-tsadigestalg", "MD5") - .shouldHaveExitValue(68); + .shouldHaveExitValue(68) + .shouldContain("The timestamp is invalid. Without a valid timestamp"); checkWeak("tsweak.jar"); signVerbose("tsweak", "unsigned.jar", "tsweak2.jar", "signer") .shouldHaveExitValue(64) + .shouldContain("The timestamp is invalid. Without a valid timestamp") .shouldContain("TSA certificate chain is invalid"); // Weak timestamp is an error and jar treated unsigned @@ -397,19 +436,26 @@ public class TimestampCheck { .shouldContain("treated as unsigned") .shouldMatch("Timestamp.*512.*weak"); + // Algorithm used in signing is weak signVerbose("normal", "unsigned.jar", "halfWeak.jar", "signer", "-digestalg", "MD5") + .shouldContain("-digestalg option is considered a security risk") .shouldHaveExitValue(4); checkHalfWeak("halfWeak.jar"); // sign with DSA key signVerbose("normal", "unsigned.jar", "sign1.jar", "dsakey") .shouldHaveExitValue(0); + // sign with RSAkeysize < 1024 signVerbose("normal", "sign1.jar", "sign2.jar", "weakkeysize") + .shouldContain("Algorithm constraints check failed on keysize") .shouldHaveExitValue(4); checkMultiple("sign2.jar"); + // 8191438: jarsigner should print when a timestamp will expire + checkExpiration(); + // When .SF or .RSA is missing or invalid checkMissingOrInvalidFiles("normal.jar"); @@ -417,12 +463,118 @@ public class TimestampCheck { checkInvalidTsaCertKeyUsage(); } } else { // Run as a standalone server - System.out.println("Press Enter to quit server"); + System.out.println("TSA started at " + host + + ". Press Enter to quit server"); System.in.read(); } } } + private static void checkExpiration() throws Exception { + + // Warning when expired or expiring + signVerbose(null, "unsigned.jar", "expired.jar", "expired") + .shouldContain("signer certificate has expired") + .shouldHaveExitValue(4); + verify("expired.jar") + .shouldContain("signer certificate has expired") + .shouldHaveExitValue(4); + signVerbose(null, "unsigned.jar", "expiring.jar", "expiring") + .shouldContain("signer certificate will expire within") + .shouldHaveExitValue(0); + verify("expiring.jar") + .shouldContain("signer certificate will expire within") + .shouldHaveExitValue(0); + // Info for long + signVerbose(null, "unsigned.jar", "long.jar", "long") + .shouldNotContain("signer certificate has expired") + .shouldNotContain("signer certificate will expire within") + .shouldContain("signer certificate will expire on") + .shouldHaveExitValue(0); + verify("long.jar") + .shouldNotContain("signer certificate has expired") + .shouldNotContain("signer certificate will expire within") + .shouldNotContain("The signer certificate will expire") + .shouldHaveExitValue(0); + verify("long.jar", "-verbose") + .shouldContain("The signer certificate will expire") + .shouldHaveExitValue(0); + + // Both expired + signVerbose("tsexpired", "unsigned.jar", + "tsexpired-expired.jar", "expired") + .shouldContain("The signer certificate has expired.") + .shouldContain("The timestamp has expired.") + .shouldHaveExitValue(4); + verify("tsexpired-expired.jar") + .shouldContain("signer certificate has expired") + .shouldContain("timestamp has expired.") + .shouldHaveExitValue(4); + + // TS expired but signer still good + signVerbose("tsexpired", "unsigned.jar", + "tsexpired-long.jar", "long") + .shouldContain("The timestamp expired on") + .shouldHaveExitValue(0); + verify("tsexpired-long.jar") + .shouldMatch("timestamp expired on.*However, the JAR will be valid") + .shouldNotContain("Error") + .shouldHaveExitValue(0); + + signVerbose("tsexpired", "unsigned.jar", + "tsexpired-ca.jar", "ca") + .shouldContain("The timestamp has expired.") + .shouldHaveExitValue(4); + verify("tsexpired-ca.jar") + .shouldNotContain("timestamp has expired") + .shouldNotContain("Error") + .shouldHaveExitValue(0); + + // Warning when expiring + sign("tsexpiring") + .shouldContain("timestamp will expire within") + .shouldHaveExitValue(0); + verify("tsexpiring.jar") + .shouldContain("timestamp will expire within") + .shouldNotContain("still valid") + .shouldHaveExitValue(0); + + signVerbose("tsexpiring", "unsigned.jar", + "tsexpiring-ca.jar", "ca") + .shouldContain("self-signed") + .stderrShouldNotMatch("The.*expir") + .shouldHaveExitValue(4); // self-signed + verify("tsexpiring-ca.jar") + .stderrShouldNotMatch("The.*expir") + .shouldHaveExitValue(0); + + signVerbose("tsexpiringsoon", "unsigned.jar", + "tsexpiringsoon-long.jar", "long") + .shouldContain("The timestamp will expire") + .shouldHaveExitValue(0); + verify("tsexpiringsoon-long.jar") + .shouldMatch("timestamp will expire.*However, the JAR will be valid until") + .shouldHaveExitValue(0); + + // Info for long + sign("tslong") + .shouldNotContain("timestamp has expired") + .shouldNotContain("timestamp will expire within") + .shouldContain("timestamp will expire on") + .shouldContain("signer certificate will expire on") + .shouldHaveExitValue(0); + verify("tslong.jar") + .shouldNotContain("timestamp has expired") + .shouldNotContain("timestamp will expire within") + .shouldNotContain("timestamp will expire on") + .shouldNotContain("signer certificate will expire on") + .shouldHaveExitValue(0); + verify("tslong.jar", "-verbose") + .shouldContain("timestamp will expire on") + .shouldContain("signer certificate will expire on") + .shouldHaveExitValue(0); + } + private static void checkInvalidTsaCertKeyUsage() throws Exception { // Hack: Rewrite the TSA cert inside normal.jar into ts2.jar. @@ -670,6 +822,14 @@ public class TimestampCheck { keytool("-alias tsbad3 -genkeypair -dname CN=tsbad3"); keytool("-alias tsnoca -genkeypair -dname CN=tsnoca"); + keytool("-alias expired -genkeypair -dname CN=expired"); + keytool("-alias expiring -genkeypair -dname CN=expiring"); + keytool("-alias long -genkeypair -dname CN=long"); + keytool("-alias tsexpired -genkeypair -dname CN=tsexpired"); + keytool("-alias tsexpiring -genkeypair -dname CN=tsexpiring"); + keytool("-alias tsexpiringsoon -genkeypair -dname CN=tsexpiringsoon"); + keytool("-alias tslong -genkeypair -dname CN=tslong"); + // tsnoca's issuer will be removed from keystore later keytool("-alias ca -genkeypair -ext bc -dname CN=CA"); gencert("tsnoca", "-ext eku:critical=ts"); @@ -681,7 +841,15 @@ public class TimestampCheck { gencert("dsakey"); gencert("weakkeysize"); gencert("badku", "-ext ku:critical=keyAgreement"); - gencert("ts", "-ext eku:critical=ts"); + gencert("ts", "-ext eku:critical=ts -validity 500"); + + gencert("expired", "-validity 10 -startdate -12d"); + gencert("expiring", "-validity 178"); + gencert("long", "-validity 182"); + gencert("tsexpired", "-ext eku:critical=ts -validity 10 -startdate -12d"); + gencert("tsexpiring", "-ext eku:critical=ts -validity 364"); + gencert("tsexpiringsoon", "-ext eku:critical=ts -validity 170"); // earlier than expiring + gencert("tslong", "-ext eku:critical=ts -validity 367"); for (int i = 0; i < 5; i++) { @@ -701,7 +869,7 @@ public class TimestampCheck { } } - gencert("tsold", "-ext eku:critical=ts -startdate -40d -validity 45"); + gencert("tsold", "-ext eku:critical=ts -startdate -40d -validity 500"); gencert("tsweak", "-ext eku:critical=ts"); gencert("tsbad1"); diff --git a/test/sun/security/tools/jarsigner/warnings/AliasNotInStoreTest.java b/test/sun/security/tools/jarsigner/warnings/AliasNotInStoreTest.java index 81475fb7b5f3bf653d8a7d4f20fa4e14714fc5c2..8619ee12f58eb084daa30c9706ad25ecb76cb12b 100644 --- a/test/sun/security/tools/jarsigner/warnings/AliasNotInStoreTest.java +++ b/test/sun/security/tools/jarsigner/warnings/AliasNotInStoreTest.java @@ -51,32 +51,12 @@ public class AliasNotInStoreTest extends Test { JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); // create first key pair for signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", FIRST_KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", BOTH_KEYS_KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=First", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); - - // create second key pair for signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", SECOND_KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", BOTH_KEYS_KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Second", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + createAlias(FIRST_KEY_ALIAS); + createAlias(SECOND_KEY_ALIAS); // sign jar with first key OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, - "-keystore", BOTH_KEYS_KEYSTORE, + "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD, "-signedjar", SIGNED_JARFILE, @@ -93,7 +73,7 @@ public class AliasNotInStoreTest extends Test { // sign jar with second key analyzer = ProcessTools.executeCommand(JARSIGNER, - "-keystore", BOTH_KEYS_KEYSTORE, + "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD, UPDATED_SIGNED_JARFILE, @@ -104,7 +84,7 @@ public class AliasNotInStoreTest extends Test { // create keystore that contains only first key ProcessTools.executeCommand(KEYTOOL, "-importkeystore", - "-srckeystore", BOTH_KEYS_KEYSTORE, + "-srckeystore", KEYSTORE, "-srcalias", FIRST_KEY_ALIAS, "-srcstorepass", PASSWORD, "-srckeypass", PASSWORD, @@ -113,7 +93,7 @@ public class AliasNotInStoreTest extends Test { "-deststorepass", PASSWORD, "-destkeypass", PASSWORD).shouldHaveExitValue(0); - // verify jar with keystore that contains only first key in strict mode, + // verify jar with keystore that contains only first key, // so there is signed entry (FirstClass.class) that is not signed // by any alias in the keystore analyzer = ProcessTools.executeCommand(JARSIGNER, diff --git a/test/sun/security/tools/jarsigner/warnings/BadExtendedKeyUsageTest.java b/test/sun/security/tools/jarsigner/warnings/BadExtendedKeyUsageTest.java index a8c3c8a40d8b796e27d9772d8884375d00839afe..1a49c15d1fd14f343bb68432d68756f28cdb0c8d 100644 --- a/test/sun/security/tools/jarsigner/warnings/BadExtendedKeyUsageTest.java +++ b/test/sun/security/tools/jarsigner/warnings/BadExtendedKeyUsageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,17 +52,14 @@ public class BadExtendedKeyUsageTest extends Test { // create a certificate whose signer certificate's // ExtendedKeyUsage extension doesn't allow code signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Test", + // create key pair for jar signing + createAlias(CA_KEY_ALIAS); + createAlias(KEY_ALIAS); + + issueCert( + KEY_ALIAS, "-ext", "ExtendedkeyUsage=serverAuth", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + "-validity", Integer.toString(VALIDITY)); // sign jar OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, diff --git a/test/sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java b/test/sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java index fd37eb904f39ec8afb3df80bdb35a8a8396901c2..fb0fac9fdc37113f546af0143e2349adc0c82708 100644 --- a/test/sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java +++ b/test/sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,17 +53,13 @@ public class BadKeyUsageTest extends Test { // create a certificate whose signer certificate's KeyUsage extension // doesn't allow code signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Test", + createAlias(CA_KEY_ALIAS); + createAlias(KEY_ALIAS); + + issueCert( + KEY_ALIAS, "-ext", "KeyUsage=keyAgreement", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + "-validity", Integer.toString(VALIDITY)); // sign jar OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, diff --git a/test/sun/security/tools/jarsigner/warnings/BadNetscapeCertTypeTest.java b/test/sun/security/tools/jarsigner/warnings/BadNetscapeCertTypeTest.java index e2e8086d8e071535a1711fd14cb536e38eef3727..443331ddeec73c5e2f20a359eb8ab4670a1ec844 100644 --- a/test/sun/security/tools/jarsigner/warnings/BadNetscapeCertTypeTest.java +++ b/test/sun/security/tools/jarsigner/warnings/BadNetscapeCertTypeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,6 @@ import jdk.testlibrary.OutputAnalyzer; import jdk.testlibrary.ProcessTools; import jdk.testlibrary.JarUtils; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Base64; - /** * @test * @bug 8024302 8026037 @@ -38,25 +34,14 @@ import java.util.Base64; */ public class BadNetscapeCertTypeTest extends Test { - private static final String NETSCAPE_KEYSTORE_BASE64 = TEST_SOURCES + FS - + "bad_netscape_cert_type.jks.base64"; - - private static final String NETSCAPE_KEYSTORE - = "bad_netscape_cert_type.jks"; - /** * The test signs and verifies a jar that contains entries * whose signer certificate's NetscapeCertType extension * doesn't allow code signing (badNetscapeCertType). * Warning message is expected. - * Run bad_netscape_cert_type.sh script to create bad_netscape_cert_type.jks */ public static void main(String[] args) throws Throwable { - Files.write(Paths.get(NETSCAPE_KEYSTORE), - Base64.getMimeDecoder().decode( - Files.readAllBytes(Paths.get(NETSCAPE_KEYSTORE_BASE64)))); - BadNetscapeCertTypeTest test = new BadNetscapeCertTypeTest(); test.start(); } @@ -66,10 +51,22 @@ public class BadNetscapeCertTypeTest extends Test { Utils.createFiles(FIRST_FILE); JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + // create a certificate whose signer certificate's + // NetscapeCertType extension doesn't allow code signing + // create key pair for jar signing + createAlias(CA_KEY_ALIAS); + createAlias(KEY_ALIAS); + + issueCert( + KEY_ALIAS, + // NetscapeCertType [ SSL client ] + "-ext", "2.16.840.1.113730.1.1=03020780", + "-validity", Integer.toString(VALIDITY)); + // sign jar OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, "-verbose", - "-keystore", NETSCAPE_KEYSTORE, + "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD, "-signedjar", SIGNED_JARFILE, @@ -82,7 +79,7 @@ public class BadNetscapeCertTypeTest extends Test { analyzer = ProcessTools.executeCommand(JARSIGNER, "-verify", "-verbose", - "-keystore", NETSCAPE_KEYSTORE, + "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD, SIGNED_JARFILE); @@ -94,7 +91,7 @@ public class BadNetscapeCertTypeTest extends Test { "-verify", "-verbose", "-strict", - "-keystore", NETSCAPE_KEYSTORE, + "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD, SIGNED_JARFILE); diff --git a/test/sun/security/tools/jarsigner/warnings/ChainNotValidatedTest.java b/test/sun/security/tools/jarsigner/warnings/ChainNotValidatedTest.java index 21f09799c71d269c1b7ac9d1adb88cab38bf88bd..b9d0ae59ec1af3982922dea92f63095c28a75d40 100644 --- a/test/sun/security/tools/jarsigner/warnings/ChainNotValidatedTest.java +++ b/test/sun/security/tools/jarsigner/warnings/ChainNotValidatedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,117 +21,52 @@ * questions. */ -import java.io.File; import jdk.testlibrary.OutputAnalyzer; import jdk.testlibrary.ProcessTools; import jdk.testlibrary.JarUtils; +import java.nio.file.Files; +import java.nio.file.Paths; + /** * @test * @bug 8024302 8026037 * @summary Test for chainNotValidated warning * @library /lib/testlibrary ../ - * @run main ChainNotValidatedTest + * @run main ChainNotValidatedTest ca2yes + * @run main ChainNotValidatedTest ca2no */ public class ChainNotValidatedTest extends Test { - private static final String CHAIN = "chain"; - - /** - * The test signs and verifies a jar that contains entries - * whose cert chain can't be correctly validated (chainNotValidated). - * Warning message is expected. - */ public static void main(String[] args) throws Throwable { ChainNotValidatedTest test = new ChainNotValidatedTest(); - test.start(); + test.start(args[0].equals("ca2yes")); } - private void start() throws Throwable { + private void start(boolean ca2yes) throws Throwable { // create a jar file that contains one class file Utils.createFiles(FIRST_FILE); JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); - // create self-signed certificate whose BasicConstraints extension - // is set to false, so the certificate may not be used - // as a parent certificate (certpath validation should fail) - ProcessTools.executeCommand(KEYTOOL, - "-genkeypair", - "-alias", CA_KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=CA", - "-ext", "BasicConstraints:critical=ca:false", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + // We have 2 @run. Need cleanup. + Files.deleteIfExists(Paths.get(KEYSTORE)); - // create a certificate that is signed by self-signed certificate - // despite of it may not be used as a parent certificate - // (certpath validation should fail) - ProcessTools.executeCommand(KEYTOOL, - "-genkeypair", - "-alias", KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Test", - "-ext", "BasicConstraints:critical=ca:false", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); - - ProcessTools.executeCommand(KEYTOOL, - "-certreq", - "-alias", KEY_ALIAS, - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-file", CERT_REQUEST_FILENAME).shouldHaveExitValue(0); + // Root CA is not checked at all. If the intermediate CA has + // BasicConstraints extension set to true, it will be valid. + // Otherwise, chain validation will fail. + createAlias(CA_KEY_ALIAS); + createAlias(CA2_KEY_ALIAS); + issueCert(CA2_KEY_ALIAS, + "-ext", + "bc=ca:" + ca2yes); - ProcessTools.executeCommand(KEYTOOL, - "-gencert", - "-alias", CA_KEY_ALIAS, - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-infile", CERT_REQUEST_FILENAME, - "-validity", Integer.toString(VALIDITY), - "-outfile", CERT_FILENAME).shouldHaveExitValue(0); - - ProcessTools.executeCommand(KEYTOOL, - "-importcert", - "-alias", KEY_ALIAS, - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-file", CERT_FILENAME).shouldHaveExitValue(0); - - ProcessBuilder pb = new ProcessBuilder(KEYTOOL, - "-export", - "-rfc", - "-alias", KEY_ALIAS, - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD); - pb.redirectOutput(ProcessBuilder.Redirect.appendTo(new File(CHAIN))); - ProcessTools.executeCommand(pb).shouldHaveExitValue(0); - - pb = new ProcessBuilder(KEYTOOL, - "-export", - "-rfc", - "-alias", CA_KEY_ALIAS, - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD); - pb.redirectOutput(ProcessBuilder.Redirect.appendTo(new File(CHAIN))); - ProcessTools.executeCommand(pb).shouldHaveExitValue(0); + createAlias(KEY_ALIAS); + issueCert(KEY_ALIAS, "-alias", CA2_KEY_ALIAS); - // remove CA certificate + // remove CA2 certificate so it's not trusted ProcessTools.executeCommand(KEYTOOL, "-delete", - "-alias", CA_KEY_ALIAS, + "-alias", CA2_KEY_ALIAS, "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD).shouldHaveExitValue(0); @@ -141,12 +76,15 @@ public class ChainNotValidatedTest extends Test { "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD, - "-certchain", CHAIN, "-signedjar", SIGNED_JARFILE, UNSIGNED_JARFILE, KEY_ALIAS); - checkSigning(analyzer, CHAIN_NOT_VALIDATED_SIGNING_WARNING); + if (ca2yes) { + checkSigning(analyzer, "!" + CHAIN_NOT_VALIDATED_SIGNING_WARNING); + } else { + checkSigning(analyzer, CHAIN_NOT_VALIDATED_SIGNING_WARNING); + } // verify signed jar analyzer = ProcessTools.executeCommand(JARSIGNER, @@ -155,10 +93,13 @@ public class ChainNotValidatedTest extends Test { "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD, - "-certchain", CHAIN, SIGNED_JARFILE); - checkVerifying(analyzer, 0, CHAIN_NOT_VALIDATED_VERIFYING_WARNING); + if (ca2yes) { + checkVerifying(analyzer, 0, "!" + CHAIN_NOT_VALIDATED_VERIFYING_WARNING); + } else { + checkVerifying(analyzer, 0, CHAIN_NOT_VALIDATED_VERIFYING_WARNING); + } // verify signed jar in strict mode analyzer = ProcessTools.executeCommand(JARSIGNER, @@ -168,11 +109,15 @@ public class ChainNotValidatedTest extends Test { "-keystore", KEYSTORE, "-storepass", PASSWORD, "-keypass", PASSWORD, - "-certchain", CHAIN, SIGNED_JARFILE); - checkVerifying(analyzer, CHAIN_NOT_VALIDATED_EXIT_CODE, - CHAIN_NOT_VALIDATED_VERIFYING_WARNING); + if (ca2yes) { + checkVerifying(analyzer, 0, + "!" + CHAIN_NOT_VALIDATED_VERIFYING_WARNING); + } else { + checkVerifying(analyzer, CHAIN_NOT_VALIDATED_EXIT_CODE, + CHAIN_NOT_VALIDATED_VERIFYING_WARNING); + } System.out.println("Test passed"); } diff --git a/test/sun/security/tools/jarsigner/warnings/HasExpiredCertTest.java b/test/sun/security/tools/jarsigner/warnings/HasExpiredCertTest.java index ccb8e918a70e28484587c59eede569cb4a6f8c5f..f457776fb658d31b886fdb01604c8f1bd62c5457 100644 --- a/test/sun/security/tools/jarsigner/warnings/HasExpiredCertTest.java +++ b/test/sun/security/tools/jarsigner/warnings/HasExpiredCertTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,18 +52,13 @@ public class HasExpiredCertTest extends Test { JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); // create key pair for jar signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Test", + createAlias(CA_KEY_ALIAS); + createAlias(KEY_ALIAS); + + issueCert( + KEY_ALIAS, "-startdate", "-" + SHORT_VALIDITY * 2 + "d", - "-validity", Integer.toString(SHORT_VALIDITY)) - .shouldHaveExitValue(0); + "-validity", Integer.toString(SHORT_VALIDITY)); // sign jar OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, diff --git a/test/sun/security/tools/jarsigner/warnings/HasExpiringCertTest.java b/test/sun/security/tools/jarsigner/warnings/HasExpiringCertTest.java index f34148e600f4511b9b17eb654c149ae5399c2c82..8a71c667807a2aff3cc3d7fd442c619e8ccac72e 100644 --- a/test/sun/security/tools/jarsigner/warnings/HasExpiringCertTest.java +++ b/test/sun/security/tools/jarsigner/warnings/HasExpiringCertTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,17 +52,12 @@ public class HasExpiringCertTest extends Test { JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); // create key pair for jar signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Test", - "-validity", Integer.toString(SHORT_VALIDITY)) - .shouldHaveExitValue(0); + createAlias(CA_KEY_ALIAS); + createAlias(KEY_ALIAS); + + issueCert( + KEY_ALIAS, + "-validity", Integer.toString(SHORT_VALIDITY)); // sign jar OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, diff --git a/test/sun/security/tools/jarsigner/warnings/HasUnsignedEntryTest.java b/test/sun/security/tools/jarsigner/warnings/HasUnsignedEntryTest.java index e71feb3de3af19c4fd813e67929fec9d84defdd5..078ff8962dfb1eaf1e8485348ad7d2173ca2460c 100644 --- a/test/sun/security/tools/jarsigner/warnings/HasUnsignedEntryTest.java +++ b/test/sun/security/tools/jarsigner/warnings/HasUnsignedEntryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,16 +51,11 @@ public class HasUnsignedEntryTest extends Test { JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); // create key pair for signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Test", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + createAlias(CA_KEY_ALIAS); + createAlias(KEY_ALIAS); + issueCert( + KEY_ALIAS, + "-validity", Integer.toString(VALIDITY)); // sign jar OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, diff --git a/test/sun/security/tools/jarsigner/warnings/MultipleWarningsTest.java b/test/sun/security/tools/jarsigner/warnings/MultipleWarningsTest.java index 677914c5d04a40b0cbca3c2ddd228e66ef5918fa..862e8cae799ea68536f1f84e8c34a43c53fe748a 100644 --- a/test/sun/security/tools/jarsigner/warnings/MultipleWarningsTest.java +++ b/test/sun/security/tools/jarsigner/warnings/MultipleWarningsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,35 +54,25 @@ public class MultipleWarningsTest extends Test { // create a jar file that contains one class file JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + createAlias(CA_KEY_ALIAS); + // create first expired certificate // whose ExtendedKeyUsage extension does not allow code signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", FIRST_KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=First", + createAlias(FIRST_KEY_ALIAS); + issueCert( + FIRST_KEY_ALIAS, "-ext", "ExtendedkeyUsage=serverAuth", "-startdate", "-" + VALIDITY * 2 + "d", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + "-validity", Integer.toString(VALIDITY)); // create second expired certificate // whose KeyUsage extension does not allow code signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", SECOND_KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Second", + createAlias(SECOND_KEY_ALIAS); + issueCert( + SECOND_KEY_ALIAS, "-ext", "ExtendedkeyUsage=serverAuth", "-startdate", "-" + VALIDITY * 2 + "d", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + "-validity", Integer.toString(VALIDITY)); // sign jar with first key OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, diff --git a/test/sun/security/tools/jarsigner/warnings/NoTimestampTest.java b/test/sun/security/tools/jarsigner/warnings/NoTimestampTest.java index 10b142ab96732a372e179dd39eb514ac8905f911..8429fe26b2071ed4469a8908ffc35d46746432bf 100644 --- a/test/sun/security/tools/jarsigner/warnings/NoTimestampTest.java +++ b/test/sun/security/tools/jarsigner/warnings/NoTimestampTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,15 +57,9 @@ public class NoTimestampTest extends Test { * 24 * 60 * 60 * 1000L); // create key pair - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Test", + createAlias(CA_KEY_ALIAS); + createAlias(KEY_ALIAS); + issueCert(KEY_ALIAS, "-validity", Integer.toString(VALIDITY)); // sign jar file diff --git a/test/sun/security/tools/jarsigner/warnings/NotSignedByAliasTest.java b/test/sun/security/tools/jarsigner/warnings/NotSignedByAliasTest.java index 40ef68ef60a221ea82c3ec25b13fa46082bc69d3..9fb92625f8383c18b8a7c379b2612de0c733bffa 100644 --- a/test/sun/security/tools/jarsigner/warnings/NotSignedByAliasTest.java +++ b/test/sun/security/tools/jarsigner/warnings/NotSignedByAliasTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,29 +49,19 @@ public class NotSignedByAliasTest extends Test { Utils.createFiles(FIRST_FILE); JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); + createAlias(CA_KEY_ALIAS); + // create first key pair for signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", FIRST_KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=First", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + createAlias(FIRST_KEY_ALIAS); + issueCert( + FIRST_KEY_ALIAS, + "-validity", Integer.toString(VALIDITY)); // create first key pair for signing - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", SECOND_KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Second", - "-validity", Integer.toString(VALIDITY)).shouldHaveExitValue(0); + createAlias(SECOND_KEY_ALIAS); + issueCert( + SECOND_KEY_ALIAS, + "-validity", Integer.toString(VALIDITY)); // sign jar with first key OutputAnalyzer analyzer = ProcessTools.executeCommand(JARSIGNER, diff --git a/test/sun/security/tools/jarsigner/warnings/NotYetValidCertTest.java b/test/sun/security/tools/jarsigner/warnings/NotYetValidCertTest.java index a75c27846fc52238035a25437949b28a2e80861c..4d19c9adc9feea3c7d071069673b99ada9e08e6b 100644 --- a/test/sun/security/tools/jarsigner/warnings/NotYetValidCertTest.java +++ b/test/sun/security/tools/jarsigner/warnings/NotYetValidCertTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,15 +50,11 @@ public class NotYetValidCertTest extends Test { JarUtils.createJar(UNSIGNED_JARFILE, FIRST_FILE); // create certificate that will be valid only tomorrow - ProcessTools.executeCommand(KEYTOOL, - "-genkey", - "-alias", KEY_ALIAS, - "-keyalg", KEY_ALG, - "-keysize", Integer.toString(KEY_SIZE), - "-keystore", KEYSTORE, - "-storepass", PASSWORD, - "-keypass", PASSWORD, - "-dname", "CN=Test", + createAlias(CA_KEY_ALIAS); + createAlias(KEY_ALIAS); + + issueCert( + KEY_ALIAS, "-startdate", "+1d", "-validity", Integer.toString(VALIDITY)); diff --git a/test/sun/security/tools/jarsigner/warnings/Test.java b/test/sun/security/tools/jarsigner/warnings/Test.java index 5688d6126cfe4a367bf4657f06b0a06ef96ab64f..939b904abfffe5ce4a269f0eafbf557f6b8c8081 100644 --- a/test/sun/security/tools/jarsigner/warnings/Test.java +++ b/test/sun/security/tools/jarsigner/warnings/Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,6 @@ public abstract class Test { static final String FIRST_FILE = "first.txt"; static final String SECOND_FILE = "second.txt"; static final String PASSWORD = "password"; - static final String BOTH_KEYS_KEYSTORE = "both_keys.jks"; static final String FIRST_KEY_KEYSTORE = "first_key.jks"; static final String KEYSTORE = "keystore.jks"; static final String FIRST_KEY_ALIAS = "first"; @@ -55,11 +54,13 @@ public abstract class Test { static final String CERT_REQUEST_FILENAME = "test.req"; static final String CERT_FILENAME = "test.crt"; static final String CA_KEY_ALIAS = "ca"; + static final String CA2_KEY_ALIAS = "ca2"; static final int KEY_SIZE = 2048; static final int TIMEOUT = 6 * 60 * 1000; // in millis static final int VALIDITY = 365; static final String WARNING = "Warning:"; + static final String WARNING_OR_ERROR = "(Warning|Error):"; static final String CHAIN_NOT_VALIDATED_VERIFYING_WARNING = "This jar contains entries " @@ -126,10 +127,10 @@ public abstract class Test { + "(%1$tY-%1$tm-%1$td) or after any future revocation date."; static final String NO_TIMESTAMP_VERIFYING_WARN_TEMPLATE - = "This jar contains signatures that does not include a timestamp. " + = "This jar contains signatures that do not include a timestamp. " + "Without a timestamp, users may not be able to validate this jar " - + "after the signer certificate's expiration date " - + "(%1$tY-%1$tm-%1$td) or after any future revocation date."; + + "after any of the signer certificates expire " + + "(as early as %1$tY-%1$tm-%1$td)."; static final String NOT_YET_VALID_CERT_SIGNING_WARNING = "The signer certificate is not yet valid."; @@ -154,14 +155,72 @@ public abstract class Test { static final int ALIAS_NOT_IN_STORE_EXIT_CODE = 32; static final int NOT_SIGNED_BY_ALIAS_EXIT_CODE = 32; + protected void createAlias(String alias, String ... options) + throws Throwable { + List cmd = new ArrayList<>(); + cmd.addAll(Arrays.asList( + "-genkeypair", + "-alias", alias, + "-keyalg", KEY_ALG, + "-keysize", Integer.toString(KEY_SIZE), + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-dname", "CN=" + alias)); + cmd.addAll(Arrays.asList(options)); + + keytool(cmd.toArray(new String[cmd.size()])) + .shouldHaveExitValue(0); + } + + protected void issueCert(String alias, String ... options) + throws Throwable { + keytool("-certreq", + "-alias", alias, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-file", alias + ".req") + .shouldHaveExitValue(0); + + List cmd = new ArrayList<>(); + cmd.addAll(Arrays.asList( + "-gencert", + "-alias", CA_KEY_ALIAS, + "-infile", alias + ".req", + "-outfile", alias + ".cert", + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-file", alias + ".req")); + cmd.addAll(Arrays.asList(options)); + + keytool(cmd.toArray(new String[cmd.size()])) + .shouldHaveExitValue(0); + + keytool("-importcert", + "-alias", alias, + "-keystore", KEYSTORE, + "-storepass", PASSWORD, + "-keypass", PASSWORD, + "-file", alias + ".cert") + .shouldHaveExitValue(0); + } + protected void checkVerifying(OutputAnalyzer analyzer, int expectedExitCode, String... warnings) { analyzer.shouldHaveExitValue(expectedExitCode); + int count = 0; for (String warning : warnings) { - analyzer.shouldContain(warning); + if (warning.startsWith("!")) { + analyzer.shouldNotContain(warning.substring(1)); + } else { + count++; + analyzer.shouldContain(warning); + } } - if (warnings.length > 0) { - analyzer.shouldContain(WARNING); + if (count > 0) { + analyzer.shouldMatch(WARNING_OR_ERROR); } if (expectedExitCode == 0) { analyzer.shouldContain(JAR_VERIFIED); @@ -172,11 +231,17 @@ public abstract class Test { protected void checkSigning(OutputAnalyzer analyzer, String... warnings) { analyzer.shouldHaveExitValue(0); + int count = 0; for (String warning : warnings) { - analyzer.shouldContain(warning); + if (warning.startsWith("!")) { + analyzer.shouldNotContain(warning.substring(1)); + } else { + count++; + analyzer.shouldContain(warning); + } } - if (warnings.length > 0) { - analyzer.shouldContain(WARNING); + if (count > 0) { + analyzer.shouldMatch(WARNING_OR_ERROR); } analyzer.shouldContain(JAR_SIGNED); } diff --git a/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.jks.base64 b/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.jks.base64 deleted file mode 100644 index 756d5ffcb60be8e9ed5b942c6ee12665e65ce370..0000000000000000000000000000000000000000 --- a/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.jks.base64 +++ /dev/null @@ -1,26 +0,0 @@ -/u3+7QAAAAIAAAABAAAAAQAFYWxpYXMAAAFBpkwW0gAAAr0wggK5MA4GCisGAQQB -KgIRAQEFAASCAqWkGJ3PPjYmWNKrV23Y1u413RMAkrRZ+1OLWYRcQt4jtxtIyEH5 -Ho5b9dy9XN9FBKlTOD4c2Pc1T43BLKXeuLu3uLLeIxgXFt0z9CLyGwdYZZ751kXr -DQ99qY6aNQUO6SeE4Wdty0KPAqid6ZJ8bF7T6wsTZSvNhaBRzyFydEfG7bbUYjOl -mWC44nlsu6VEU3o9RQpcm1gIMwradOaIVT/HoB2bKmAv8gHqI6kreiEZwTdZkSAI -IRi2vt1RPllXt5hgjDxUfZe8XOYYweR4Vt2/jVuKLJ80DNTu/9SeUD88zQAz53k4 -r3nRhv6TRcPm6tV/Fh92XLHiskL+TAzTfm+bUAudPCCVxN+yRtxvAgA+UhdV/SuM -Zn5F6nrmP+YJG1hmprgCJIJJaCEXa9RXYC+vIVpO0WVNRuGlGm+/1afnOuQC8Wss -ShXwjkaqTwAhqBFq7eYmmP8BK3gflYrt2zDLXvhl4ndVvMhMthFJ3ZvLh2LWpqLI -/n8EMCf8US3lIEFk9DTHBZjffiHkqK2e7+FXEpG3xrgE6ZYLMdbd5Pb3YjZfhQx+ -ZTtiEFzYSaEGhacek/m7dRq1qmwgFsytng2OdWZe2ln8LJY0odr1dGUfJHfgafvi -tlfbkg/rgjONtwliChDggbkUwnerrj/D/zrdEufUvfyltSshhHXRNDD3fH6spmEk -hHKgxEc4yvxqJxzdMGtuib355aSfNegyl+GsnsKzXQCVEK2h3BLTQObzaD+8NZ12 -LQHvbrCiaS34vxJ3rEC+a+SW7itZp0aCdXMWdMJNkRKqyLBD3vG3zN05sN3XrhEM -8BRT020TWY00tbVFbbBFheYLQRgTjrQtr0Yt6UHWBZc4N20crDLcSH5gqcCOVpla -1Y2uqFEn8yqrGRwn/kgfNgAAAAEABVguNTA5AAABtTCCAbEwggEaoAMCAQICCQDH -cEuVvzCuqzANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDDARUZXN0MB4XDTEzMTAx -MTA2NTUwNloXDTIzMTAwOTA2NTUwNlowDzENMAsGA1UEAwwEVGVzdDCBnzANBgkq -hkiG9w0BAQEFAAOBjQAwgYkCgYEA8hOfp2Dcnvt//ZZQAja9TRiwKqXVS+TiYE3S -gngCBjIi+YYdo0DsUeO5MBfE6uvCWOr5lwAR/u1iaJOhIoGJDiGoPasZlt+yIgtR -LzA7j2q+1q6kcwiVxfikI3aUgHV/QsybTriT4Bf7TQNKtJG23MQa4sD7+PjtCWD7 -p3cHTfkCAwEAAaMVMBMwEQYJYIZIAYb4QgEBBAQDAgeAMA0GCSqGSIb3DQEBBQUA -A4GBAKoDlTJ8wLRA7G8XdGm4gv733n1cSQzlkcsjfOO6/mA5Jvu8tyFNq9HTf9AT -VXbrbGcUYJjhzSSY3w5apXK1kXyqTB1LUNEJ45WnmciqSSecVTpJz9TuegyoX0Zf -HScSgqfDmjqoiiFiNCgn3ZEJ85ykGvoFYGH+php+BVi3S0bj5E/jRpyV3vNnii/S -wJDSAXF6bYU= diff --git a/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.sh b/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.sh deleted file mode 100644 index 49fe91ee06b2ee1c0a1467d143692360bed8e05b..0000000000000000000000000000000000000000 --- a/test/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.sh +++ /dev/null @@ -1,48 +0,0 @@ -# -# Copyright (c) 2013, 2015, 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. -# - -#!/bin/sh - -# This script creates JKS keystore with a certificate -# that contains Netscape Certificate Type extension -# that does not allow code signing -# The keystore is used by BadNetscapeCertTypeTest.java test - -rm -rf keystore.jks -echo "nsCertType = client" > ext.cfg - -openssl req -new -out cert.req -keyout key.pem -days 3650 \ - -passin pass:password -passout pass:password -subj "/CN=Test" -openssl x509 -in cert.req -out cert.pem -req -signkey key.pem -days 3650 \ - -passin pass:password -extfile ext.cfg -openssl pkcs12 -export -in cert.pem -inkey key.pem -out keystore.p12 \ - -passin pass:password -passout pass:password -name alias - -${JAVA_HOME}/bin/keytool -importkeystore \ - -srckeystore keystore.p12 -srcstoretype pkcs12 \ - -srcstorepass password -alias alias \ - -destkeystore bad_netscape_cert_type.jks -deststoretype jks \ - -deststorepass password -destalias alias \ - -openssl base64 < bad_netscape_cert_type.jks > bad_netscape_cert_type.jks.base64 -rm -rf cert.req key.pem cert.pem keystore.p12 ext.cfg bad_netscape_cert_type.jks