8024302: Clarify jar verifications
8023338: Update jarsigner to encourage timestamping
Reviewed-by: mullan, ahgross
--- a/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java Thu Sep 19 08:34:37 2013 -0700
+++ b/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java Thu Sep 19 10:40:16 2013 +0800
@@ -158,8 +158,13 @@
private String altSignerClasspath = null;
private ZipFile zipFile = null;
+ // Informational warnings
+ private boolean hasExpiringCert = false;
+ private boolean noTimestamp = false;
+ private Date expireDate = new Date(0L); // used in noTimestamp warning
+
+ // Severe warnings
private boolean hasExpiredCert = false;
- private boolean hasExpiringCert = false;
private boolean notYetValidCert = false;
private boolean chainNotValidated = false;
private boolean notSignedByAlias = false;
@@ -258,9 +263,6 @@
if (strict) {
int exitCode = 0;
- if (hasExpiringCert) {
- exitCode |= 2;
- }
if (chainNotValidated || hasExpiredCert || notYetValidCert) {
exitCode |= 4;
}
@@ -754,14 +756,25 @@
System.out.println(rb.getString(
"jar.is.unsigned.signatures.missing.or.not.parsable."));
} else {
- System.out.println(rb.getString("jar.verified."));
- if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
- badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
- notYetValidCert || chainNotValidated ||
- aliasNotInStore || notSignedByAlias) {
+ boolean warningAppeared = false;
+ boolean errorAppeared = false;
+ if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
+ notYetValidCert || chainNotValidated || hasExpiredCert ||
+ hasUnsignedEntry ||
+ aliasNotInStore || notSignedByAlias) {
- System.out.println();
- System.out.println(rb.getString("Warning."));
+ 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;
+ }
+
if (badKeyUsage) {
System.out.println(
rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
@@ -785,10 +798,6 @@
System.out.println(rb.getString(
"This.jar.contains.entries.whose.signer.certificate.has.expired."));
}
- if (hasExpiringCert) {
- System.out.println(rb.getString(
- "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
- }
if (notYetValidCert) {
System.out.println(rb.getString(
"This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
@@ -807,10 +816,29 @@
if (aliasNotInStore) {
System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
}
+ } else {
+ System.out.println(rb.getString("jar.verified."));
+ }
+ 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) {
+ System.out.println(
+ String.format(rb.getString("no.timestamp.verifying"), expireDate));
+ }
+ }
+ 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."));
+ "Re.run.with.the.verbose.and.certs.options.for.more.details."));
}
}
}
@@ -870,6 +898,9 @@
try {
boolean printValidity = true;
if (timestamp == null) {
+ if (expireDate.getTime() == 0 || expireDate.after(notAfter)) {
+ expireDate = notAfter;
+ }
x509Cert.checkValidity();
// test if cert will expire within six months
if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) {
@@ -1233,6 +1264,10 @@
tsaCert = getTsaCert(tsaAlias);
}
+ if (tsaUrl == null && tsaCert == null) {
+ noTimestamp = true;
+ }
+
SignatureFile.Block block = null;
try {
@@ -1380,12 +1415,20 @@
}
}
- if (hasExpiredCert || hasExpiringCert || notYetValidCert
- || badKeyUsage || badExtendedKeyUsage
- || badNetscapeCertType || chainNotValidated) {
- System.out.println();
+ boolean warningAppeared = false;
+ if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
+ notYetValidCert || chainNotValidated || hasExpiredCert) {
+ 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;
+ }
- System.out.println(rb.getString("Warning."));
if (badKeyUsage) {
System.out.println(
rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
@@ -1404,9 +1447,6 @@
if (hasExpiredCert) {
System.out.println(
rb.getString("The.signer.certificate.has.expired."));
- } else if (hasExpiringCert) {
- System.out.println(
- rb.getString("The.signer.certificate.will.expire.within.six.months."));
} else if (notYetValidCert) {
System.out.println(
rb.getString("The.signer.certificate.is.not.yet.valid."));
@@ -1416,6 +1456,24 @@
System.out.println(
rb.getString("The.signer.s.certificate.chain.is.not.validated."));
}
+ } 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));
+ }
}
// no IOException thrown in the above try clause, so disable
@@ -1502,6 +1560,7 @@
timestamp = ts.getTimestamp();
} else {
timestamp = null;
+ noTimestamp = true;
}
// display the certificate(s). The first one is end-entity cert and
// its KeyUsage should be checked.
--- a/jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java Thu Sep 19 08:34:37 2013 -0700
+++ b/jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java Thu Sep 19 10:40:16 2013 +0800
@@ -135,7 +135,10 @@
{".Unsigned.entries.", "(Unsigned entries)"},
{"jar.is.unsigned.signatures.missing.or.not.parsable.",
"jar is unsigned. (signatures missing or not parsable)"},
+ {"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."},
{"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 -"},
@@ -193,6 +196,7 @@
"using an alternative signing mechanism"},
{"entry.was.signed.on", "entry was signed on {0}"},
{"Warning.", "Warning: "},
+ {"Error.", "Error: "},
{"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.",
@@ -229,6 +233,10 @@
"The signer's certificate chain is not validated."},
{"This.jar.contains.entries.whose.certificate.chain.is.not.validated.",
"This jar contains entries whose certificate chain is not validated."},
+ {"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."},
+ {"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."},
{"Unknown.password.type.", "Unknown password type: "},
{"Cannot.find.environment.variable.",
"Cannot find environment variable: "},
--- a/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java Thu Sep 19 08:34:37 2013 -0700
+++ b/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java Thu Sep 19 10:40:16 2013 +0800
@@ -239,13 +239,13 @@
" -J-Djava.security.egd=file:/dev/./urandom" +
" -debug -keystore " + TSKS + " -storepass changeit" +
" -tsa http://localhost:" + port + "/%d" +
- " -signedjar new.jar " + JAR + " old";
+ " -signedjar new_%d.jar " + JAR + " old";
} else {
cmd = System.getProperty("java.home") + "/bin/jarsigner" +
" -J-Djava.security.egd=file:/dev/./urandom" +
" -debug -keystore " + TSKS + " -storepass changeit" +
" -tsa http://localhost:" + port + "/%d" +
- " -signedjar new.jar " + JAR + " old";
+ " -signedjar new_%d.jar " + JAR + " old";
}
try {
@@ -280,7 +280,7 @@
static void jarsigner(String cmd, int path, boolean expected)
throws Exception {
System.err.println("Test " + path);
- Process p = Runtime.getRuntime().exec(String.format(cmd, path));
+ Process p = Runtime.getRuntime().exec(String.format(cmd, path, path));
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getErrorStream()));
while (true) {
@@ -288,9 +288,25 @@
if (s == null) break;
System.err.println(s);
}
+
+ // 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");
+ }
}
}
--- a/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh Thu Sep 19 08:34:37 2013 -0700
+++ b/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh Thu Sep 19 10:40:16 2013 +0800
@@ -139,7 +139,6 @@
# 16 and 32 already covered in the first part
# ==========================================================
-$KT -genkeypair -alias expiring -dname CN=expiring -startdate -1m
$KT -genkeypair -alias expired -dname CN=expired -startdate -10m
$KT -genkeypair -alias notyetvalid -dname CN=notyetvalid -startdate +1m
$KT -genkeypair -alias badku -dname CN=badku -ext KU=cRLSign -validity 365
@@ -154,9 +153,6 @@
$KT -importcert -alias badchain
$KT -delete -alias ca
-$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar expiring
-[ $? = 2 ] || exit $LINENO
-
$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar expired
[ $? = 4 ] || exit $LINENO
--- a/jdk/test/sun/security/tools/jarsigner/ts.sh Thu Sep 19 08:34:37 2013 -0700
+++ b/jdk/test/sun/security/tools/jarsigner/ts.sh Thu Sep 19 10:40:16 2013 +0800
@@ -22,7 +22,7 @@
#
# @test
-# @bug 6543842 6543440 6939248 8009636
+# @bug 6543842 6543440 6939248 8009636 8024302
# @summary checking response of timestamp
#
# @run shell/timeout=600 ts.sh
@@ -53,7 +53,7 @@
JAR="${TESTJAVA}${FS}bin${FS}jar"
JAVA="${TESTJAVA}${FS}bin${FS}java"
JAVAC="${TESTJAVA}${FS}bin${FS}javac"
-KT="${TESTJAVA}${FS}bin${FS}keytool -keystore tsks -storepass changeit -keypass changeit"
+KT="${TESTJAVA}${FS}bin${FS}keytool -keystore tsks -storepass changeit -keypass changeit -validity 200"
rm tsks
echo Nothing > A
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/jarsigner/warnings.sh Thu Sep 19 10:40:16 2013 +0800
@@ -0,0 +1,117 @@
+#
+# Copyright (c) 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 8024302
+# @summary Clarify jar verifications
+#
+
+if [ "${TESTJAVA}" = "" ] ; then
+ JAVAC_CMD=`which javac`
+ TESTJAVA=`dirname $JAVAC_CMD`/..
+fi
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+ Windows_* )
+ FS="\\"
+ ;;
+ * )
+ FS="/"
+ ;;
+esac
+
+KS=warnings.jks
+JFILE=warnings.jar
+
+KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit \
+ -keystore $KS"
+JAR=$TESTJAVA${FS}bin${FS}jar
+JARSIGNER="$TESTJAVA${FS}bin${FS}jarsigner -keystore $KS -storepass changeit"
+
+rm $KS 2> /dev/null
+
+export LANG=C
+
+echo 12345 > file
+
+ERR=""
+
+# Normal signer expiring on 2100-01-01
+$KT -alias s1 -dname CN=s1 -genkey -startdate 2000/01/01 -validity 36525 || ERR="$ERR keytool s1,"
+# Cert expiring soon, informational warning
+$KT -alias s2 -dname CN=s2 -genkey -validity 100 || ERR="$ERR keytool s2,"
+# Cert expired, severe warning
+$KT -alias s3 -dname CN=s3 -genkey -startdate -200d -validity 100 || ERR="$ERR keytool s3,"
+
+# noTimestamp is informatiional warning and includes a date
+$JAR cvf $JFILE file
+$JARSIGNER $JFILE s1 > output1 || ERR="$ERR jarsigner s1,"
+$JARSIGNER -strict $JFILE s1 >> output1 || ERR="$ERR jarsigner s1 strict,"
+$JARSIGNER -verify $JFILE s1 >> output1 || ERR="$ERR jarsigner s1,"
+$JARSIGNER -verify -strict $JFILE s1 >> output1 || ERR="$ERR jarsigner s1 strict,"
+
+cat output1 | grep Warning || ERR="$ERR s1 warning,"
+cat output1 | grep Error && ERR="$ERR s1 error,"
+cat output1 | grep timestamp | grep 2100-01-01 || ERR="$ERR s1 timestamp,"
+cat output1 | grep "with signer errors" && ERR="$ERR s1 err,"
+
+# hasExpiringCert is informatiional warning
+$JAR cvf $JFILE file
+$JARSIGNER $JFILE s2 > output2 || ERR="$ERR jarsigner s2,"
+$JARSIGNER -strict $JFILE s2 >> output2 || ERR="$ERR jarsigner s2 strict,"
+$JARSIGNER -verify $JFILE s2 >> output2 || ERR="$ERR jarsigner s2,"
+$JARSIGNER -verify -strict $JFILE s2 >> output2 || ERR="$ERR jarsigner s2 strict,"
+
+cat output2 | grep Warning || ERR="$ERR s2 warning,"
+cat output2 | grep Error && ERR="$ERR s2 error,"
+cat output2 | grep timestamp || ERR="$ERR s2 timestamp,"
+cat output2 | grep "will expire" || ERR="$ERR s2 expiring,"
+cat output2 | grep "with signer errors" && ERR="$ERR s2 err,"
+
+# hasExpiredCert is severe warning
+$JAR cvf $JFILE file
+$JARSIGNER $JFILE s3 > output3 || ERR="$ERR jarsigner s3,"
+$JARSIGNER -strict $JFILE s3 > output3s && ERR="$ERR jarsigner s3 strict,"
+$JARSIGNER -verify $JFILE s3 >> output3 || ERR="$ERR jarsigner s3,"
+$JARSIGNER -verify -strict $JFILE s3 >> output3s && ERR="$ERR jarsigner s3 strict,"
+
+# warning without -strict
+cat output3 | grep Warning || ERR="$ERR s3 warning,"
+cat output3 | grep Error && ERR="$ERR s3 error,"
+cat output3 | grep "with signer errors" && ERR="$ERR s3 err,"
+
+# error with -strict
+cat output3s | grep Warning || ERR="$ERR s3s warning,"
+cat output3s | grep Error || ERR="$ERR s3s error,"
+cat output3s | grep "with signer errors" || ERR="$ERR s3 err,"
+
+if [ "$ERR" = "" ]; then
+ exit 0
+else
+ echo "ERR is $ERR"
+ exit 1
+fi
+
+