JDK-8031572 : jarsigner -verify exits with 0 when a jar file is not properly signed
  • Type: Bug
  • Status: Closed
  • Resolution: Fixed
  • Component: security-libs
  • Sub-Component: java.security
  • Priority: P1
  • Affected Version: 8
  • OS: linux
  • Submit Date: 2014-01-13
  • Updated Date: 2014-10-15
  • Resolved Date: 2014-01-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availabitlity Release.

To download the current JDK release, click here.
7u60Fixed 8 b127Fixed 9Fixed
Related Reports
Relates :  
Relates :  
java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b121)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b63, mixed mode)

Linux tc 3.12.6-1-ARCH #1 SMP PREEMPT Fri Dec 20 19:39:00 CET 2013 x86_64 GNU/Linux

In Apache maven-jarsigner-plugin we got a regression around the jarsigner -verify command applyed to a unsigned jar.

With jdk 1.7.0_45:

$> jarsigner -verify  tampered.jar
jarsigner: java.lang.SecurityException: Invalid signature file digest for Manifest main attributes

Exit code is 1.

With jdk 1.8.0:

$> jarsigner -verify  tampered.jar
jar is unsigned. (signatures missing or not parsable)

Exit code is 0.

REGRESSION.  Last worked in version 7u45

java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

Try on a bad signed jar:

svn co http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-jarsigner-plugin/src/it/verify-fail/tampered.jar
run on it jarsigner -verify tampered.jar


Get the maven-jarsigner-plugin and execute the verify-fail IT

svn co http://svn.apache.org/repos/asf/maven/plugins/trunk/maven-jarsigner-plugin
cd maven-jarsigner-plugin
mvn verify -Prun-its -Dinvoker.pom=src/it/verify-fail/pom.xml

Exit code = 1
Exit code = 0

This bug can be reproduced always.
Tried manual verification with tampered jar and with regression test. Fix works with b127.

SQE is OK to add this fix into JDK8

Release team: Approved for fixing

8-critical-request justification: We decide to support arbitrary order of jar entries for JarFile now, and thus change the priority to P1. The code change is being reviewed now at http://cr.openjdk.java.net/~weijun/8031572/webrev.00/. After this fix, the JarFile can have its entries in any order. Running jarsigner on the Maven tampered jar included in this bug will give the same result as 1.7.0_45. Please note that when opened as a JarInputStream, the MANIFEST and signature-related entries (DSA and SF) still must appear the beginning of the jar file, we should add a clarification on this later. A new regression test is included in the webrev. The fix will also need to be backported to 7u60.

I have found a fix to the issue. In fact, JarFile already tries to deal with entries reordering. It gathers all META-INF entries and asks JarVerifier to read them first. The problem is that META-INF might contain other files that is not signature-related (like in this case). The fix is to only ask JarVerifier to read signature-related files in JarFIle::initializeVerifier. After this fix, JarFile can accept any entries order. On the other hand, JarInputStream still needs MANIFEST.MF at the beginning followed by signature-related files. I've created a test to confirm this. Webrev at http://cr.openjdk.java.net/~weijun/8031572/webrev.00/ for jdk9 and http://cr.openjdk.java.net/~weijun/8031572/8/webrev.00/ for jdk8.

I attached is a simple maven example project to create a signed jar. I checked that the jar produced is fine, entries are in the expected locations. If i shuffle the entries around so the signatures are at the end then JDK 8 jarsigner will assume the jar is unsigned, which is as Weijun says. However, I would still like to point out the fragility. If one simply unzips and re-zips the jar then the order of entries may change, since re-zipping may reorder entries in alphabetical order of the names. In the example project it works fine since the alias is "applet" but if i change to say "xxxxxx" then things will fail.

JDK-8021788 is the bug fix that leads to this regression. It is also included in 7u60. It is not complete and JDK-8022761 enhanced the fix, which is also in 7u60.

Regarding (4), precisely, if there are other files inside META-INF before MANIFEST.MF and the signature-related files, the jar file will be treated as unsigned. I think Paul's experiment does not include such files, while the jar in this bug has maven files inside META-INF. I've asked the bug reporter again if any Maven tool would created a jar like that. If we have to support it, then we need to clearly document that opening a signed jar with JarFile and JarInputStream could lead to different result. There is no way for JarInputStream to verify a file before it sees the signature, and we need to explain it. On the other hand, we can let JarFile to look for all signature-related files at the beginning.

