Skip to content

Commit 78580f1

Browse files
committed
8378291: Test vector in test/jdk/java/util/jar/JarEntry/GetMethodsReturnClones.java is effectively unsigned
Reviewed-by: eirbjo, lancea
1 parent dc81630 commit 78580f1

2 files changed

Lines changed: 121 additions & 30 deletions

File tree

test/jdk/java/util/jar/JarEntry/GetMethodsReturnClones.java

Lines changed: 121 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,72 +21,163 @@
2121
* questions.
2222
*/
2323

24+
import java.io.IOException;
25+
import java.io.InputStream;
26+
import java.io.OutputStream;
27+
import java.nio.file.Files;
28+
import java.nio.file.Path;
29+
import java.security.CodeSigner;
30+
import java.security.KeyStore;
31+
import java.security.cert.Certificate;
32+
import java.util.ArrayList;
33+
import java.util.Arrays;
34+
import java.util.Enumeration;
35+
import java.util.List;
36+
import java.util.concurrent.TimeUnit;
37+
import java.util.jar.JarEntry;
38+
import java.util.jar.JarFile;
39+
import java.util.jar.JarOutputStream;
40+
import java.util.zip.ZipFile;
41+
42+
import jdk.security.jarsigner.JarSigner;
43+
import org.junit.jupiter.api.BeforeAll;
44+
import org.junit.jupiter.api.Test;
45+
import sun.security.tools.keytool.CertAndKeyGen;
46+
import sun.security.x509.X500Name;
47+
import static java.nio.charset.StandardCharsets.US_ASCII;
48+
import static org.junit.jupiter.api.Assertions.assertNotNull;
49+
import static org.junit.jupiter.api.Assertions.assertNull;
50+
2451
/*
2552
* @test
2653
* @bug 6337925
2754
* @summary Ensure that callers cannot modify the internal JarEntry cert and
2855
* codesigner arrays.
56+
* @modules java.base/sun.security.tools.keytool
57+
* java.base/sun.security.x509
2958
* @run junit GetMethodsReturnClones
3059
*/
31-
import org.junit.jupiter.api.BeforeAll;
32-
import org.junit.jupiter.api.Test;
60+
class GetMethodsReturnClones {
3361

34-
import java.io.IOException;
35-
import java.io.InputStream;
36-
import java.security.CodeSigner;
37-
import java.security.cert.Certificate;
38-
import java.util.*;
39-
import java.util.jar.*;
4062

41-
import static org.junit.jupiter.api.Assertions.assertNotNull;
42-
43-
public class GetMethodsReturnClones {
63+
private static final String ENTRY_NAME = "foobar.txt";
64+
private static final String SIGNER_NAME = "DUMMY";
65+
private static final String DIGEST_ALGORITHM = "SHA-256";
66+
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
67+
private static final String KEY_TYPE = "RSA";
4468

45-
private static final String BASE = System.getProperty("test.src", ".") +
46-
System.getProperty("file.separator");
4769
private static List<JarEntry> jarEntries;
4870

71+
/*
72+
* Creates a signed JAR file and initializes the "jarEntries"
73+
*/
4974
@BeforeAll()
50-
static void setupEntries() throws IOException {
75+
static void setupJarEntries() throws Exception {
76+
Path unsigned = createJar();
77+
Path signed = signJar(unsigned);
78+
System.err.println("created signed JAR file at " + signed.toAbsolutePath());
5179
List<JarEntry> entries = new ArrayList<>();
52-
try (JarFile jf = new JarFile(BASE + "test.jar", true)) {
53-
byte[] buffer = new byte[8192];
80+
try (JarFile jf = new JarFile(signed.toFile(), true)) {
5481
Enumeration<JarEntry> e = jf.entries();
5582
while (e.hasMoreElements()) {
5683
JarEntry je = e.nextElement();
5784
entries.add(je);
5885
try (InputStream is = jf.getInputStream(je)) {
59-
while (is.read(buffer, 0, buffer.length) != -1) {
60-
// we just read. this will throw a SecurityException
61-
// if a signature/digest check fails.
62-
}
86+
// we just read. this will throw a SecurityException
87+
// if a signature/digest check fails.
88+
var _ = is.readAllBytes();
6389
}
6490
}
6591
}
6692
jarEntries = entries;
6793
}
6894

95+
/*
96+
* For entries in the signed JAR file, this test verifies that if a non-null
97+
* array is returned by the JarEntry.getCertificates() method, then any subsequent
98+
* updates to that returned array do not propagate back to the original array.
99+
*/
69100
@Test
70-
void certsTest() {
101+
void testCertificatesArray() {
71102
for (JarEntry je : jarEntries) {
72103
Certificate[] certs = je.getCertificates();
73-
if (certs != null) {
74-
certs[0] = null;
75-
certs = je.getCertificates();
76-
assertNotNull(certs[0], "Modified internal certs array");
104+
System.err.println("Certificates for " + je.getName() + " " + Arrays.toString(certs));
105+
if (isSignatureRelated(je)) {
106+
// we don't expect this entry to be signed
107+
assertNull(certs, "JarEntry.getCertificates() returned non-null for " + je.getName());
108+
continue;
77109
}
110+
assertNotNull(certs, "JarEntry.getCertificates() returned null for " + je.getName());
111+
assertNotNull(certs[0], "Certificate is null");
112+
113+
certs[0] = null; // intentionally update the returned array
114+
certs = je.getCertificates(); // now get the certs again
115+
assertNotNull(certs, "JarEntry.getCertificates() returned null for " + je.getName());
116+
// verify that the newly returned array doesn't have the overwritten value
117+
assertNotNull(certs[0], "Internal certificates array was modified");
78118
}
79119
}
80120

121+
/*
122+
* For entries in the signed JAR file, this test verifies that if a non-null
123+
* array is returned by the JarEntry.getCodeSigners() method, then any subsequent
124+
* updates to that returned array do not propagate back to the original array.
125+
*/
81126
@Test
82-
void signersTest() {
127+
void testCodeSignersArray() {
83128
for (JarEntry je : jarEntries) {
84129
CodeSigner[] signers = je.getCodeSigners();
85-
if (signers != null) {
86-
signers[0] = null;
87-
signers = je.getCodeSigners();
88-
assertNotNull(signers[0], "Modified internal codesigners array");
130+
System.err.println("CodeSigners for " + je.getName() + " " + Arrays.toString(signers));
131+
if (isSignatureRelated(je)) {
132+
// we don't expect this entry to be signed
133+
assertNull(signers, "JarEntry.getCodeSigners() returned non-null for " + je.getName());
134+
continue;
89135
}
136+
assertNotNull(signers, "JarEntry.getCodeSigners() returned null for " + je.getName());
137+
assertNotNull(signers[0], "CodeSigner is null");
138+
139+
signers[0] = null; // intentionally update the array
140+
signers = je.getCodeSigners(); // now get the codesigners again
141+
assertNotNull(signers, "JarEntry.getCodeSigners() returned null for " + je.getName());
142+
// verify that the newly returned array doesn't have the overwritten value
143+
assertNotNull(signers[0], "CodeSigner is null");
144+
}
145+
}
146+
147+
private static Path createJar() throws IOException {
148+
final Path unsigned = Path.of("unsigned.jar");
149+
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(unsigned))) {
150+
out.putNextEntry(new JarEntry(ENTRY_NAME));
151+
out.write("hello world".getBytes(US_ASCII));
90152
}
153+
return unsigned;
154+
}
155+
156+
private static Path signJar(final Path unsigned) throws Exception {
157+
final Path signed = Path.of("signed.jar");
158+
final JarSigner signer = new JarSigner.Builder(privateKeyEntry())
159+
.signerName(SIGNER_NAME)
160+
.digestAlgorithm(DIGEST_ALGORITHM)
161+
.signatureAlgorithm(SIGNATURE_ALGORITHM)
162+
.build();
163+
try (ZipFile zip = new ZipFile(unsigned.toFile());
164+
OutputStream out = Files.newOutputStream(signed)) {
165+
signer.sign(zip, out);
166+
}
167+
return signed;
168+
}
169+
170+
private static KeyStore.PrivateKeyEntry privateKeyEntry() throws Exception {
171+
final CertAndKeyGen gen = new CertAndKeyGen(KEY_TYPE, SIGNATURE_ALGORITHM);
172+
gen.generate(4096);
173+
final long oneDayInSecs = TimeUnit.SECONDS.convert(1, TimeUnit.DAYS);
174+
Certificate cert = gen.getSelfCertificate(new X500Name("cn=duke"), oneDayInSecs);
175+
return new KeyStore.PrivateKeyEntry(gen.getPrivateKey(), new Certificate[] {cert});
176+
}
177+
178+
private static boolean isSignatureRelated(final JarEntry entry) {
179+
final String entryName = entry.getName();
180+
return entryName.equals("META-INF/" + SIGNER_NAME + ".SF")
181+
|| entryName.equals("META-INF/" + SIGNER_NAME + ".RSA");
91182
}
92183
}
-2.95 KB
Binary file not shown.

0 commit comments

Comments
 (0)