Skip to content

Commit d809033

Browse files
committed
8341775: Duplicate manifest files are removed by jarsigner after signing
Reviewed-by: weijun, hchao
1 parent a269bef commit d809033

File tree

4 files changed

+118
-1
lines changed

4 files changed

+118
-1
lines changed

src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ public ExitException(int errorCode) {
237237
private boolean badNetscapeCertType = false;
238238
private boolean signerSelfSigned = false;
239239
private boolean allAliasesFound = true;
240+
private boolean hasMultipleManifests = false;
240241

241242
private Throwable chainNotValidatedReason = null;
242243
private Throwable tsaChainNotValidatedReason = null;
@@ -1252,6 +1253,11 @@ private void displayMessagesAndResult(boolean isSigning) {
12521253
rb.getString("The.full.keyAlgName.signing.key.is.considered.a.security.risk.and.is.disabled."),
12531254
fullDisplayKeyName(privateKey)));
12541255
}
1256+
1257+
if (hasMultipleManifests) {
1258+
warnings.add(String.format(rb.getString("multiple.manifest.warning.")));
1259+
}
1260+
12551261
} else {
12561262
if ((legacyAlg & 1) != 0) {
12571263
warnings.add(String.format(
@@ -1965,6 +1971,15 @@ void signJar(String jarName, String alias)
19651971
Throwable failedCause = null;
19661972
String failedMessage = null;
19671973

1974+
try (JarFile asJar = new JarFile(jarFile)) {
1975+
if (JUZFA.getManifestNum(asJar) > 1) {
1976+
hasMultipleManifests = true;
1977+
}
1978+
} catch (IOException ioe) {
1979+
// intentionally "eat" this, since we don't want to fail, if we
1980+
// cannot perform the multiple manifest check to output the warning
1981+
}
1982+
19681983
try {
19691984
Event.setReportListener(Event.ReporterCategory.ZIPFILEATTRS,
19701985
(t, o) -> externalFileAttributesDetected = true);

src/jdk.jartool/share/classes/sun/security/tools/jarsigner/resources/jarsigner.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ jar.is.unsigned=jar is unsigned.
9696
jar.treated.unsigned=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).
9797
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.
9898
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:
99+
multiple.manifest.warning.=Duplicate manifest entries were detected in the jar file. JarSigner operated on only one, and the others have been discarded.
99100
jar.signed.=jar signed.
100101
jar.signed.with.signer.errors.=jar signed, with signer errors.
101102
jar.verified.=jar verified.

src/jdk.jartool/share/man/jarsigner.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
# Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
33
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
#
55
# This code is free software; you can redistribute it and/or modify it
@@ -922,6 +922,10 @@ hasExpiringCert
922922
hasExpiringTsaCert
923923
: The timestamp will expire within one year on `YYYY-MM-DD`.
924924

925+
hasMultipleManifests
926+
: This JAR contained multiple manifest files. During signing, one of the files
927+
was selected, and the others were discarded.
928+
925929
hasNonexistentEntries
926930
: This JAR contains signed entries for files that do not exist.
927931

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8341775
27+
* @summary Print warning that duplicate manifest files are removed by jarsigner
28+
* after signing whether or not -verbose is passed
29+
* @library /test/lib
30+
*/
31+
32+
import jdk.test.lib.SecurityTools;
33+
34+
import java.io.ByteArrayOutputStream;
35+
import java.io.FileOutputStream;
36+
import java.io.IOException;
37+
import java.nio.charset.StandardCharsets;
38+
import java.nio.file.Files;
39+
import java.nio.file.Path;
40+
import java.util.jar.JarFile;
41+
import java.util.zip.ZipEntry;
42+
import java.util.zip.ZipOutputStream;
43+
44+
public class MultiManifest {
45+
46+
private static final String META_INF = "META-INF/";
47+
48+
public static void main(String[] args) throws Exception {
49+
50+
writeMultiManifestJar();
51+
52+
SecurityTools.keytool("-keystore ks -storepass changeit "
53+
+ "-keypass changeit -alias a -dname CN=a -keyalg rsa "
54+
+ "-genkey -validity 300");
55+
56+
SecurityTools.jarsigner("-verbose -keystore ks -storepass changeit "
57+
+ "MultiManifest.jar -signedjar MultiManifest.signed.jar a")
58+
.shouldHaveExitValue(0)
59+
.shouldContain("Duplicate manifest entries were detected")
60+
.shouldContain("discarded");
61+
62+
SecurityTools.jarsigner("-keystore ks -storepass changeit "
63+
+ "MultiManifest.jar -signedjar MultiManifest.signed.jar a")
64+
.shouldHaveExitValue(0)
65+
.shouldContain("Duplicate manifest entries were detected")
66+
.shouldContain("discarded");
67+
68+
}
69+
70+
public static void writeMultiManifestJar() throws IOException {
71+
int locPosA, locPosB, cenPos;
72+
var out = new ByteArrayOutputStream(1024);
73+
try (var zos = new ZipOutputStream(out)) {
74+
zos.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME));
75+
zos.closeEntry();
76+
locPosA = out.size();
77+
zos.putNextEntry(new ZipEntry(META_INF + "AANIFEST.MF"));
78+
zos.closeEntry();
79+
locPosB = out.size();
80+
zos.putNextEntry(new ZipEntry(META_INF + "BANIFEST.MF"));
81+
zos.flush();
82+
cenPos = out.size();
83+
}
84+
var template = out.toByteArray();
85+
// ISO_8859_1 to keep the 8-bit value
86+
var s = new String(template, StandardCharsets.ISO_8859_1);
87+
// change META-INF/AANIFEST.MF to META-INF/MANIFEST.MF
88+
var loc = s.indexOf("AANIFEST.MF", locPosA);
89+
var cen = s.indexOf("AANIFEST.MF", cenPos);
90+
template[loc] = template[cen] = (byte) 'M';
91+
// change META-INF/BANIFEST.MF to META-INF/MANIFEST.MF
92+
loc = s.indexOf("BANIFEST.MF", locPosB);
93+
cen = s.indexOf("BANIFEST.MF", cenPos);
94+
template[loc] = template[cen] = (byte) 'M';
95+
Files.write(Path.of("MultiManifest.jar"), template);
96+
}
97+
}

0 commit comments

Comments
 (0)