TimestampCheck.java 28.8 KB
Newer Older
1
/*
2
 * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
19 20 21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
22 23 24
 */

import com.sun.net.httpserver.*;
25 26

import java.io.ByteArrayInputStream;
27
import java.io.ByteArrayOutputStream;
28
import java.io.File;
29 30
import java.io.FileInputStream;
import java.io.IOException;
31
import java.io.InputStream;
32 33 34
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.InetSocketAddress;
35 36
import java.nio.file.Files;
import java.nio.file.Paths;
37 38 39 40
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.Certificate;
41 42
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
43
import java.security.cert.X509Certificate;
44 45 46
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
47 48 49 50
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import sun.misc.IOUtils;
51 52
import jdk.testlibrary.SecurityTools;
import jdk.testlibrary.OutputAnalyzer;
53
import jdk.testlibrary.JarUtils;
54 55
import sun.security.pkcs.ContentInfo;
import sun.security.pkcs.PKCS7;
56
import sun.security.pkcs.PKCS9Attribute;
57
import sun.security.pkcs.SignerInfo;
58
import sun.security.timestamp.TimestampToken;
59 60 61 62 63 64
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X500Name;

65 66
import jdk.testlibrary.Utils;

67 68
/*
 * @test
R
rpatil 已提交
69
 * @bug 6543842 6543440 6939248 8009636 8024302 8163304 8169911 8169688 8171121
70
 *      8180289
71 72 73 74 75 76 77
 * @summary checking response of timestamp
 * @modules java.base/sun.security.pkcs
 *          java.base/sun.security.timestamp
 *          java.base/sun.security.x509
 *          java.base/sun.security.util
 *          java.base/sun.security.tools.keytool
 * @library /lib/testlibrary
78
 * @run main/othervm/timeout=600 TimestampCheck
79
 */
80 81
public class TimestampCheck {

82 83
    static final String defaultPolicyId = "2.3.4";
    static String host = null;
84

85 86 87 88 89 90
    static class Handler implements HttpHandler, AutoCloseable {

        private final HttpServer httpServer;
        private final String keystore;

        @Override
91 92 93 94 95 96 97 98 99 100 101
        public void handle(HttpExchange t) throws IOException {
            int len = 0;
            for (String h: t.getRequestHeaders().keySet()) {
                if (h.equalsIgnoreCase("Content-length")) {
                    len = Integer.valueOf(t.getRequestHeaders().get(h).get(0));
                }
            }
            byte[] input = new byte[len];
            t.getRequestBody().read(input);

            try {
102
                String path = t.getRequestURI().getPath().substring(1);
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
                byte[] output = sign(input, path);
                Headers out = t.getResponseHeaders();
                out.set("Content-Type", "application/timestamp-reply");

                t.sendResponseHeaders(200, output.length);
                OutputStream os = t.getResponseBody();
                os.write(output);
            } catch (Exception e) {
                e.printStackTrace();
                t.sendResponseHeaders(500, 0);
            }
            t.close();
        }