Paul's point (4) is the critical question here. If signed JAR files created with Maven in the usual way have their signature data at the end of the file, and the jarsigner tool now treats those as being unsigned, then that's a regression likely to affect many, many people and we should fix it. I share Paul's concern about false positives. This isn't a crash or other hard failure, so Maven users aren't going to be particularly motivated to adopt the -strict workaround, or to upgrade to a new version of Maven (or its associated plugins) containing that workaround. Changing this behavior in the first place, on the basis of an undocumented assumption about the JAR-file format, seems like a mistake. It's fine to optimize for the case of signature data at the beginning of a JAR file, but suddenly imposing this restriction after many years makes no sense.

Some observations,: 1) it is not clear to me if the tampered.jar was generated by maven or generated by other means, it's certainly pre-generated. Who/what ordered those manifest entries? 2) i can unzip tampered.jar, re-zip in the required order, and get the required error from jarsigner. That might be a sufficient work around for this maven plugin (or using -strict) 3) it seems restrictive to require certain manifest entries in a certain and undocumented order, by all means optimize for that (common?) case. 4) i tested against a signed jar [1] and i can unzip and re-zip moving the signature manifiest entries to the end *and* obtain the same correct (and verified) result using jarsigner in JDK 8 for both the original and shuffled jar. Can this observation can be generalized to all correctly signed jars? It's tricky to predict how this will affect maven users. Assuming 4) then i suspect scope of impact is greatly reduced e.g. if maven signing plugins stuff *correct* signatures at the end of the jar, i dunno if they do, we should double check and verify what they do. However, i am generally concerned about false positives, be it the jars are verified with maven-jarsigner-plugin or using jarsigner directly. What would have previously been rejected is no longer and thus jars might be consumed in a manner thought to be safe but are not. [1] http://central.maven.org/maven2/org/bouncycastle/bctsp-jdk16/1.46/bctsp-jdk16-1.46.jar

Release team: We'd like to better understand the Maven use case before we decide what to do. In case this is something we have to fix we don't want to loose any time so please investigate what would be needed to fix this while we gather the details from Maven.

This is a bug raised by Apache Maven. Rory is contacting them to see understand the impact on Maven.

SQE approves this deferral because the bug doesn't qualify as P1.

ILW=MML=P4 Impact: medium. The jarsigner output is different, but because when a tampered signed jar is treated as unsigned it loses all privilege that a signed jar file can obtain. Thus an attacker cannot get benefit from doing this. When the signature is used to verify the authenticity, no signature should not be considered worse than a bad signature. Likelihood: medium. The jar and jarsigner tools always put the MANIFEST.MF and signature-related files at the beginning of a jar file, so this is not likely to happen for correctly-signed jars. Workaround: Low. With the -strict option jarsigner would exit with 16, which means "This jar contains unsigned entries which have not been integrity-checked."

The "IS signed, but not properly" case already leads to different results. The jar described in this bug has an exit code change from JDK 7 to JDK 8, and there are other cases where both return 0 (wrong key usage) or 1 (signature bits altered). The current output for exit code 0 -- "jar is unsigned. (signatures missing or not parsable)" -- is hard to explain.

8-defer-request justification: The customer's jar file is treated as unsigned because they have adjusted the order of jar entries inside the file and the signature-related entries are moved to the end. This is a corner case due to Maven moving things around in the META-INF directory manually. Therefore I request for a deferral for JDK 8. From the beginning, the jar file has an ���undocumented��� assumption that the MANIFEST.MF file and signature-related files should appear at the beginning of the jar file (except for directory entries, say, META-INF/). The benefit is that we can validate an entry only when it's used without reading the whole file at the beginning. If those files are not placed at the beginning, depending on if we open the file as a JarFile or as a JarInputStream, a file can be treated sometimes as signed and sometimes not. In JDK 8, we have make the behavior consistent so that if these signature-related files appear after other normal files, they are not treated as signature-related anymore. This is why customer sees the file as unsigned. We have filed a doc bug to clarify this behavior (JDK-8031748). Returning an exit code of 0 is the current behavior for unsigned jar files but we are considering changing it to non-zero. A "signed" jar file could be treated as unsigned for various reasons, including unparseable signature, missing digest lines, jar entry reordering (as in this case), etc. It will be better to report this to user in a more visible way. It's unlikely a user using jarsigner to verify a plain unsigned jar file.