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 import com.sun.net.httpserver.*;
25 import java.io.BufferedReader;
26 import java.io.ByteArrayOutputStream;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.OutputStream;
32 import java.math.BigInteger;
33 import java.net.InetSocketAddress;
34 import java.security.KeyStore;
35 import java.security.PrivateKey;
36 import java.security.Signature;
37 import java.security.cert.Certificate;
38 import java.security.cert.X509Certificate;
39 import java.util.Calendar;
40 import java.util.jar.JarEntry;
41 import java.util.jar.JarFile;
42 import java.util.Locale;
43
44 import sun.security.pkcs.ContentInfo;
45 import sun.security.pkcs.PKCS7;
46 import sun.security.pkcs.PKCS9Attribute;
47 import sun.security.pkcs.SignerInfo;
48 import sun.security.timestamp.TimestampToken;
49 import sun.security.util.DerOutputStream;
50 import sun.security.util.DerValue;
51 import sun.security.util.ObjectIdentifier;
52 import sun.security.x509.AlgorithmId;
53 import sun.security.x509.X500Name;
54
55 public class TimestampCheck {
56 static final String TSKS = "tsks";
57 static final String JAR = "old.jar";
58
59 static final String defaultPolicyId = "2.3.4.5";
60
61 static class Handler implements HttpHandler, AutoCloseable {
62
63 private final HttpServer httpServer;
64 private final String keystore;
65
66 @Override
67 public void handle(HttpExchange t) throws IOException {
68 int len = 0;
69 for (String h: t.getRequestHeaders().keySet()) {
70 if (h.equalsIgnoreCase("Content-length")) {
71 len = Integer.valueOf(t.getRequestHeaders().get(h).get(0));
72 }
73 }
74 byte[] input = new byte[len];
75 t.getRequestBody().read(input);
76
77 try {
78 int path = 0;
79 if (t.getRequestURI().getPath().length() > 1) {
80 path = Integer.parseInt(
81 t.getRequestURI().getPath().substring(1));
82 }
83 byte[] output = sign(input, path);
84 Headers out = t.getResponseHeaders();
85 out.set("Content-Type", "application/timestamp-reply");
86
87 t.sendResponseHeaders(200, output.length);
88 OutputStream os = t.getResponseBody();
89 os.write(output);
90 } catch (Exception e) {
91 e.printStackTrace();
92 t.sendResponseHeaders(500, 0);
93 }
94 t.close();
95 }
96
97 /**
98 * @param input The data to sign
99 * @param path different cases to simulate, impl on URL path
100 * 0: normal
101 * 1: Missing nonce
102 * 2: Different nonce
103 * 3: Bad digets octets in messageImprint
104 * 4: Different algorithmId in messageImprint
105 * 5: whole chain in cert set
106 * 6: extension is missing
107 * 7: extension is non-critical
108 * 8: extension does not have timestamping
109 * 9: no cert in response
110 * 10: normal
111 * 11: always return default policy id
112 * 12: normal
113 * otherwise: normal
114 * @returns the signed
115 */
116 byte[] sign(byte[] input, int path) throws Exception {
117 // Read TSRequest
118 DerValue value = new DerValue(input);
119 System.err.println("\nIncoming Request\n===================");
120 System.err.println("Version: " + value.data.getInteger());
121 DerValue messageImprint = value.data.getDerValue();
122 AlgorithmId aid = AlgorithmId.parse(
123 messageImprint.data.getDerValue());
124 System.err.println("AlgorithmId: " + aid);
125
126 ObjectIdentifier policyId = new ObjectIdentifier(defaultPolicyId);
127 BigInteger nonce = null;
128 while (value.data.available() > 0) {
129 DerValue v = value.data.getDerValue();
130 if (v.tag == DerValue.tag_Integer) {
131 nonce = v.getBigInteger();
132 System.err.println("nonce: " + nonce);
133 } else if (v.tag == DerValue.tag_Boolean) {
134 System.err.println("certReq: " + v.getBoolean());
135 } else if (v.tag == DerValue.tag_ObjectId) {
136 policyId = v.getOID();
137 System.err.println("PolicyID: " + policyId);
138 }
139 }
140
141 // Write TSResponse
142 System.err.println("\nResponse\n===================");
143 KeyStore ks = KeyStore.getInstance("JKS");
144 try (FileInputStream fis = new FileInputStream(keystore)) {
145 ks.load(fis, "changeit".toCharArray());
146 }
147
148 String alias = "ts";
149 if (path == 6) alias = "tsbad1";
150 if (path == 7) alias = "tsbad2";
151 if (path == 8) alias = "tsbad3";
152
153 if (path == 11) {
154 policyId = new ObjectIdentifier(defaultPolicyId);
155 }
156
157 DerOutputStream statusInfo = new DerOutputStream();
158 statusInfo.putInteger(0);
159
160 DerOutputStream token = new DerOutputStream();
161 AlgorithmId[] algorithms = {aid};
162 Certificate[] chain = ks.getCertificateChain(alias);
163 X509Certificate[] signerCertificateChain = null;
164 X509Certificate signer = (X509Certificate)chain[0];
165 if (path == 5) { // Only case 5 uses full chain
166 signerCertificateChain = new X509Certificate[chain.length];
167 for (int i=0; i<chain.length; i++) {
168 signerCertificateChain[i] = (X509Certificate)chain[i];
169 }
170 } else if (path == 9) {
171 signerCertificateChain = new X509Certificate[0];
172 } else {
173 signerCertificateChain = new X509Certificate[1];
174 signerCertificateChain[0] = (X509Certificate)chain[0];
175 }
176
177 DerOutputStream tst = new DerOutputStream();
178
179 tst.putInteger(1);
180 tst.putOID(policyId);
181
182 if (path != 3 && path != 4) {
183 tst.putDerValue(messageImprint);
184 } else {
185 byte[] data = messageImprint.toByteArray();
186 if (path == 4) {
187 data[6] = (byte)0x01;
188 } else {
189 data[data.length-1] = (byte)0x01;
190 data[data.length-2] = (byte)0x02;
191 data[data.length-3] = (byte)0x03;
192 }
193 tst.write(data);
194 }
195
196 tst.putInteger(1);
197
198 Calendar cal = Calendar.getInstance();
199 tst.putGeneralizedTime(cal.getTime());
200
201 if (path == 2) {
202 tst.putInteger(1234);
203 } else if (path == 1) {
204 // do nothing
205 } else {
206 tst.putInteger(nonce);
207 }
208
209 DerOutputStream tstInfo = new DerOutputStream();
210 tstInfo.write(DerValue.tag_Sequence, tst);
211
212 DerOutputStream tstInfo2 = new DerOutputStream();
213 tstInfo2.putOctetString(tstInfo.toByteArray());
214
215 Signature sig = Signature.getInstance("SHA1withRSA");
216 sig.initSign((PrivateKey)(ks.getKey(
217 alias, "changeit".toCharArray())));
218 sig.update(tstInfo.toByteArray());
219
220 ContentInfo contentInfo = new ContentInfo(new ObjectIdentifier(
221 "1.2.840.113549.1.9.16.1.4"),
222 new DerValue(tstInfo2.toByteArray()));
223
224 System.err.println("Signing...");
225 System.err.println(new X500Name(signer
226 .getIssuerX500Principal().getName()));
227 System.err.println(signer.getSerialNumber());
228
229 SignerInfo signerInfo = new SignerInfo(
230 new X500Name(signer.getIssuerX500Principal().getName()),
231 signer.getSerialNumber(),
232 aid, AlgorithmId.get("RSA"), sig.sign());
233
234 SignerInfo[] signerInfos = {signerInfo};
235 PKCS7 p7 =
236 new PKCS7(algorithms, contentInfo, signerCertificateChain,
237 signerInfos);
238 ByteArrayOutputStream p7out = new ByteArrayOutputStream();
239 p7.encodeSignedData(p7out);
240
241 DerOutputStream response = new DerOutputStream();
242 response.write(DerValue.tag_Sequence, statusInfo);
243 response.putDerValue(new DerValue(p7out.toByteArray()));
244
245 DerOutputStream out = new DerOutputStream();
246 out.write(DerValue.tag_Sequence, response);
247
248 return out.toByteArray();
249 }
250
251 private Handler(HttpServer httpServer, String keystore) {
252 this.httpServer = httpServer;
253 this.keystore = keystore;
254 }
255
256 /**
257 * Initialize TSA instance.
277 /**
278 * Stop TSA service.
279 */
280 void stop() {
281 httpServer.stop(0);
282 }
283
284 /**
285 * Return server port number.
286 */
287 int getPort() {
288 return httpServer.getAddress().getPort();
289 }
290
291 @Override
292 public void close() throws Exception {
293 stop();
294 }
295 }
296
297 public static void main(String[] args) throws Exception {
298 try (Handler tsa = Handler.init(0, TSKS);) {
299 tsa.start();
300 int port = tsa.getPort();
301
302 String cmd;
303 // Use -J-Djava.security.egd=file:/dev/./urandom to speed up
304 // nonce generation in timestamping request. Not avaibale on
305 // Windows and defaults to thread seed generator, not too bad.
306 if (System.getProperty("java.home").endsWith("jre")) {
307 cmd = System.getProperty("java.home") + "/../bin/jarsigner";
308 } else {
309 cmd = System.getProperty("java.home") + "/bin/jarsigner";
310 }
311
312 cmd += " " + System.getProperty("test.tool.vm.opts")
313 + " -J-Djava.security.egd=file:/dev/./urandom"
314 + " -J-Duser.language=en -J-Duser.country=US"
315 + " -debug -keystore " + TSKS + " -storepass changeit"
316 + " -tsa http://localhost:" + port + "/%d"
317 + " -signedjar new_%d.jar " + JAR + " old";
318
319 if (args.length == 0) { // Run this test
320 jarsigner(cmd, 0, true); // Success, normal call
321 jarsigner(cmd, 1, false); // These 4 should fail
322 jarsigner(cmd, 2, false);
323 jarsigner(cmd, 3, false);
324 jarsigner(cmd, 4, false);
325 jarsigner(cmd, 5, true); // Success, 6543440 solved.
326 jarsigner(cmd, 6, false); // tsbad1
327 jarsigner(cmd, 7, false); // tsbad2
328 jarsigner(cmd, 8, false); // tsbad3
329 jarsigner(cmd, 9, false); // no cert in timestamp
330 jarsigner(cmd + " -tsapolicyid 1.2.3.4", 10, true);
331 checkTimestamp("new_10.jar", "1.2.3.4", "SHA-256");
332 jarsigner(cmd + " -tsapolicyid 1.2.3.5", 11, false);
333 jarsigner(cmd + " -tsadigestalg SHA", 12, true);
334 checkTimestamp("new_12.jar", defaultPolicyId, "SHA-1");
335 } else { // Run as a standalone server
336 System.err.println("Press Enter to quit server");
337 System.in.read();
338 }
339 }
340 }
341
342 static void checkTimestamp(String file, String policyId, String digestAlg)
343 throws Exception {
344 try (JarFile jf = new JarFile(file)) {
345 JarEntry je = jf.getJarEntry("META-INF/OLD.RSA");
346 try (InputStream is = jf.getInputStream(je)) {
347 byte[] content = is.readAllBytes();
348 PKCS7 p7 = new PKCS7(content);
349 SignerInfo[] si = p7.getSignerInfos();
350 if (si == null || si.length == 0) {
351 throw new Exception("Not signed");
352 }
353 PKCS9Attribute p9 = si[0].getUnauthenticatedAttributes()
354 .getAttribute(PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
355 PKCS7 tsToken = new PKCS7((byte[]) p9.getValue());
356 TimestampToken tt =
357 new TimestampToken(tsToken.getContentInfo().getData());
358 if (!tt.getHashAlgorithm().toString().equals(digestAlg)) {
359 throw new Exception("Digest alg different");
360 }
361 if (!tt.getPolicyID().equals(policyId)) {
362 throw new Exception("policyId different");
363 }
364 }
365 }
366 }
367
368 /**
369 * @param cmd the command line (with a hole to plug in)
370 * @param path the path in the URL, i.e, http://localhost/path
371 * @param expected if this command should succeed
372 */
373 static void jarsigner(String cmd, int path, boolean expected)
374 throws Exception {
375 System.err.println("Test " + path);
376 Process p = Runtime.getRuntime().exec(String.format(Locale.ROOT,cmd, path, path));
377 BufferedReader reader = new BufferedReader(
378 new InputStreamReader(p.getErrorStream()));
379 while (true) {
380 String s = reader.readLine();
381 if (s == null) break;
382 System.err.println(s);
383 }
384
385 // Will not see noTimestamp warning
386 boolean seeWarning = false;
387 reader = new BufferedReader(
388 new InputStreamReader(p.getInputStream()));
389 while (true) {
390 String s = reader.readLine();
391 if (s == null) break;
392 System.err.println(s);
393 if (s.indexOf("Warning:") >= 0) {
394 seeWarning = true;
395 }
396 }
397 int result = p.waitFor();
398 if (expected && result != 0 || !expected && result == 0) {
399 throw new Exception("Failed");
400 }
401 if (seeWarning) {
402 throw new Exception("See warning");
403 }
404 }
405 }
|
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 import com.sun.net.httpserver.*;
25 import java.io.ByteArrayOutputStream;
26 import java.io.File;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30 import java.math.BigInteger;
31 import java.net.InetSocketAddress;
32 import java.nio.file.Files;
33 import java.nio.file.Paths;
34 import java.security.KeyStore;
35 import java.security.PrivateKey;
36 import java.security.Signature;
37 import java.security.cert.Certificate;
38 import java.security.cert.X509Certificate;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Calendar;
42 import java.util.List;
43 import java.util.jar.JarEntry;
44 import java.util.jar.JarFile;
45
46 import jdk.testlibrary.*;
47 import jdk.testlibrary.JarUtils;
48 import sun.security.pkcs.ContentInfo;
49 import sun.security.pkcs.PKCS7;
50 import sun.security.pkcs.PKCS9Attribute;
51 import sun.security.pkcs.SignerInfo;
52 import sun.security.timestamp.TimestampToken;
53 import sun.security.util.DerOutputStream;
54 import sun.security.util.DerValue;
55 import sun.security.util.ObjectIdentifier;
56 import sun.security.x509.AlgorithmId;
57 import sun.security.x509.X500Name;
58
59 /*
60 * @test
61 * @bug 6543842 6543440 6939248 8009636 8024302 8163304
62 * @summary checking response of timestamp
63 * @modules java.base/sun.security.pkcs
64 * java.base/sun.security.timestamp
65 * java.base/sun.security.x509
66 * java.base/sun.security.util
67 * java.base/sun.security.tools.keytool
68 * @library /lib/testlibrary
69 * @run main/timeout=600 TimestampCheck
70 */
71 public class TimestampCheck {
72
73 static final String defaultPolicyId = "2.3.4";
74 static String host = null;
75
76 static class Handler implements HttpHandler, AutoCloseable {
77
78 private final HttpServer httpServer;
79 private final String keystore;
80
81 @Override
82 public void handle(HttpExchange t) throws IOException {
83 int len = 0;
84 for (String h: t.getRequestHeaders().keySet()) {
85 if (h.equalsIgnoreCase("Content-length")) {
86 len = Integer.valueOf(t.getRequestHeaders().get(h).get(0));
87 }
88 }
89 byte[] input = new byte[len];
90 t.getRequestBody().read(input);
91
92 try {
93 String path = t.getRequestURI().getPath().substring(1);
94 byte[] output = sign(input, path);
95 Headers out = t.getResponseHeaders();
96 out.set("Content-Type", "application/timestamp-reply");
97
98 t.sendResponseHeaders(200, output.length);
99 OutputStream os = t.getResponseBody();
100 os.write(output);
101 } catch (Exception e) {
102 e.printStackTrace();
103 t.sendResponseHeaders(500, 0);
104 }
105 t.close();
106 }
107
108 /**
109 * @param input The data to sign
110 * @param path different cases to simulate, impl on URL path
111 * @returns the signed
112 */
113 byte[] sign(byte[] input, String path) throws Exception {
114
115 DerValue value = new DerValue(input);
116 System.err.println("\nIncoming Request\n===================");
117 System.err.println("Version: " + value.data.getInteger());
118 DerValue messageImprint = value.data.getDerValue();
119 AlgorithmId aid = AlgorithmId.parse(
120 messageImprint.data.getDerValue());
121 System.err.println("AlgorithmId: " + aid);
122
123 ObjectIdentifier policyId = new ObjectIdentifier(defaultPolicyId);
124 BigInteger nonce = null;
125 while (value.data.available() > 0) {
126 DerValue v = value.data.getDerValue();
127 if (v.tag == DerValue.tag_Integer) {
128 nonce = v.getBigInteger();
129 System.err.println("nonce: " + nonce);
130 } else if (v.tag == DerValue.tag_Boolean) {
131 System.err.println("certReq: " + v.getBoolean());
132 } else if (v.tag == DerValue.tag_ObjectId) {
133 policyId = v.getOID();
134 System.err.println("PolicyID: " + policyId);
135 }
136 }
137
138 System.err.println("\nResponse\n===================");
139 KeyStore ks = KeyStore.getInstance(
140 new File(keystore), "changeit".toCharArray());
141
142 String alias = "ts";
143 if (path.startsWith("bad") || path.equals("weak")) {
144 alias = "ts" + path;
145 }
146
147 if (path.equals("diffpolicy")) {
148 policyId = new ObjectIdentifier(defaultPolicyId);
149 }
150
151 DerOutputStream statusInfo = new DerOutputStream();
152 statusInfo.putInteger(0);
153
154 AlgorithmId[] algorithms = {aid};
155 Certificate[] chain = ks.getCertificateChain(alias);
156 X509Certificate[] signerCertificateChain;
157 X509Certificate signer = (X509Certificate)chain[0];
158
159 if (path.equals("fullchain")) { // Only case 5 uses full chain
160 signerCertificateChain = new X509Certificate[chain.length];
161 for (int i=0; i<chain.length; i++) {
162 signerCertificateChain[i] = (X509Certificate)chain[i];
163 }
164 } else if (path.equals("nocert")) {
165 signerCertificateChain = new X509Certificate[0];
166 } else {
167 signerCertificateChain = new X509Certificate[1];
168 signerCertificateChain[0] = (X509Certificate)chain[0];
169 }
170
171 DerOutputStream tst = new DerOutputStream();
172
173 tst.putInteger(1);
174 tst.putOID(policyId);
175
176 if (!path.equals("baddigest") && !path.equals("diffalg")) {
177 tst.putDerValue(messageImprint);
178 } else {
179 byte[] data = messageImprint.toByteArray();
180 if (path.equals("diffalg")) {
181 data[6] = (byte)0x01;
182 } else {
183 data[data.length-1] = (byte)0x01;
184 data[data.length-2] = (byte)0x02;
185 data[data.length-3] = (byte)0x03;
186 }
187 tst.write(data);
188 }
189
190 tst.putInteger(1);
191
192 Calendar cal = Calendar.getInstance();
193 tst.putGeneralizedTime(cal.getTime());
194
195 if (path.equals("diffnonce")) {
196 tst.putInteger(1234);
197 } else if (path.equals("nononce")) {
198 // no noce
199 } else {
200 tst.putInteger(nonce);
201 }
202
203 DerOutputStream tstInfo = new DerOutputStream();
204 tstInfo.write(DerValue.tag_Sequence, tst);
205
206 DerOutputStream tstInfo2 = new DerOutputStream();
207 tstInfo2.putOctetString(tstInfo.toByteArray());
208
209 // Always use the same algorithm at timestamp signing
210 // so it is different from the hash algorithm.
211 Signature sig = Signature.getInstance("SHA1withRSA");
212 sig.initSign((PrivateKey)(ks.getKey(
213 alias, "changeit".toCharArray())));
214 sig.update(tstInfo.toByteArray());
215
216 ContentInfo contentInfo = new ContentInfo(new ObjectIdentifier(
217 "1.2.840.113549.1.9.16.1.4"),
218 new DerValue(tstInfo2.toByteArray()));
219
220 System.err.println("Signing...");
221 System.err.println(new X500Name(signer
222 .getIssuerX500Principal().getName()));
223 System.err.println(signer.getSerialNumber());
224
225 SignerInfo signerInfo = new SignerInfo(
226 new X500Name(signer.getIssuerX500Principal().getName()),
227 signer.getSerialNumber(),
228 AlgorithmId.get("SHA-1"), AlgorithmId.get("RSA"), sig.sign());
229
230 SignerInfo[] signerInfos = {signerInfo};
231 PKCS7 p7 = new PKCS7(algorithms, contentInfo,
232 signerCertificateChain, signerInfos);
233 ByteArrayOutputStream p7out = new ByteArrayOutputStream();
234 p7.encodeSignedData(p7out);
235
236 DerOutputStream response = new DerOutputStream();
237 response.write(DerValue.tag_Sequence, statusInfo);
238 response.putDerValue(new DerValue(p7out.toByteArray()));
239
240 DerOutputStream out = new DerOutputStream();
241 out.write(DerValue.tag_Sequence, response);
242
243 return out.toByteArray();
244 }
245
246 private Handler(HttpServer httpServer, String keystore) {
247 this.httpServer = httpServer;
248 this.keystore = keystore;
249 }
250
251 /**
252 * Initialize TSA instance.
272 /**
273 * Stop TSA service.
274 */
275 void stop() {
276 httpServer.stop(0);
277 }
278
279 /**
280 * Return server port number.
281 */
282 int getPort() {
283 return httpServer.getAddress().getPort();
284 }
285
286 @Override
287 public void close() throws Exception {
288 stop();
289 }
290 }
291
292 public static void main(String[] args) throws Throwable {
293
294 prepare();
295
296 try (Handler tsa = Handler.init(0, "tsks");) {
297 tsa.start();
298 int port = tsa.getPort();
299 host = "http://localhost:" + port + "/";
300
301 if (args.length == 0) { // Run this test
302 sign("none")
303 .shouldContain("is not timestamped")
304 .shouldHaveExitValue(0);
305
306 sign("badku")
307 .shouldHaveExitValue(0);
308 checkBadKU("badku.jar");
309
310 sign("normal")
311 .shouldNotContain("is not timestamped")
312 .shouldHaveExitValue(0);
313
314 sign("nononce")
315 .shouldHaveExitValue(1);
316 sign("diffnonce")
317 .shouldHaveExitValue(1);
318 sign("baddigest")
319 .shouldHaveExitValue(1);
320 sign("diffalg")
321 .shouldHaveExitValue(1);
322 sign("fullchain")
323 .shouldHaveExitValue(0); // Success, 6543440 solved.
324 sign("bad1")
325 .shouldHaveExitValue(1);
326 sign("bad2")
327 .shouldHaveExitValue(1);
328 sign("bad3")
329 .shouldHaveExitValue(1);
330 sign("nocert")
331 .shouldHaveExitValue(1);
332
333 sign("policy", "-tsapolicyid", "1.2.3")
334 .shouldHaveExitValue(0);
335 checkTimestamp("policy.jar", "1.2.3", "SHA-256");
336
337 sign("diffpolicy", "-tsapolicyid", "1.2.3")
338 .shouldHaveExitValue(1);
339
340 sign("tsaalg", "-tsadigestalg", "SHA")
341 .shouldHaveExitValue(0);
342 checkTimestamp("tsaalg.jar", defaultPolicyId, "SHA-1");
343
344 sign("weak", "-digestalg", "MD2",
345 "-sigalg", "MD2withRSA", "-tsadigestalg", "MD2")
346 .shouldHaveExitValue(0)
347 .shouldMatch("MD2.*-digestalg.*risk")
348 .shouldMatch("MD2.*-tsadigestalg.*risk")
349 .shouldMatch("MD2withRSA.*-sigalg.*risk");
350 checkWeak("weak.jar");
351
352 // Using MD5 is a warning in signing but still accepted
353 // as signed in verification
354 sign("semiweak", "-digestalg", "MD5",
355 "-sigalg", "MD5withRSA", "-tsadigestalg", "MD5")
356 .shouldHaveExitValue(0)
357 .shouldMatch("MD5.*-digestalg.*risk")
358 .shouldMatch("MD5.*-tsadigestalg.*risk")
359 .shouldMatch("MD5withRSA.*-sigalg.*risk");
360 verify("semiweak.jar")
361 .shouldHaveExitValue(0)
362 .shouldContain("jar verified");
363
364 // When .SF or .RSA is missing or invalid
365 checkMissingOrInvalidFiles("normal.jar");
366 } else { // Run as a standalone server
367 System.err.println("Press Enter to quit server");
368 System.in.read();
369 }
370 }
371 }
372
373 private static void checkMissingOrInvalidFiles(String s)
374 throws Throwable {
375 JarUtils.updateJar(s, "1.jar", "-", "META-INF/OLD.SF");
376 verify("1.jar", "-verbose")
377 .shouldHaveExitValue(0)
378 .shouldContain("treated as unsigned")
379 .shouldContain("Missing signature-related file META-INF/OLD.SF");
380 JarUtils.updateJar(s, "2.jar", "-", "META-INF/OLD.RSA");
381 verify("2.jar", "-verbose")
382 .shouldHaveExitValue(0)
383 .shouldContain("treated as unsigned")
384 .shouldContain("Missing block file for signature-related file META-INF/OLD.SF");
385 JarUtils.updateJar(s, "3.jar", "META-INF/OLD.SF");
386 verify("3.jar", "-verbose")
387 .shouldHaveExitValue(0)
388 .shouldContain("treated as unsigned")
389 .shouldContain("Unparsable signature-related file META-INF/OLD.SF");
390 JarUtils.updateJar(s, "4.jar", "META-INF/OLD.RSA");
391 verify("4.jar", "-verbose")
392 .shouldHaveExitValue(0)
393 .shouldContain("treated as unsigned")
394 .shouldContain("Unparsable signature-related file META-INF/OLD.RSA");
395 }
396
397 static OutputAnalyzer jarsigner(List<String> extra)
398 throws Throwable {
399 JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner")
400 .addVMArg("-Duser.language=en")
401 .addVMArg("-Duser.country=US")
402 .addToolArg("-keystore")
403 .addToolArg("tsks")
404 .addToolArg("-storepass")
405 .addToolArg("changeit");
406 for (String s : extra) {
407 if (s.startsWith("-J")) {
408 launcher.addVMArg(s.substring(2));
409 } else {
410 launcher.addToolArg(s);
411 }
412 }
413 return ProcessTools.executeCommand(launcher.getCommand());
414 }
415
416 static OutputAnalyzer verify(String file, String... extra)
417 throws Throwable {
418 List<String> args = new ArrayList<>();
419 args.add("-verify");
420 args.add(file);
421 args.addAll(Arrays.asList(extra));
422 return jarsigner(args);
423 }
424
425 static void checkBadKU(String file) throws Throwable {
426 verify(file)
427 .shouldHaveExitValue(0)
428 .shouldContain("treated as unsigned")
429 .shouldContain("re-run jarsigner with debug enabled");
430 verify(file, "-verbose")
431 .shouldHaveExitValue(0)
432 .shouldContain("Signed by")
433 .shouldContain("treated as unsigned")
434 .shouldContain("re-run jarsigner with debug enabled");
435 verify(file, "-J-Djava.security.debug=jar")
436 .shouldHaveExitValue(0)
437 .shouldContain("SignatureException: Key usage restricted")
438 .shouldContain("treated as unsigned")
439 .shouldContain("re-run jarsigner with debug enabled");
440 }
441
442 static void checkWeak(String file) throws Throwable {
443 verify(file)
444 .shouldHaveExitValue(0)
445 .shouldContain("treated as unsigned")
446 .shouldMatch("weak algorithm that is now disabled.")
447 .shouldMatch("Re-run jarsigner with the -verbose option for more details");
448 verify(file, "-verbose")
449 .shouldHaveExitValue(0)
450 .shouldContain("treated as unsigned")
451 .shouldMatch("weak algorithm that is now disabled by")
452 .shouldMatch("Digest algorithm: .*weak")
453 .shouldMatch("Signature algorithm: .*weak")
454 .shouldMatch("Timestamp digest algorithm: .*weak")
455 .shouldNotMatch("Timestamp signature algorithm: .*weak.*weak")
456 .shouldMatch("Timestamp signature algorithm: .*key.*weak");
457 verify(file, "-J-Djava.security.debug=jar")
458 .shouldHaveExitValue(0)
459 .shouldMatch("SignatureException:.*Disabled");
460 }
461
462 static void checkTimestamp(String file, String policyId, String digestAlg)
463 throws Exception {
464 try (JarFile jf = new JarFile(file)) {
465 JarEntry je = jf.getJarEntry("META-INF/OLD.RSA");
466 try (InputStream is = jf.getInputStream(je)) {
467 byte[] content = is.readAllBytes();
468 PKCS7 p7 = new PKCS7(content);
469 SignerInfo[] si = p7.getSignerInfos();
470 if (si == null || si.length == 0) {
471 throw new Exception("Not signed");
472 }
473 PKCS9Attribute p9 = si[0].getUnauthenticatedAttributes()
474 .getAttribute(PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
475 PKCS7 tsToken = new PKCS7((byte[]) p9.getValue());
476 TimestampToken tt =
477 new TimestampToken(tsToken.getContentInfo().getData());
478 if (!tt.getHashAlgorithm().toString().equals(digestAlg)) {
479 throw new Exception("Digest alg different");
480 }
481 if (!tt.getPolicyID().equals(policyId)) {
482 throw new Exception("policyId different");
483 }
484 }
485 }
486 }
487
488 static int which = 0;
489
490 /**
491 * @param extra more args given to jarsigner
492 */
493 static OutputAnalyzer sign(String path, String... extra)
494 throws Throwable {
495 which++;
496 System.err.println("\n>> Test #" + which + ": " + Arrays.toString(extra));
497 List<String> args = List.of("-J-Djava.security.egd=file:/dev/./urandom",
498 "-debug", "-signedjar", path + ".jar", "old.jar",
499 path.equals("badku") ? "badku" : "old");
500 args = new ArrayList<>(args);
501 if (!path.equals("none") && !path.equals("badku")) {
502 args.add("-tsa");
503 args.add(host + path);
504 }
505 args.addAll(Arrays.asList(extra));
506 return jarsigner(args);
507 }
508
509 static void prepare() throws Exception {
510 jdk.testlibrary.JarUtils.createJar("old.jar", "A");
511 Files.deleteIfExists(Paths.get("tsks"));
512 keytool("-alias ca -genkeypair -ext bc -dname CN=CA");
513 keytool("-alias old -genkeypair -dname CN=old");
514 keytool("-alias badku -genkeypair -dname CN=badku");
515 keytool("-alias ts -genkeypair -dname CN=ts");
516 keytool("-alias tsweak -genkeypair -keysize 512 -dname CN=tsbad1");
517 keytool("-alias tsbad1 -genkeypair -dname CN=tsbad1");
518 keytool("-alias tsbad2 -genkeypair -dname CN=tsbad2");
519 keytool("-alias tsbad3 -genkeypair -dname CN=tsbad3");
520
521 gencert("old");
522 gencert("badku", "-ext ku:critical=keyAgreement");
523 gencert("ts", "-ext eku:critical=ts");
524 gencert("tsweak", "-ext eku:critical=ts");
525 gencert("tsbad1");
526 gencert("tsbad2", "-ext eku=ts");
527 gencert("tsbad3", "-ext eku:critical=cs");
528 }
529
530 static void gencert(String alias, String... extra) throws Exception {
531 keytool("-alias " + alias + " -certreq -file " + alias + ".req");
532 String genCmd = "-gencert -alias ca -infile " +
533 alias + ".req -outfile " + alias + ".cert";
534 for (String s : extra) {
535 genCmd += " " + s;
536 }
537 keytool(genCmd);
538 keytool("-alias " + alias + " -importcert -file " + alias + ".cert");
539 }
540
541 static void keytool(String cmd) throws Exception {
542 cmd = "-keystore tsks -storepass changeit -keypass changeit " +
543 "-keyalg rsa -validity 200 " + cmd;
544 sun.security.tools.keytool.Main.main(cmd.split(" "));
545 }
546 }
|