        /**
         * @param input The data to sign
         * @param path different cases to simulate, impl on URL path
         * @returns the signed
         */
122
        byte[] sign(byte[] input, String path) throws Exception {
123
            DerValue value = new DerValue(input);
124 125
            System.out.println("\nIncoming Request\n===================");
            System.out.println("Version: " + value.data.getInteger());
126 127 128
            DerValue messageImprint = value.data.getDerValue();
            AlgorithmId aid = AlgorithmId.parse(
                    messageImprint.data.getDerValue());
129
            System.out.println("AlgorithmId: " + aid);
130

131
            ObjectIdentifier policyId = new ObjectIdentifier(defaultPolicyId);
132 133 134 135 136
            BigInteger nonce = null;
            while (value.data.available() > 0) {
                DerValue v = value.data.getDerValue();
                if (v.tag == DerValue.tag_Integer) {
                    nonce = v.getBigInteger();
137
                    System.out.println("nonce: " + nonce);
138
                } else if (v.tag == DerValue.tag_Boolean) {
139
                    System.out.println("certReq: " + v.getBoolean());
140 141
                } else if (v.tag == DerValue.tag_ObjectId) {
                    policyId = v.getOID();
142
                    System.out.println("PolicyID: " + policyId);
143 144 145
                }
            }

146
            System.out.println("\nResponse\n===================");
147 148 149 150
            FileInputStream is = new FileInputStream(keystore);
            KeyStore ks = KeyStore.getInstance("JCEKS");
            ks.load(is, "changeit".toCharArray());
            is.close();
151

152 153 154
            // If path starts with "ts", use the TSA it points to.
            // Otherwise, always use "ts".
            String alias = path.startsWith("ts") ? path : "ts";
155

156
            if (path.equals("diffpolicy")) {
157 158 159
                policyId = new ObjectIdentifier(defaultPolicyId);
            }

160 161 162 163 164
            DerOutputStream statusInfo = new DerOutputStream();
            statusInfo.putInteger(0);

            AlgorithmId[] algorithms = {aid};
            Certificate[] chain = ks.getCertificateChain(alias);
165
            X509Certificate[] signerCertificateChain;
166
            X509Certificate signer = (X509Certificate)chain[0];
167 168

            if (path.equals("fullchain")) {   // Only case 5 uses full chain
169 170 171 172
                signerCertificateChain = new X509Certificate[chain.length];
                for (int i=0; i<chain.length; i++) {
                    signerCertificateChain[i] = (X509Certificate)chain[i];
                }
173
            } else if (path.equals("nocert")) {
174 175 176 177 178 179 180 181 182
                signerCertificateChain = new X509Certificate[0];
            } else {
                signerCertificateChain = new X509Certificate[1];
                signerCertificateChain[0] = (X509Certificate)chain[0];
            }

            DerOutputStream tst = new DerOutputStream();

            tst.putInteger(1);
183
            tst.putOID(policyId);
184

185
            if (!path.equals("baddigest") && !path.equals("diffalg")) {
186 187 188
                tst.putDerValue(messageImprint);
            } else {
                byte[] data = messageImprint.toByteArray();
189
                if (path.equals("diffalg")) {
190 191 192 193 194 195 196 197 198 199 200
                    data[6] = (byte)0x01;
                } else {
                    data[data.length-1] = (byte)0x01;
                    data[data.length-2] = (byte)0x02;
                    data[data.length-3] = (byte)0x03;
                }
                tst.write(data);
            }

            tst.putInteger(1);

201 202 203 204 205
            Instant instant = Instant.now();
            if (path.equals("tsold")) {
                instant = instant.minus(20, ChronoUnit.DAYS);
            }
            tst.putGeneralizedTime(Date.from(instant));
206

207
            if (path.equals("diffnonce")) {
208
                tst.putInteger(1234);
209 210
            } else if (path.equals("nononce")) {
                // no noce
211 212 213 214 215 216 217 218 219 220
            } else {
                tst.putInteger(nonce);
            }

            DerOutputStream tstInfo = new DerOutputStream();
            tstInfo.write(DerValue.tag_Sequence, tst);

            DerOutputStream tstInfo2 = new DerOutputStream();
            tstInfo2.putOctetString(tstInfo.toByteArray());

221 222
            // Always use the same algorithm at timestamp signing
            // so it is different from the hash algorithm.
223
            Signature sig = Signature.getInstance("SHA1withRSA");
224 225 226 227 228 229 230 231
            sig.initSign((PrivateKey)(ks.getKey(
                    alias, "changeit".toCharArray())));
            sig.update(tstInfo.toByteArray());

            ContentInfo contentInfo = new ContentInfo(new ObjectIdentifier(
                    "1.2.840.113549.1.9.16.1.4"),
                    new DerValue(tstInfo2.toByteArray()));

232 233
            System.out.println("Signing...");
            System.out.println(new X500Name(signer
234
                    .getIssuerX500Principal().getName()));
235
            System.out.println(signer.getSerialNumber());
236 237 238 239

            SignerInfo signerInfo = new SignerInfo(
                    new X500Name(signer.getIssuerX500Principal().getName()),
                    signer.getSerialNumber(),
240
                    AlgorithmId.get("SHA-1"), AlgorithmId.get("RSA"), sig.sign());
241 242

            SignerInfo[] signerInfos = {signerInfo};
243 244
            PKCS7 p7 = new PKCS7(algorithms, contentInfo,
                    signerCertificateChain, signerInfos);
245 246 247 248 249 250 251 252 253 254 255 256 257
            ByteArrayOutputStream p7out = new ByteArrayOutputStream();
            p7.encodeSignedData(p7out);

            DerOutputStream response = new DerOutputStream();
            response.write(DerValue.tag_Sequence, statusInfo);
            response.putDerValue(new DerValue(p7out.toByteArray()));

            DerOutputStream out = new DerOutputStream();
            out.write(DerValue.tag_Sequence, response);

            return out.toByteArray();
        }

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
        private Handler(HttpServer httpServer, String keystore) {
            this.httpServer = httpServer;
            this.keystore = keystore;
        }

