Skip to content

Commit c40bce3

Browse files
franferraxPaul Hohensee
authored andcommitted
8339280: jarsigner -verify performs cross-checking between CEN and LOC
8353299: VerifyJarEntryName.java test fails 8367782: VerifyJarEntryName.java: Fix modifyJarEntryName to operate on bytes and re-introduce verifySignatureEntryName Reviewed-by: abakhtin, sgehwolf Backport-of: bbd5b174c50346152a624317b6bd76ec48f7e551
1 parent 9c5f0dd commit c40bce3

File tree

6 files changed

+333
-0
lines changed

6 files changed

+333
-0
lines changed

src/bsd/doc/man/jarsigner.1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,11 @@ Informational warnings include those that are not errors but regarded as bad pra
624624
hasExpiringCert
625625
This jar contains entries whose signer certificate will expire within six months\&.
626626
.TP
627+
internalInconsistenciesDetected
628+
This jar contains internal inconsistencies detected during verification
629+
that may result in different contents when reading via JarFile
630+
and JarInputStream\&.
631+
.TP
627632
noTimestamp
628633
This jar contains signatures that does not include a timestamp\&. Without a timestamp, users may not be able to validate this JAR file after the signer certificate\&'s expiration date (\f3YYYY-MM-DD\fR) or after any future revocation date\&.
629634
.SH EXAMPLES

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

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
import java.io.*;
2929
import java.net.UnknownHostException;
30+
import java.nio.file.Files;
31+
import java.nio.file.Path;
3032
import java.security.cert.CertPathValidatorException;
3133
import java.security.cert.PKIXBuilderParameters;
3234
import java.util.*;
@@ -217,6 +219,8 @@ public static void main(String args[]) throws Exception {
217219
private Throwable chainNotValidatedReason = null;
218220
private Throwable tsaChainNotValidatedReason = null;
219221

222+
private List<String> crossChkWarnings = new ArrayList<>();
223+
220224
PKIXBuilderParameters pkixParameters;
221225
Set<X509Certificate> trustedCerts = new HashSet<>();
222226

@@ -1045,6 +1049,7 @@ void verifyJar(String jarName)
10451049
}
10461050
}
10471051
System.out.println();
1052+
crossCheckEntries(jarName);
10481053

