diff -u new/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java new/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java --- new/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java 2016-09-10 07:21:03.579659700 +0800 +++ new/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java 2016-10-19 15:18:20.000000000 +0800 @@ -52,6 +52,7 @@ import jdk.security.jarsigner.JarSignerException; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; +import sun.security.timestamp.TimestampToken; import sun.security.tools.KeyStoreUtil; import sun.security.x509.*; import sun.security.util.*; @@ -89,6 +90,20 @@ private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds + // PROPERTY_CERTPATH_DISABLED_ALGS is currently more restrictive than + // PROPERTY_JAR_DISABLED_ALGS and we used it at signing time. + private static final DisabledAlgorithmConstraints VERIFY_CHECK = + new DisabledAlgorithmConstraints( + DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); + private static final DisabledAlgorithmConstraints SIGN_CHECK = + new DisabledAlgorithmConstraints( + DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); + + private static final Set DIGEST_PRIMITIVE_SET = Collections + .unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST)); + 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 { @@ -165,6 +180,8 @@ private Throwable chainNotValidatedReason = null; + private boolean seeWeak = false; + CertificateFactory certificateFactory; CertPathValidator validator; PKIXParameters pkixParameters; @@ -631,7 +648,9 @@ boolean anySigned = false; // if there exists entry inside jar signed JarFile jf = null; Map digestMap = new HashMap<>(); - Map sigMap = new HashMap<>(); + Map sigMap = new HashMap<>(); + Map sigNameMap = new HashMap<>(); + Map unparsableSignatures = new HashMap<>(); try { jf = new JarFile(jarName, true); @@ -648,21 +667,32 @@ && SignatureFileVerifier.isBlockOrSF(name)) { String alias = name.substring(name.lastIndexOf('/') + 1, name.lastIndexOf('.')); - if (name.endsWith(".SF")) { - Manifest sf = new Manifest(is); - for (Object obj :sf.getMainAttributes().keySet()) { - String key = obj.toString(); - if (key.endsWith("-Digest-Manifest")) { - digestMap.put(alias, - key.substring(0, key.length()-16)); + try { + if (name.endsWith(".SF")) { + Manifest sf = new Manifest(is); + boolean found = false; + for (Object obj : sf.getMainAttributes().keySet()) { + String key = obj.toString(); + if (key.endsWith("-Digest-Manifest")) { + digestMap.put(alias, + key.substring(0, key.length() - 16)); + found = true; + break; + } } + if (!found) { + unparsableSignatures.putIfAbsent(alias, + String.format( + rb.getString("history.unparsable"), + name)); + } + } else { + sigNameMap.put(alias, name); + sigMap.put(alias, new PKCS7(is)); } - } else { - PKCS7 p7 = new PKCS7(is); - StringBuilder sb = new StringBuilder(); - for (SignerInfo si : p7.getSignerInfos()) { - sigMap.put(alias, si); - } + } catch (IOException ioe) { + unparsableSignatures.putIfAbsent(alias, String.format( + rb.getString("history.unparsable"), name)); } } else { while (is.read(buffer, 0, buffer.length) != -1) { @@ -826,22 +856,11 @@ System.out.println(rb.getString( ".X.not.signed.by.specified.alias.es.")); } - System.out.println(); - for (String s: digestMap.keySet()) { - SignerInfo si = sigMap.get(s); - String alg = AlgorithmId.makeSigAlg( - si.getDigestAlgorithmId().getName(), - si.getDigestEncryptionAlgorithmId().getName()); - Timestamp ts = si.getTimestamp(); - System.out.println("Signed with " + digestMap.get(s) - + " and " + alg + " by " + si.getIssuerName() - + (ts == null ? "" : - (" at " + si.getTimestamp().getTimestamp()))); - } - System.out.println(); } - if (man == null) + if (man == null) { + System.out.println(); System.out.println(rb.getString("no.manifest.")); + } // If signer is a trusted cert or private entry in user's own // keystore, it can be self-signed. @@ -849,8 +868,99 @@ signerSelfSigned = false; } + // Even if the verbose option is not specified, all out strings + // must be generated so seeWeak can be updated. + if (!digestMap.isEmpty() + || !sigMap.isEmpty() + || !unparsableSignatures.isEmpty()) { + if (verbose != null) { + System.out.println(); + } + for (String s : sigMap.keySet()) { + if (!digestMap.containsKey(s)) { + unparsableSignatures.putIfAbsent(s, String.format( + rb.getString("history.nosf"), s)); + } + } + for (String s : digestMap.keySet()) { + PKCS7 p7 = sigMap.get(s); + if (p7 != null) { + String history; + try { + SignerInfo si = p7.getSignerInfos()[0]; + X509Certificate signer = si.getCertificate(p7); + String digestAlg = digestMap.get(s); + String sigAlg = AlgorithmId.makeSigAlg( + si.getDigestAlgorithmId().getName(), + si.getDigestEncryptionAlgorithmId().getName()); + PublicKey key = signer.getPublicKey(); + PKCS7 tsToken = si.getTsToken(); + if (tsToken != null) { + SignerInfo tsSi = tsToken.getSignerInfos()[0]; + X509Certificate tsSigner = tsSi.getCertificate(tsToken); + byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); + TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo); + PublicKey tsKey = tsSigner.getPublicKey(); + String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName(); + String tsSigAlg = AlgorithmId.makeSigAlg( + tsSi.getDigestAlgorithmId().getName(), + tsSi.getDigestEncryptionAlgorithmId().getName()); + Calendar c = Calendar.getInstance( + TimeZone.getTimeZone("UTC"), + Locale.getDefault(Locale.Category.FORMAT)); + c.setTime(tsTokenInfo.getDate()); + history = String.format( + rb.getString("history.with.ts"), + signer.getSubjectX500Principal(), + withWeak(digestAlg, DIGEST_PRIMITIVE_SET), + withWeak(sigAlg, SIG_PRIMITIVE_SET), + withWeak(key), + c, + tsSigner.getSubjectX500Principal(), + withWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET), + withWeak(tsSigAlg, SIG_PRIMITIVE_SET), + withWeak(tsKey)); + } else { + history = String.format( + rb.getString("history.without.ts"), + signer.getSubjectX500Principal(), + withWeak(digestAlg, DIGEST_PRIMITIVE_SET), + withWeak(sigAlg, SIG_PRIMITIVE_SET), + withWeak(key)); + } + } catch (Exception e) { + // The only usage of sigNameMap, remember the name + // of the block file if it's invalid. + history = String.format( + rb.getString("history.unparsable"), + sigNameMap.get(s)); + } + if (verbose != null) { + System.out.println(history); + } + } else { + unparsableSignatures.putIfAbsent(s, String.format( + rb.getString("history.nobk"), s)); + } + } + if (verbose != null) { + for (String s : unparsableSignatures.keySet()) { + System.out.println(unparsableSignatures.get(s)); + } + } + } + System.out.println(); if (!anySigned) { - if (hasSignature) { + if (seeWeak) { + if (verbose != null) { + System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose")); + System.out.println("\n " + + DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS + + "=" + Security.getProperty(DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS)); + } else { + System.out.println(rb.getString("jar.treated.unsigned.see.weak")); + } + } else if (hasSignature) { System.out.println(rb.getString("jar.treated.unsigned")); } else { System.out.println(rb.getString("jar.is.unsigned")); @@ -878,7 +988,9 @@ 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. + // Everything is done with JarFile API. The signing + // history (digestMap etc) will show these info and + // print out proper warnings. } if (badKeyUsage) { @@ -969,6 +1081,26 @@ System.exit(1); } + private String withWeak(String alg, Set primitiveSet) { + if (VERIFY_CHECK.permits(primitiveSet, alg, null)) { + return alg; + } else { + seeWeak = true; + return String.format(rb.getString("with.weak"), alg); + } + } + + private String withWeak(PublicKey key) { + if (VERIFY_CHECK.permits(SIG_PRIMITIVE_SET, key)) { + return String.format( + rb.getString("key.bit"), KeyUtil.getKeySize(key)); + } else { + seeWeak = true; + return String.format( + rb.getString("key.bit.weak"), KeyUtil.getKeySize(key)); + } + } + private static MessageFormat validityTimeForm = null; private static MessageFormat notYetTimeForm = null; private static MessageFormat expiredTimeForm = null; @@ -1158,22 +1290,22 @@ void signJar(String jarName, String alias) throws Exception { - DisabledAlgorithmConstraints dac = - new DisabledAlgorithmConstraints( - DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); - - if (digestalg != null && !dac.permits( - Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), digestalg, null)) { + if (digestalg != null && !SIGN_CHECK.permits( + DIGEST_PRIMITIVE_SET, digestalg, null)) { weakAlg |= 1; } - if (tSADigestAlg != null && !dac.permits( - Collections.singleton(CryptoPrimitive.MESSAGE_DIGEST), tSADigestAlg, null)) { + if (tSADigestAlg != null && !SIGN_CHECK.permits( + DIGEST_PRIMITIVE_SET, tSADigestAlg, null)) { weakAlg |= 4; } - if (sigalg != null && !dac.permits( - Collections.singleton(CryptoPrimitive.SIGNATURE), sigalg, null)) { + if (sigalg != null && !SIGN_CHECK.permits( + SIG_PRIMITIVE_SET , sigalg, null)) { weakAlg |= 2; } + if (!SIGN_CHECK.permits( + SIG_PRIMITIVE_SET, privateKey)) { + weakAlg |= 8; + } boolean aliasUsed = false; X509Certificate tsaCert = null; @@ -1418,6 +1550,11 @@ rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."), tSADigestAlg, "-tsadigestalg")); } + if ((weakAlg & 8) == 8) { + System.out.println(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 { System.out.println(rb.getString("jar.signed.")); } diff -u new/test/sun/security/tools/jarsigner/TimestampCheck.java new/test/sun/security/tools/jarsigner/TimestampCheck.java --- new/test/sun/security/tools/jarsigner/TimestampCheck.java 2016-09-10 07:21:12.232900000 +0800 +++ new/test/sun/security/tools/jarsigner/TimestampCheck.java 2016-10-19 15:18:26.000000000 +0800 @@ -22,25 +22,29 @@ */ import com.sun.net.httpserver.*; -import java.io.BufferedReader; import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.math.BigInteger; import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.Locale; +import jdk.testlibrary.*; +import jdk.testlibrary.JarUtils; import sun.security.pkcs.ContentInfo; import sun.security.pkcs.PKCS7; import sun.security.pkcs.PKCS9Attribute; @@ -52,11 +56,22 @@ import sun.security.x509.AlgorithmId; import sun.security.x509.X500Name; +/* + * @test + * @bug 6543842 6543440 6939248 8009636 8024302 8163304 + * @summary checking response of timestamp + * @modules java.base/sun.security.pkcs + * java.base/sun.security.timestamp + * java.base/sun.security.x509 + * java.base/sun.security.util + * java.base/sun.security.tools.keytool + * @library /lib/testlibrary + * @run main/timeout=600 TimestampCheck + */ public class TimestampCheck { - static final String TSKS = "tsks"; - static final String JAR = "old.jar"; - static final String defaultPolicyId = "2.3.4.5"; + static final String defaultPolicyId = "2.3.4"; + static String host = null; static class Handler implements HttpHandler, AutoCloseable { @@ -75,11 +90,7 @@ t.getRequestBody().read(input); try { - int path = 0; - if (t.getRequestURI().getPath().length() > 1) { - path = Integer.parseInt( - t.getRequestURI().getPath().substring(1)); - } + String path = t.getRequestURI().getPath().substring(1); byte[] output = sign(input, path); Headers out = t.getResponseHeaders(); out.set("Content-Type", "application/timestamp-reply"); @@ -97,24 +108,10 @@ /** * @param input The data to sign * @param path different cases to simulate, impl on URL path - * 0: normal - * 1: Missing nonce - * 2: Different nonce - * 3: Bad digets octets in messageImprint - * 4: Different algorithmId in messageImprint - * 5: whole chain in cert set - * 6: extension is missing - * 7: extension is non-critical - * 8: extension does not have timestamping - * 9: no cert in response - * 10: normal - * 11: always return default policy id - * 12: normal - * otherwise: normal * @returns the signed */ - byte[] sign(byte[] input, int path) throws Exception { - // Read TSRequest + byte[] sign(byte[] input, String path) throws Exception { + DerValue value = new DerValue(input); System.err.println("\nIncoming Request\n==================="); System.err.println("Version: " + value.data.getInteger()); @@ -138,19 +135,16 @@ } } - // Write TSResponse System.err.println("\nResponse\n==================="); - KeyStore ks = KeyStore.getInstance("JKS"); - try (FileInputStream fis = new FileInputStream(keystore)) { - ks.load(fis, "changeit".toCharArray()); - } + KeyStore ks = KeyStore.getInstance( + new File(keystore), "changeit".toCharArray()); String alias = "ts"; - if (path == 6) alias = "tsbad1"; - if (path == 7) alias = "tsbad2"; - if (path == 8) alias = "tsbad3"; + if (path.startsWith("bad") || path.equals("weak")) { + alias = "ts" + path; + } - if (path == 11) { + if (path.equals("diffpolicy")) { policyId = new ObjectIdentifier(defaultPolicyId); } @@ -159,14 +153,15 @@ AlgorithmId[] algorithms = {aid}; Certificate[] chain = ks.getCertificateChain(alias); - X509Certificate[] signerCertificateChain = null; + X509Certificate[] signerCertificateChain; X509Certificate signer = (X509Certificate)chain[0]; - if (path == 5) { // Only case 5 uses full chain + + if (path.equals("fullchain")) { // Only case 5 uses full chain signerCertificateChain = new X509Certificate[chain.length]; for (int i=0; i extra) + throws Throwable { + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner") + .addVMArg("-Duser.language=en") + .addVMArg("-Duser.country=US") + .addToolArg("-keystore") + .addToolArg("tsks") + .addToolArg("-storepass") + .addToolArg("changeit"); + for (String s : extra) { + if (s.startsWith("-J")) { + launcher.addVMArg(s.substring(2)); + } else { + launcher.addToolArg(s); + } + } + return ProcessTools.executeCommand(launcher.getCommand()); + } + + static OutputAnalyzer verify(String file, String... extra) + throws Throwable { + List args = new ArrayList<>(); + args.add("-verify"); + args.add(file); + args.addAll(Arrays.asList(extra)); + return jarsigner(args); + } + + static void checkBadKU(String file) throws Throwable { + verify(file) + .shouldHaveExitValue(0) + .shouldContain("treated as unsigned") + .shouldContain("re-run jarsigner with debug enabled"); + verify(file, "-verbose") + .shouldHaveExitValue(0) + .shouldContain("Signed by") + .shouldContain("treated as unsigned") + .shouldContain("re-run jarsigner with debug enabled"); + verify(file, "-J-Djava.security.debug=jar") + .shouldHaveExitValue(0) + .shouldContain("SignatureException: Key usage restricted") + .shouldContain("treated as unsigned") + .shouldContain("re-run jarsigner with debug enabled"); + } + + static void checkWeak(String file) throws Throwable { + verify(file) + .shouldHaveExitValue(0) + .shouldContain("treated as unsigned") + .shouldMatch("weak algorithm that is now disabled.") + .shouldMatch("Re-run jarsigner with the -verbose option for more details"); + verify(file, "-verbose") + .shouldHaveExitValue(0) + .shouldContain("treated as unsigned") + .shouldMatch("weak algorithm that is now disabled by") + .shouldMatch("Digest algorithm: .*weak") + .shouldMatch("Signature algorithm: .*weak") + .shouldMatch("Timestamp digest algorithm: .*weak") + .shouldNotMatch("Timestamp signature algorithm: .*weak.*weak") + .shouldMatch("Timestamp signature algorithm: .*key.*weak"); + verify(file, "-J-Djava.security.debug=jar") + .shouldHaveExitValue(0) + .shouldMatch("SignatureException:.*Disabled"); + } + static void checkTimestamp(String file, String policyId, String digestAlg) throws Exception { try (JarFile jf = new JarFile(file)) { @@ -366,40 +486,61 @@ } + static int which = 0; + /** - * @param cmd the command line (with a hole to plug in) - * @param path the path in the URL, i.e, http://localhost/path - * @param expected if this command should succeed + * @param extra more args given to jarsigner */ - static void jarsigner(String cmd, int path, boolean expected) - throws Exception { - System.err.println("Test " + path); - Process p = Runtime.getRuntime().exec(String.format(Locale.ROOT,cmd, path, path)); - BufferedReader reader = new BufferedReader( - new InputStreamReader(p.getErrorStream())); - while (true) { - String s = reader.readLine(); - if (s == null) break; - System.err.println(s); + static OutputAnalyzer sign(String path, String... extra) + throws Throwable { + which++; + System.err.println("\n>> Test #" + which + ": " + Arrays.toString(extra)); + List args = List.of("-J-Djava.security.egd=file:/dev/./urandom", + "-debug", "-signedjar", path + ".jar", "old.jar", + path.equals("badku") ? "badku" : "old"); + args = new ArrayList<>(args); + if (!path.equals("none") && !path.equals("badku")) { + args.add("-tsa"); + args.add(host + path); } + args.addAll(Arrays.asList(extra)); + return jarsigner(args); + } + + static void prepare() throws Exception { + jdk.testlibrary.JarUtils.createJar("old.jar", "A"); + Files.deleteIfExists(Paths.get("tsks")); + keytool("-alias ca -genkeypair -ext bc -dname CN=CA"); + keytool("-alias old -genkeypair -dname CN=old"); + keytool("-alias badku -genkeypair -dname CN=badku"); + keytool("-alias ts -genkeypair -dname CN=ts"); + keytool("-alias tsweak -genkeypair -keysize 512 -dname CN=tsbad1"); + keytool("-alias tsbad1 -genkeypair -dname CN=tsbad1"); + keytool("-alias tsbad2 -genkeypair -dname CN=tsbad2"); + keytool("-alias tsbad3 -genkeypair -dname CN=tsbad3"); + + gencert("old"); + gencert("badku", "-ext ku:critical=keyAgreement"); + gencert("ts", "-ext eku:critical=ts"); + gencert("tsweak", "-ext eku:critical=ts"); + gencert("tsbad1"); + gencert("tsbad2", "-ext eku=ts"); + gencert("tsbad3", "-ext eku:critical=cs"); + } - // Will not see noTimestamp warning - boolean seeWarning = false; - reader = new BufferedReader( - new InputStreamReader(p.getInputStream())); - while (true) { - String s = reader.readLine(); - if (s == null) break; - System.err.println(s); - if (s.indexOf("Warning:") >= 0) { - seeWarning = true; - } - } - int result = p.waitFor(); - if (expected && result != 0 || !expected && result == 0) { - throw new Exception("Failed"); - } - if (seeWarning) { - throw new Exception("See warning"); + static void gencert(String alias, String... extra) throws Exception { + keytool("-alias " + alias + " -certreq -file " + alias + ".req"); + String genCmd = "-gencert -alias ca -infile " + + alias + ".req -outfile " + alias + ".cert"; + for (String s : extra) { + genCmd += " " + s; } + keytool(genCmd); + keytool("-alias " + alias + " -importcert -file " + alias + ".cert"); + } + + static void keytool(String cmd) throws Exception { + cmd = "-keystore tsks -storepass changeit -keypass changeit " + + "-keyalg rsa -validity 200 " + cmd; + sun.security.tools.keytool.Main.main(cmd.split(" ")); } } only in patch2: unchanged: --- old/src/java.base/share/classes/module-info.java 2016-10-19 15:18:15.000000000 +0800 +++ new/src/java.base/share/classes/module-info.java 2016-10-19 15:18:15.000000000 +0800 @@ -265,6 +265,8 @@ jdk.crypto.pkcs11; exports sun.security.ssl to java.security.jgss; + exports sun.security.timestamp to + jdk.jartool; exports sun.security.tools to jdk.jartool; exports sun.security.util to only in patch2: unchanged: --- old/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java 2016-10-19 15:18:17.000000000 +0800 +++ new/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java 2016-10-19 15:18:17.000000000 +0800 @@ -498,6 +498,23 @@ return unauthenticatedAttributes; } + /** + * Returns the timestamp PKCS7 data unverified. + * @return a PKCS7 object + */ + public PKCS7 getTsToken() throws IOException { + if (unauthenticatedAttributes == null) { + return null; + } + PKCS9Attribute tsTokenAttr = + unauthenticatedAttributes.getAttribute( + PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID); + if (tsTokenAttr == null) { + return null; + } + return new PKCS7((byte[])tsTokenAttr.getValue()); + } + /* * Extracts a timestamp from a PKCS7 SignerInfo. * @@ -525,19 +542,12 @@ if (timestamp != null || !hasTimestamp) return timestamp; - if (unauthenticatedAttributes == null) { - hasTimestamp = false; - return null; - } - PKCS9Attribute tsTokenAttr = - unauthenticatedAttributes.getAttribute( - PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID); - if (tsTokenAttr == null) { + PKCS7 tsToken = getTsToken(); + if (tsToken == null) { hasTimestamp = false; return null; } - PKCS7 tsToken = new PKCS7((byte[])tsTokenAttr.getValue()); // Extract the content (an encoded timestamp token info) byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); // Extract the signer (the Timestamping Authority) only in patch2: unchanged: --- old/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java 2016-10-19 15:18:24.000000000 +0800 +++ new/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Resources.java 2016-10-19 15:18:23.000000000 +0800 @@ -145,11 +145,26 @@ {"jar.is.unsigned", "jar is unsigned."}, {"jar.treated.unsigned", - "Signature not parsable or verifiable. The jar will be treated as unsigned. The jar may have been signed with a weak algorithm that is now disabled. For more information, rerun jarsigner with debug enabled (-J-Djava.security.debug=jar)."}, + "WARNING: Signature is either not parsable or not verifiable, and the jar will be treated as unsigned. For more information, re-run jarsigner with debug enabled (-J-Djava.security.debug=jar)."}, + {"jar.treated.unsigned.see.weak", + "The jar will be treated as unsigned, because it is signed with a weak algorithm that is now disabled.\n\nRe-run jarsigner with the -verbose option for more details."}, + {"jar.treated.unsigned.see.weak.verbose", + "WARNING: The jar will be treated as unsigned, because it is signed with a weak algorithm that is now disabled by the security property:"}, {"jar.signed.", "jar signed."}, {"jar.signed.with.signer.errors.", "jar signed, with signer errors."}, {"jar.verified.", "jar verified."}, {"jar.verified.with.signer.errors.", "jar verified, with signer errors."}, + + {"history.with.ts", "- Signed by \"%1$s\"\n Digest algorithm: %2$s\n Signature algorithm: %3$s, %4$s\n Timestamped by \"%6$s\" on %5$tc\n Timestamp digest algorithm: %7$s\n Timestamp signature algorithm: %8$s, %9$s"}, + {"history.without.ts", "- Signed by \"%1$s\"\n Digest algorithm: %2$s\n Signature algorithm: %3$s, %4$s"}, + {"history.unparsable", "- Unparsable signature-related file %s"}, + {"history.nosf", "- Missing signature-related file META-INF/%s.SF"}, + {"history.nobk", "- Missing block file for signature-related file META-INF/%s.SF"}, + + {"with.weak", "%s (weak)"}, + {"key.bit", "%d-bit key"}, + {"key.bit.weak", "%d-bit key (weak)"}, + {"jarsigner.", "jarsigner: "}, {"signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.", "signature filename must consist of the following characters: A-Z, 0-9, _ or -"}, @@ -248,6 +263,8 @@ "The signer's certificate is self-signed."}, {"The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.", "The %1$s algorithm specified for the %2$s option is considered a security risk."}, + {"The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk.", + "The %s signing key has a keysize of %d which is considered a security risk."}, {"This.jar.contains.entries.whose.certificate.chain.is.not.validated.reason.1", "This jar contains entries whose certificate chain is not validated. Reason: %s"}, {"no.timestamp.signing", only in patch2: unchanged: --- old/test/lib/testlibrary/jdk/testlibrary/JarUtils.java 2016-10-19 15:18:25.000000000 +0800 +++ new/test/lib/testlibrary/jdk/testlibrary/JarUtils.java 2016-10-19 15:18:25.000000000 +0800 @@ -24,6 +24,7 @@ package jdk.testlibrary; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -40,7 +41,8 @@ public final class JarUtils { /** - * Create jar file with specified files. + * Create jar file with specified files. If a specified file does not exist, + * a new jar entry will be created with the file name itself the content. */ public static void createJar(String dest, String... files) throws IOException { @@ -54,6 +56,8 @@ jos.putNextEntry(new JarEntry(file)); try (FileInputStream fis = new FileInputStream(file)) { fis.transferTo(jos); + } catch (FileNotFoundException e) { + jos.write(file.getBytes()); } } } @@ -61,7 +65,17 @@ } /** - * Add specified files to existing jar file. + * Add or remove specified files to existing jar file. If a specified file + * to be updated or added does not exist, the jar entry will be created + * with the file name itself the content. + * + * @param src the original jar file name + * @param dest the new jar file name + * @param files the files to update. The list is broken into 2 groups + * by a "-" string. The files before in the 1st group will + * be either updated or added. The files in the 2nd group + * will be removed. If no "-" exists, all files belong to + * the 1st group. */ public static void updateJar(String src, String dest, String... files) throws IOException { @@ -77,8 +91,11 @@ JarEntry entry = entries.nextElement(); String name = entry.getName(); boolean found = false; + boolean update = true; for (String file : files) { - if (name.equals(file)) { + if (file.equals("-")) { + update = false; + } else if (name.equals(file)) { updatedFiles.add(file); found = true; break; @@ -86,11 +103,18 @@ } if (found) { - System.out.println(String.format("Updating %s with %s", - dest, name)); - jos.putNextEntry(new JarEntry(name)); - try (FileInputStream fis = new FileInputStream(name)) { - fis.transferTo(jos); + if (update) { + System.out.println(String.format("Updating %s with %s", + dest, name)); + jos.putNextEntry(new JarEntry(name)); + try (FileInputStream fis = new FileInputStream(name)) { + fis.transferTo(jos); + } catch (FileNotFoundException e) { + jos.write(name.getBytes()); + } + } else { + System.out.println(String.format("Removing %s from %s", + name, dest)); } } else { System.out.println(String.format("Copying %s to %s", @@ -103,12 +127,17 @@ // append new files for (String file : files) { + if (file.equals("-")) { + break; + } if (!updatedFiles.contains(file)) { System.out.println(String.format("Adding %s with %s", dest, file)); jos.putNextEntry(new JarEntry(file)); try (FileInputStream fis = new FileInputStream(file)) { fis.transferTo(jos); + } catch (FileNotFoundException e) { + jos.write(file.getBytes()); } } } only in patch2: unchanged: --- old/test/sun/security/tools/jarsigner/ts.sh 2016-10-19 15:18:28.000000000 +0800 +++ /dev/null 2016-10-19 15:18:28.000000000 +0800 @@ -1,103 +0,0 @@ -# -# Copyright (c) 2007, 2013, 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 6543842 6543440 6939248 8009636 8024302 -# @summary checking response of timestamp -# @modules java.base/sun.security.pkcs -# java.base/sun.security.timestamp -# java.base/sun.security.x509 -# java.base/sun.security.util -# -# @run shell/timeout=600 ts.sh - -# Run for a long time because jarsigner with timestamp needs to create a -# 64-bit random number and it might be extremely slow on a machine with -# not enough entropy pool - -# set platform-dependent variables -OS=`uname -s` -case "$OS" in - Windows_* ) - FS="\\" - ;; - * ) - FS="/" - ;; -esac - -if [ "${TESTSRC}" = "" ] ; then - TESTSRC="." -fi -if [ "${TESTJAVA}" = "" ] ; then - JAVAC_CMD=`which javac` - TESTJAVA=`dirname $JAVAC_CMD`/.. -fi - -JAR="${TESTJAVA}${FS}bin${FS}jar ${TESTTOOLVMOPTS}" -JAVA="${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS}" -JAVAC="${TESTJAVA}${FS}bin${FS}javac ${TESTTOOLVMOPTS} ${TESTJAVACOPTS}" -KT="${TESTJAVA}${FS}bin${FS}keytool ${TESTTOOLVMOPTS} -keystore tsks -storepass changeit -keypass changeit -keyalg rsa -validity 200" - -rm tsks -echo Nothing > A -rm old.jar -$JAR cvf old.jar A - -# ca is CA -# old is signer for code -# ts is signer for timestamp -# tsbad1 has no extendedKeyUsage -# tsbad2's extendedKeyUsage is non-critical -# tsbad3's extendedKeyUsage has no timestamping - -$KT -alias ca -genkeypair -ext bc -dname CN=CA -$KT -alias old -genkeypair -dname CN=old -$KT -alias ts -genkeypair -dname CN=ts -$KT -alias tsbad1 -genkeypair -dname CN=tsbad1 -$KT -alias tsbad2 -genkeypair -dname CN=tsbad2 -$KT -alias tsbad3 -genkeypair -dname CN=tsbad3 - -$KT -alias old -certreq | \ - $KT -alias ca -gencert | \ - $KT -alias old -importcert -$KT -alias ts -certreq | \ - $KT -alias ca -gencert -ext eku:critical=ts | \ - $KT -alias ts -importcert -$KT -alias tsbad1 -certreq | \ - $KT -alias ca -gencert | \ - $KT -alias tsbad1 -importcert -$KT -alias tsbad2 -certreq | \ - $KT -alias ca -gencert -ext eku=ts | \ - $KT -alias tsbad2 -importcert -$KT -alias tsbad3 -certreq | \ - $KT -alias ca -gencert -ext eku:critical=cs | \ - $KT -alias tsbad3 -importcert - -EXTRAOPTS="--add-exports java.base/sun.security.pkcs=ALL-UNNAMED \ - --add-exports java.base/sun.security.timestamp=ALL-UNNAMED \ - --add-exports java.base/sun.security.x509=ALL-UNNAMED \ - --add-exports java.base/sun.security.util=ALL-UNNAMED" -$JAVAC ${EXTRAOPTS} -d . ${TESTSRC}/TimestampCheck.java -$JAVA ${TESTVMOPTS} ${EXTRAOPTS} "-Dtest.tool.vm.opts=${TESTTOOLVMOPTS}" TimestampCheck -