        /**
         * Initialize TSA instance.
         *
         * Extended Key Info extension of certificate that is used for
         * signing TSA responses should contain timeStamping value.
         */
        static Handler init(int port, String keystore) throws IOException {
            HttpServer httpServer = HttpServer.create(
                    new InetSocketAddress(port), 0);
            Handler tsa = new Handler(httpServer, keystore);
            httpServer.createContext("/", tsa);
            return tsa;
        }
276

277 278 279 280 281
        /**
         * Start TSA service.
         */
        void start() {
            httpServer.start();
282 283
        }

284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
        /**
         * Stop TSA service.
         */
        void stop() {
            httpServer.stop(0);
        }

        /**
         * Return server port number.
         */
        int getPort() {
            return httpServer.getAddress().getPort();
        }

        @Override
        public void close() throws Exception {
            stop();
        }
    }
303 304 305 306 307

    public static void main(String[] args) throws Throwable {

        prepare();

308
        try (Handler tsa = Handler.init(0, "ks");) {
309 310 311
            tsa.start();
            int port = tsa.getPort();

312
            host = "http://localhost:" + port + "/";
313

314
            if (args.length == 0) {         // Run this test
315 316 317

                sign("normal")
                        .shouldNotContain("Warning")
318 319
                        .shouldHaveExitValue(0);

320 321
                verify("normal.jar")
                        .shouldNotContain("Warning")
322 323
                        .shouldHaveExitValue(0);

324 325 326 327 328 329 330 331 332 333
                // Simulate signing at a previous date:
                // 1. tsold will create a timestamp of 20 days ago.
                // 2. oldsigner expired 10 days ago.
                // jarsigner will show a warning at signing.
                signVerbose("tsold", "unsigned.jar", "tsold.jar", "oldsigner")
                        .shouldHaveExitValue(4);

                // It verifies perfectly.
                verify("tsold.jar", "-verbose", "-certs")
                        .shouldNotContain("Warning")
334 335
                        .shouldHaveExitValue(0);

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
                signVerbose(null, "unsigned.jar", "none.jar", "signer")
                        .shouldContain("is not timestamped")
                        .shouldHaveExitValue(0);

                signVerbose(null, "unsigned.jar", "badku.jar", "badku")
                        .shouldHaveExitValue(8);
                checkBadKU("badku.jar");

                // 8180289: unvalidated TSA cert chain
                sign("tsnoca")
                        .shouldContain("TSA certificate chain is invalid")
                        .shouldHaveExitValue(64);

                verify("tsnoca.jar", "-verbose", "-certs")
                        .shouldHaveExitValue(64)
                        .shouldContain("jar verified")
                        .shouldContain("Invalid TSA certificate chain")
                        .shouldContain("TSA certificate chain is invalid");

355 356 357 358 359 360 361 362 363 364
                sign("nononce")
                        .shouldHaveExitValue(1);
                sign("diffnonce")
                        .shouldHaveExitValue(1);
                sign("baddigest")
                        .shouldHaveExitValue(1);
                sign("diffalg")
                        .shouldHaveExitValue(1);
                sign("fullchain")
                        .shouldHaveExitValue(0);   // Success, 6543440 solved.
365
                sign("tsbad1")
366
                        .shouldHaveExitValue(1);
367
                sign("tsbad2")
368
                        .shouldHaveExitValue(1);
369
                sign("tsbad3")
370 371 372 373 374 375 376 377 378 379 380
                        .shouldHaveExitValue(1);
                sign("nocert")
                        .shouldHaveExitValue(1);

                sign("policy", "-tsapolicyid",  "1.2.3")
                        .shouldHaveExitValue(0);
                checkTimestamp("policy.jar", "1.2.3", "SHA-256");

                sign("diffpolicy", "-tsapolicyid", "1.2.3")
                        .shouldHaveExitValue(1);

381
                sign("sha1alg", "-tsadigestalg", "SHA")
382
                        .shouldHaveExitValue(0);
383
                checkTimestamp("sha1alg.jar", defaultPolicyId, "SHA-1");
384

385
                sign("tsweak", "-digestalg", "MD5",
R
rpatil 已提交
386
                                "-sigalg", "MD5withRSA", "-tsadigestalg", "MD5")
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
                        .shouldHaveExitValue(68);
                checkWeak("tsweak.jar");

                signVerbose("tsweak", "unsigned.jar", "tsweak2.jar", "signer")
                        .shouldHaveExitValue(64)
                        .shouldContain("TSA certificate chain is invalid");

                // Weak timestamp is an error and jar treated unsigned
                verify("tsweak2.jar", "-verbose")
                        .shouldHaveExitValue(16)
                        .shouldContain("treated as unsigned")
                        .shouldMatch("Timestamp.*512.*weak");

                signVerbose("normal", "unsigned.jar", "halfWeak.jar", "signer",
                        "-digestalg", "MD5")
                        .shouldHaveExitValue(4);
403 404 405
                checkHalfWeak("halfWeak.jar");

                // sign with DSA key
406
                signVerbose("normal", "unsigned.jar", "sign1.jar", "dsakey")
407 408
                        .shouldHaveExitValue(0);
                // sign with RSAkeysize < 1024
409 410
                signVerbose("normal", "sign1.jar", "sign2.jar", "weakkeysize")
                        .shouldHaveExitValue(4);
411 412
                checkMultiple("sign2.jar");

413 414
                // When .SF or .RSA is missing or invalid
                checkMissingOrInvalidFiles("normal.jar");
415 416 417 418

                if (Files.exists(Paths.get("ts2.cert"))) {
                    checkInvalidTsaCertKeyUsage();
                }
419
            } else {                        // Run as a standalone server
420
                System.out.println("Press Enter to quit server");
421 422
                System.in.read();
            }
423 424 425
        }
    }

426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
    private static void checkInvalidTsaCertKeyUsage() throws Exception {

        // Hack: Rewrite the TSA cert inside normal.jar into ts2.jar.

        // Both the cert and the serial number must be rewritten.
        byte[] tsCert = Files.readAllBytes(Paths.get("ts.cert"));
        byte[] ts2Cert = Files.readAllBytes(Paths.get("ts2.cert"));
        byte[] tsSerial = getCert(tsCert)
                .getSerialNumber().toByteArray();
        byte[] ts2Serial = getCert(ts2Cert)
                .getSerialNumber().toByteArray();

        byte[] oldBlock;
        try (JarFile normal = new JarFile("normal.jar")) {
            oldBlock = Utils.readAllBytes(normal.getInputStream(
                    normal.getJarEntry("META-INF/SIGNER.RSA")));
        }

        JarUtils.updateJar("normal.jar", "ts2.jar",
                mapOf("META-INF/SIGNER.RSA",
                        updateBytes(updateBytes(oldBlock, tsCert, ts2Cert),
                                tsSerial, ts2Serial)));

        verify("ts2.jar", "-verbose", "-certs")
                .shouldHaveExitValue(64)
                .shouldContain("jar verified")
                .shouldContain("Invalid TSA certificate chain: Extended key usage does not permit use for TSA server");
    }