10491054
if (!anySigned) {
10501055
if (disabledAlgFound) {
@@ -1079,6 +1084,143 @@ void verifyJar(String jarName)
10791084
System.exit(1);
10801085
}
10811086

1087+
private void crossCheckEntries(String jarName) throws Exception {
1088+
Set<String> locEntries = new HashSet<>();
1089+
1090+
try (JarFile jarFile = new JarFile(jarName);
1091+
JarInputStream jis = new JarInputStream(
1092+
Files.newInputStream(Path.of(jarName)))) {
1093+
1094+
Manifest cenManifest = jarFile.getManifest();
1095+
Manifest locManifest = jis.getManifest();
1096+
compareManifest(cenManifest, locManifest);
1097+
1098+
JarEntry locEntry;
1099+
while ((locEntry = jis.getNextJarEntry()) != null) {
1100+
String entryName = locEntry.getName();
1101+
locEntries.add(entryName);
1102+
1103+
JarEntry cenEntry = jarFile.getJarEntry(entryName);
1104+
if (cenEntry == null) {
1105+
crossChkWarnings.add(String.format(rb.getString(
1106+
"entry.1.present.when.reading.jarinputstream.but.missing.via.jarfile"),
1107+
entryName));
1108+
continue;
1109+
}
1110+
1111+
try {
1112+
readEntry(jis);
1113+
} catch (SecurityException e) {
1114+
crossChkWarnings.add(String.format(rb.getString(
1115+
"signature.verification.failed.on.entry.1.when.reading.via.jarinputstream"),
1116+
entryName));
1117+
continue;
1118+
}
1119+
1120+
try (InputStream cenInputStream = jarFile.getInputStream(cenEntry)) {
1121+
if (cenInputStream == null) {
1122+
crossChkWarnings.add(String.format(rb.getString(
1123+
"entry.1.present.in.jarfile.but.unreadable"),
1124+
entryName));
1125+
continue;
1126+
} else {
1127+
try {
1128+
readEntry(cenInputStream);
1129+
} catch (SecurityException e) {
1130+
crossChkWarnings.add(String.format(rb.getString(
1131+
"signature.verification.failed.on.entry.1.when.reading.via.jarfile"),
1132+
entryName));
1133+
continue;
1134+
}
1135+
}
1136+
}
1137+
1138+
compareSigners(cenEntry, locEntry);
1139+
}
1140+
1141+
jarFile.stream()
1142+
.map(JarEntry::getName)
1143+
.filter(n -> !locEntries.contains(n) && !n.equals(JarFile.MANIFEST_NAME))
1144+
.forEach(n -> crossChkWarnings.add(String.format(rb.getString(
1145+
"entry.1.present.when.reading.jarfile.but.missing.via.jarinputstream"), n)));
1146+
}
1147+
}
1148+
1149+
private void readEntry(InputStream is) throws IOException {
1150+
is.transferTo(OutputStream.nullOutputStream());
1151+
}
1152+
1153+
private void compareManifest(Manifest cenManifest, Manifest locManifest) {
1154+
if (cenManifest == null) {
1155+
crossChkWarnings.add(rb.getString(
1156+
"manifest.missing.when.reading.jarfile"));
1157+
return;
1158+
}
1159+
if (locManifest == null) {
1160+
crossChkWarnings.add(rb.getString(
1161+
"manifest.missing.when.reading.jarinputstream"));
1162+
return;
1163+
}
1164+
1165+
Attributes cenMainAttrs = cenManifest.getMainAttributes();
1166+
Attributes locMainAttrs = locManifest.getMainAttributes();
1167+
1168+
for (Object key : cenMainAttrs.keySet()) {
1169+
Object cenValue = cenMainAttrs.get(key);
1170+
Object locValue = locMainAttrs.get(key);
1171+
1172+
if (locValue == null) {
1173+
crossChkWarnings.add(String.format(rb.getString(
1174+
"manifest.attribute.1.present.when.reading.jarfile.but.missing.via.jarinputstream"),
1175+
key));
1176+
} else if (!cenValue.equals(locValue)) {
1177+
crossChkWarnings.add(String.format(rb.getString(
1178+
"manifest.attribute.1.differs.jarfile.value.2.jarinputstream.value.3"),
1179+
key, cenValue, locValue));
1180+
}
1181+
}
1182+
1183+
for (Object key : locMainAttrs.keySet()) {
1184+
if (!cenMainAttrs.containsKey(key)) {
1185+
crossChkWarnings.add(String.format(rb.getString(
1186+
"manifest.attribute.1.present.when.reading.jarinputstream.but.missing.via.jarfile"),
1187+
key));
1188+
}
1189+
}
1190+
}
1191+
1192+
private void compareSigners(JarEntry cenEntry, JarEntry locEntry) {
1193+
CodeSigner[] cenSigners = cenEntry.getCodeSigners();
1194+
CodeSigner[] locSigners = locEntry.getCodeSigners();
1195+
1196+
boolean cenHasSigners = cenSigners != null;
1197+
boolean locHasSigners = locSigners != null;
1198+
1199+
if (cenHasSigners && locHasSigners) {
1200+
if (!Arrays.equals(cenSigners, locSigners)) {
1201+
crossChkWarnings.add(String.format(rb.getString(
1202+
"codesigners.different.for.entry.1.when.reading.jarfile.and.jarinputstream"),
1203+
cenEntry.getName()));
1204+
}
1205+
} else if (cenHasSigners) {
1206+
crossChkWarnings.add(String.format(rb.getString(
1207+
"entry.1.is.signed.in.jarfile.but.is.not.signed.in.jarinputstream"),
1208+
cenEntry.getName()));
1209+
} else if (locHasSigners) {
1210+
crossChkWarnings.add(String.format(rb.getString(
1211+
"entry.1.is.signed.in.jarinputstream.but.is.not.signed.in.jarfile"),
1212+
locEntry.getName()));
1213+
}
1214+
}
1215+
1216+
private void displayCrossChkWarnings() {
1217+
System.out.println();
1218+
// First is a summary warning
1219+
System.out.println(rb.getString("jar.contains.internal.inconsistencies.result.in.different.contents.via.jarfile.and.jarinputstream"));
1220+
// each warning message with prefix "- "
1221+
crossChkWarnings.forEach(warning -> System.out.println("- " + warning));
1222+
}
1223+
10821224
private void displayMessagesAndResult(boolean isSigning) {
10831225
String result;
10841226
List<String> errors = new ArrayList<>();
@@ -1314,13 +1456,19 @@ private void displayMessagesAndResult(boolean isSigning) {
13141456
System.out.println(rb.getString("Warning."));
13151457
warnings.forEach(System.out::println);
13161458
}
1459+
if (!crossChkWarnings.isEmpty()) {
1460+
displayCrossChkWarnings();
1461+
}
13171462
} else {
13181463
if (!errors.isEmpty() || !warnings.isEmpty()) {
13191464
System.out.println();
13201465
System.out.println(rb.getString("Warning."));
13211466
errors.forEach(System.out::println);
13221467
warnings.forEach(System.out::println);
13231468
}
1469+
if (!crossChkWarnings.isEmpty()) {
1470+
displayCrossChkWarnings();
1471+
}
13241472
}
13251473
if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) {
13261474
if (! (verbose != null && showcerts)) {

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,34 @@ public class Resources extends java.util.ListResourceBundle {
324324
{"Cannot.find.environment.variable.",
325325
"Cannot find environment variable: "},
326326
{"Cannot.find.file.", "Cannot find file: "},
327+
{"manifest.missing.when.reading.jarfile",
328+
"Manifest is missing when reading via JarFile"},
329+
{"manifest.missing.when.reading.jarinputstream",
330+
"Manifest is missing when reading via JarInputStream"},
331+
{"manifest.attribute.1.present.when.reading.jarfile.but.missing.via.jarinputstream",
332+
"Manifest main attribute %s is present when reading via JarFile but missing when reading via JarInputStream"},
333+
{"manifest.attribute.1.present.when.reading.jarinputstream.but.missing.via.jarfile",
334+
"Manifest main attribute %s is present when reading via JarInputStream but missing when reading via JarFile"},
335+
{"manifest.attribute.1.differs.jarfile.value.2.jarinputstream.value.3",
336+
"Manifest main attribute %1$s differs: JarFile value = %2$s, JarInputStream value = %3$s"},
337+
{"entry.1.present.when.reading.jarinputstream.but.missing.via.jarfile",
338+
"Entry %s is present when reading via JarInputStream but missing when reading via JarFile"},
339+
{"entry.1.present.when.reading.jarfile.but.missing.via.jarinputstream",
340+
"Entry %s is present when reading via JarFile but missing when reading via JarInputStream"},
341+
{"entry.1.present.in.jarfile.but.unreadable",
342+
"Entry %s is present in JarFile but unreadable"},
343+
{"codesigners.different.for.entry.1.when.reading.jarfile.and.jarinputstream",
344+
"Code signers are different for entry %s when reading from JarFile and JarInputStream"},
345+
{"entry.1.is.signed.in.jarfile.but.is.not.signed.in.jarinputstream",
346+
"Entry %s is signed in JarFile but is not signed in JarInputStream"},
347+
{"entry.1.is.signed.in.jarinputstream.but.is.not.signed.in.jarfile",
348+
"Entry %s is signed in JarInputStream but is not signed in JarFile"},
349+
{"jar.contains.internal.inconsistencies.result.in.different.contents.via.jarfile.and.jarinputstream",
350+
"This JAR file contains internal inconsistencies that may result in different contents when reading via JarFile and JarInputStream:"},
351+
{"signature.verification.failed.on.entry.1.when.reading.via.jarinputstream",
352+
"Signature verification failed on entry %s when reading via JarInputStream"},
353+
{"signature.verification.failed.on.entry.1.when.reading.via.jarfile",
354+
"Signature verification failed on entry %s when reading via JarFile"},
327355
};
328356

329357
/**

src/linux/doc/man/jarsigner.1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,11 @@ Informational warnings include those that are not errors but regarded as bad pra
624624
hasExpiringCert
625625
This jar contains entries whose signer certificate will expire within six months\&.
626626
.TP
627+
internalInconsistenciesDetected
628+
This jar contains internal inconsistencies detected during verification
629+
that may result in different contents when reading via JarFile
630+
and JarInputStream\&.
631+
.TP
627632
noTimestamp
628633
This jar contains signatures that does not include a timestamp\&. Without a timestamp, users may not be able to validate this JAR file after the signer certificate\&'s expiration date (\f3YYYY-MM-DD\fR) or after any future revocation date\&.
629634
.SH EXAMPLES

src/solaris/doc/sun/man/man1/jarsigner.1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,11 @@ Informational warnings include those that are not errors but regarded as bad pra
624624
hasExpiringCert
625625
This jar contains entries whose signer certificate will expire within six months\&.
626626
.TP
627+
internalInconsistenciesDetected
628+
This jar contains internal inconsistencies detected during verification
629+
that may result in different contents when reading via JarFile
630+
and JarInputStream\&.
631+
.TP
627632
noTimestamp
628633
This jar contains signatures that does not include a timestamp\&. Without a timestamp, users may not be able to validate this JAR file after the signer certificate\&'s expiration date (\f3YYYY-MM-DD\fR) or after any future revocation date\&.
629634
.SH EXAMPLES

0 commit comments

Comments
 (0)