    public static X509Certificate getCert(byte[] data)
            throws CertificateException, IOException {
        return (X509Certificate)
                CertificateFactory.getInstance("X.509")
                        .generateCertificate(new ByteArrayInputStream(data));
    }

    private static byte[] updateBytes(byte[] old, byte[] from, byte[] to) {
        int pos = 0;
        while (true) {
            if (pos + from.length > old.length) {
                return null;
            }
            if (Arrays.equals(Arrays.copyOfRange(old, pos, pos+from.length), from)) {
                byte[] result = old.clone();
                System.arraycopy(to, 0, result, pos, from.length);
                return result;
            }
            pos++;
        }
    }

477 478
    private static void checkMissingOrInvalidFiles(String s)
            throws Throwable {
479 480

        JarUtils.updateJar(s, "1.jar", mapOf("META-INF/SIGNER.SF", Boolean.FALSE));
481
        verify("1.jar", "-verbose")
482
                .shouldHaveExitValue(16)
483
                .shouldContain("treated as unsigned")
484 485
                .shouldContain("Missing signature-related file META-INF/SIGNER.SF");
        JarUtils.updateJar(s, "2.jar", mapOf("META-INF/SIGNER.RSA", Boolean.FALSE));
486
        verify("2.jar", "-verbose")
487
                .shouldHaveExitValue(16)
488
                .shouldContain("treated as unsigned")
489 490
                .shouldContain("Missing block file for signature-related file META-INF/SIGNER.SF");
        JarUtils.updateJar(s, "3.jar", mapOf("META-INF/SIGNER.SF", "dummy"));
491
        verify("3.jar", "-verbose")
492
                .shouldHaveExitValue(16)
493
                .shouldContain("treated as unsigned")
494 495
                .shouldContain("Unparsable signature-related file META-INF/SIGNER.SF");
        JarUtils.updateJar(s, "4.jar", mapOf("META-INF/SIGNER.RSA", "dummy"));
496
        verify("4.jar", "-verbose")
497
                .shouldHaveExitValue(16)
498
                .shouldContain("treated as unsigned")
499
                .shouldContain("Unparsable signature-related file META-INF/SIGNER.RSA");
500 501 502
    }

    static OutputAnalyzer jarsigner(List<String> extra)
503 504 505 506 507
            throws Exception {
        List<String> args = new ArrayList<>(
                listOf("-keystore", "ks", "-storepass", "changeit"));
        args.addAll(extra);
        return SecurityTools.jarsigner(args);
508 509 510
    }

    static OutputAnalyzer verify(String file, String... extra)
511
            throws Exception {
512 513
        List<String> args = new ArrayList<>();
        args.add("-verify");
514
        args.add("-strict");
515 516 517 518 519
        args.add(file);
        args.addAll(Arrays.asList(extra));
        return jarsigner(args);
    }

520
    static void checkBadKU(String file) throws Exception {
521 522
        System.err.println("BadKU: " + file);
        verify(file)
523
                .shouldHaveExitValue(16)
524 525 526
                .shouldContain("treated as unsigned")
                .shouldContain("re-run jarsigner with debug enabled");
        verify(file, "-verbose")
527
                .shouldHaveExitValue(16)
528 529 530 531
                .shouldContain("Signed by")
                .shouldContain("treated as unsigned")
                .shouldContain("re-run jarsigner with debug enabled");
        verify(file, "-J-Djava.security.debug=jar")
532
                .shouldHaveExitValue(16)
533 534 535 536 537
                .shouldContain("SignatureException: Key usage restricted")
                .shouldContain("treated as unsigned")
                .shouldContain("re-run jarsigner with debug enabled");
    }

538
    static void checkWeak(String file) throws Exception {
539
        verify(file)
540
                .shouldHaveExitValue(16)
541 542 543 544
                .shouldContain("treated as unsigned")
                .shouldMatch("weak algorithm that is now disabled.")
                .shouldMatch("Re-run jarsigner with the -verbose option for more details");
        verify(file, "-verbose")
545
                .shouldHaveExitValue(16)
546 547 548 549 550 551 552 553
                .shouldContain("treated as unsigned")
                .shouldMatch("weak algorithm that is now disabled by")
                .shouldMatch("Digest algorithm: .*weak")
                .shouldMatch("Signature algorithm: .*weak")
                .shouldMatch("Timestamp digest algorithm: .*weak")
                .shouldNotMatch("Timestamp signature algorithm: .*weak.*weak")
                .shouldMatch("Timestamp signature algorithm: .*key.*weak");
        verify(file, "-J-Djava.security.debug=jar")
554
                .shouldHaveExitValue(16)
555
                .shouldMatch("SignatureException:.*disabled");
556 557
    }

558
    static void checkHalfWeak(String file) throws Exception {
559
        verify(file)
560
                .shouldHaveExitValue(16)
561 562 563 564
                .shouldContain("treated as unsigned")
                .shouldMatch("weak algorithm that is now disabled.")
                .shouldMatch("Re-run jarsigner with the -verbose option for more details");
        verify(file, "-verbose")
565
                .shouldHaveExitValue(16)
566 567 568 569 570 571 572 573 574
                .shouldContain("treated as unsigned")
                .shouldMatch("weak algorithm that is now disabled by")
                .shouldMatch("Digest algorithm: .*weak")
                .shouldNotMatch("Signature algorithm: .*weak")
                .shouldNotMatch("Timestamp digest algorithm: .*weak")
                .shouldNotMatch("Timestamp signature algorithm: .*weak.*weak")
                .shouldNotMatch("Timestamp signature algorithm: .*key.*weak");
     }

575
    static void checkMultiple(String file) throws Exception {
576 577 578 579 580 581 582 583 584 585 586 587 588
        verify(file)
                .shouldHaveExitValue(0)
                .shouldContain("jar verified");
        verify(file, "-verbose", "-certs")
                .shouldHaveExitValue(0)
                .shouldContain("jar verified")
                .shouldMatch("X.509.*CN=dsakey")
                .shouldNotMatch("X.509.*CN=weakkeysize")
                .shouldMatch("Signed by .*CN=dsakey")
                .shouldMatch("Signed by .*CN=weakkeysize")
                .shouldMatch("Signature algorithm: .*key.*weak");
     }

589 590 591
    static void checkTimestamp(String file, String policyId, String digestAlg)
            throws Exception {
        try (JarFile jf = new JarFile(file)) {
592
            JarEntry je = jf.getJarEntry("META-INF/SIGNER.RSA");
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
            try (InputStream is = jf.getInputStream(je)) {
                byte[] content = IOUtils.readFully(is, -1, true);
                PKCS7 p7 = new PKCS7(content);
                SignerInfo[] si = p7.getSignerInfos();
                if (si == null || si.length == 0) {
                    throw new Exception("Not signed");
                }
                PKCS9Attribute p9 = si[0].getUnauthenticatedAttributes()
                        .getAttribute(PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
                PKCS7 tsToken = new PKCS7((byte[]) p9.getValue());
                TimestampToken tt =
                        new TimestampToken(tsToken.getContentInfo().getData());
                if (!tt.getHashAlgorithm().toString().equals(digestAlg)) {
                    throw new Exception("Digest alg different");
                }
                if (!tt.getPolicyID().equals(policyId)) {
                    throw new Exception("policyId different");
                }
            }
612 613 614
        }
    }

615 616
    static int which = 0;

617
    /**
618 619 620
     * Sign with a TSA path. Always use alias "signer" to sign "unsigned.jar".
     * The signed jar name is always path.jar.
     *
621
     * @param extra more args given to jarsigner
622
     */
623
    static OutputAnalyzer sign(String path, String... extra)
624 625 626 627 628 629 630
            throws Exception {
        return signVerbose(
                path,
                "unsigned.jar",
                path + ".jar",
                "signer",
                extra);
631 632
    }

633 634 635 636 637 638
    static OutputAnalyzer signVerbose(
            String path,    // TSA URL path
            String oldJar,
            String newJar,
            String alias,   // signer
            String...extra) throws Exception {
639
        which++;
640
        System.out.println("\n>> Test #" + which);
641
        List<String> args = new ArrayList<>();
642 643
        args.add("-strict");
        args.add("-verbose");
644 645
        args.add("-debug");
        args.add("-signedjar");
646 647
        args.add(newJar);
        args.add(oldJar);
648
        args.add(alias);
649
        if (path != null) {
650 651 652 653 654 655
            args.add("-tsa");
            args.add(host + path);
         }
        args.addAll(Arrays.asList(extra));
        return jarsigner(args);
    }
W
weijun 已提交
656

657
    static void prepare() throws Exception {
658 659 660 661
        JarUtils.createJar("unsigned.jar", "A");
        Files.deleteIfExists(Paths.get("ks"));
        keytool("-alias signer -genkeypair -ext bc -dname CN=signer");
        keytool("-alias oldsigner -genkeypair -dname CN=oldsigner");
662 663
        keytool("-alias dsakey -genkeypair -keyalg DSA -dname CN=dsakey");
        keytool("-alias weakkeysize -genkeypair -keysize 512 -dname CN=weakkeysize");
664 665
        keytool("-alias badku -genkeypair -dname CN=badku");
        keytool("-alias ts -genkeypair -dname CN=ts");
666 667
        keytool("-alias tsold -genkeypair -dname CN=tsold");
        keytool("-alias tsweak -genkeypair -keysize 512 -dname CN=tsweak");
668 669 670
        keytool("-alias tsbad1 -genkeypair -dname CN=tsbad1");
        keytool("-alias tsbad2 -genkeypair -dname CN=tsbad2");
        keytool("-alias tsbad3 -genkeypair -dname CN=tsbad3");
671 672 673 674 675 676 677
        keytool("-alias tsnoca -genkeypair -dname CN=tsnoca");

        // tsnoca's issuer will be removed from keystore later
        keytool("-alias ca -genkeypair -ext bc -dname CN=CA");
        gencert("tsnoca", "-ext eku:critical=ts");
        keytool("-delete -alias ca");
        keytool("-alias ca -genkeypair -ext bc -dname CN=CA -startdate -40d");
678

679 680
        gencert("signer");
        gencert("oldsigner", "-startdate -30d -validity 20");
681 682
        gencert("dsakey");
        gencert("weakkeysize");
683 684
        gencert("badku", "-ext ku:critical=keyAgreement");
        gencert("ts", "-ext eku:critical=ts");
685 686 687


        for (int i = 0; i < 5; i++) {
688 689 690 691 692 693 694 695
            // Issue another cert for "ts" with a different EKU.
            // Length might be different because serial number is
            // random. Try several times until a cert with the same
            // length is generated so we can substitute ts.cert
            // embedded in the PKCS7 block with ts2.cert.
            // If cannot create one, related test will be ignored.
            keytool("-gencert -alias ca -infile ts.req -outfile ts2.cert " +
                    "-ext eku:critical=1.3.6.1.5.5.7.3.9");
696 697 698 699 700 701 702 703 704 705
            if (Files.size(Paths.get("ts.cert")) != Files.size(Paths.get("ts2.cert"))) {
                Files.delete(Paths.get("ts2.cert"));
                System.out.println("Warning: cannot create same length");
            } else {
                break;
            }
        }

        gencert("tsold", "-ext eku:critical=ts -startdate -40d -validity 45");

706 707 708 709 710 711 712 713 714 715 716 717
        gencert("tsweak", "-ext eku:critical=ts");
        gencert("tsbad1");
        gencert("tsbad2", "-ext eku=ts");
        gencert("tsbad3", "-ext eku:critical=cs");
    }

    static void gencert(String alias, String... extra) throws Exception {
        keytool("-alias " + alias + " -certreq -file " + alias + ".req");
        String genCmd = "-gencert -alias ca -infile " +
                alias + ".req -outfile " + alias + ".cert";
        for (String s : extra) {
            genCmd += " " + s;
W
weijun 已提交
718
        }
719 720 721 722 723
        keytool(genCmd);
        keytool("-alias " + alias + " -importcert -file " + alias + ".cert");
    }

    static void keytool(String cmd) throws Exception {
724
        cmd = "-keystore ks -storepass changeit -keypass changeit " +
725 726
                "-keyalg rsa -validity 200 " + cmd;
        sun.security.tools.keytool.Main.main(cmd.split(" "));
727
    }
728 729 730 731 732 733 734 735

    static <K,V> Map<K,V> mapOf(K k1, V v1) {
        return Collections.singletonMap(k1, v1);
    }

    static <E> List<E> listOf(E... elements) {
        return Arrays.asList(elements);
    }
736
}