Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java
38853 views
/*1* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import com.sun.net.httpserver.*;2425import java.io.ByteArrayInputStream;26import java.io.ByteArrayOutputStream;27import java.io.File;28import java.io.FileInputStream;29import java.io.IOException;30import java.io.InputStream;31import java.io.OutputStream;32import java.math.BigInteger;33import java.net.InetSocketAddress;34import java.nio.file.Files;35import java.nio.file.Paths;36import java.security.KeyStore;37import java.security.PrivateKey;38import java.security.Signature;39import java.security.cert.Certificate;40import java.security.cert.CertificateException;41import java.security.cert.CertificateFactory;42import java.security.cert.X509Certificate;43import java.time.Instant;44import java.time.temporal.ChronoUnit;45import java.util.*;46import java.util.jar.JarEntry;47import java.util.jar.JarFile;4849import sun.misc.IOUtils;50import jdk.testlibrary.SecurityTools;51import jdk.testlibrary.OutputAnalyzer;52import jdk.testlibrary.JarUtils;53import sun.security.pkcs.ContentInfo;54import sun.security.pkcs.PKCS7;55import sun.security.pkcs.PKCS9Attribute;56import sun.security.pkcs.SignerInfo;57import sun.security.timestamp.TimestampToken;58import sun.security.util.DerOutputStream;59import sun.security.util.DerValue;60import sun.security.util.ObjectIdentifier;61import sun.security.x509.AlgorithmId;62import sun.security.x509.X500Name;6364import jdk.testlibrary.Utils;6566/*67* @test68* @bug 6543842 6543440 6939248 8009636 8024302 8163304 8169911 8169688 817112169* 8180289 817240470* @summary checking response of timestamp71* @modules java.base/sun.security.pkcs72* java.base/sun.security.timestamp73* java.base/sun.security.x50974* java.base/sun.security.util75* java.base/sun.security.tools.keytool76* @library /lib/testlibrary77* @compile -XDignore.symbol.file TimestampCheck.java78* @run main/othervm/timeout=600 TimestampCheck79*/80public class TimestampCheck {8182static final String defaultPolicyId = "2.3.4";83static String host = null;8485static class Handler implements HttpHandler, AutoCloseable {8687private final HttpServer httpServer;88private final String keystore;8990@Override91public void handle(HttpExchange t) throws IOException {92int len = 0;93for (String h: t.getRequestHeaders().keySet()) {94if (h.equalsIgnoreCase("Content-length")) {95len = Integer.valueOf(t.getRequestHeaders().get(h).get(0));96}97}98byte[] input = new byte[len];99t.getRequestBody().read(input);100101try {102String path = t.getRequestURI().getPath().substring(1);103byte[] output = sign(input, path);104Headers out = t.getResponseHeaders();105out.set("Content-Type", "application/timestamp-reply");106107t.sendResponseHeaders(200, output.length);108OutputStream os = t.getResponseBody();109os.write(output);110} catch (Exception e) {111e.printStackTrace();112t.sendResponseHeaders(500, 0);113}114t.close();115}116117/**118* @param input The data to sign119* @param path different cases to simulate, impl on URL path120* @returns the signed121*/122byte[] sign(byte[] input, String path) throws Exception {123DerValue value = new DerValue(input);124System.out.println("#\n# Incoming Request\n===================");125System.out.println("# Version: " + value.data.getInteger());126DerValue messageImprint = value.data.getDerValue();127AlgorithmId aid = AlgorithmId.parse(128messageImprint.data.getDerValue());129System.out.println("# AlgorithmId: " + aid);130131ObjectIdentifier policyId = new ObjectIdentifier(defaultPolicyId);132BigInteger nonce = null;133while (value.data.available() > 0) {134DerValue v = value.data.getDerValue();135if (v.tag == DerValue.tag_Integer) {136nonce = v.getBigInteger();137System.out.println("# nonce: " + nonce);138} else if (v.tag == DerValue.tag_Boolean) {139System.out.println("# certReq: " + v.getBoolean());140} else if (v.tag == DerValue.tag_ObjectId) {141policyId = v.getOID();142System.out.println("# PolicyID: " + policyId);143}144}145146System.out.println("#\n# Response\n===================");147FileInputStream is = new FileInputStream(keystore);148KeyStore ks = KeyStore.getInstance("JCEKS");149ks.load(is, "changeit".toCharArray());150is.close();151152// If path starts with "ts", use the TSA it points to.153// Otherwise, always use "ts".154String alias = path.startsWith("ts") ? path : "ts";155156if (path.equals("diffpolicy")) {157policyId = new ObjectIdentifier(defaultPolicyId);158}159160DerOutputStream statusInfo = new DerOutputStream();161statusInfo.putInteger(0);162163AlgorithmId[] algorithms = {aid};164Certificate[] chain = ks.getCertificateChain(alias);165X509Certificate[] signerCertificateChain;166X509Certificate signer = (X509Certificate)chain[0];167168if (path.equals("fullchain")) { // Only case 5 uses full chain169signerCertificateChain = new X509Certificate[chain.length];170for (int i=0; i<chain.length; i++) {171signerCertificateChain[i] = (X509Certificate)chain[i];172}173} else if (path.equals("nocert")) {174signerCertificateChain = new X509Certificate[0];175} else {176signerCertificateChain = new X509Certificate[1];177signerCertificateChain[0] = (X509Certificate)chain[0];178}179180DerOutputStream tst = new DerOutputStream();181182tst.putInteger(1);183tst.putOID(policyId);184185if (!path.equals("baddigest") && !path.equals("diffalg")) {186tst.putDerValue(messageImprint);187} else {188byte[] data = messageImprint.toByteArray();189if (path.equals("diffalg")) {190data[6] = (byte)0x01;191} else {192data[data.length-1] = (byte)0x01;193data[data.length-2] = (byte)0x02;194data[data.length-3] = (byte)0x03;195}196tst.write(data);197}198199tst.putInteger(1);200201Instant instant = Instant.now();202if (path.equals("tsold")) {203instant = instant.minus(20, ChronoUnit.DAYS);204}205tst.putGeneralizedTime(Date.from(instant));206207if (path.equals("diffnonce")) {208tst.putInteger(1234);209} else if (path.equals("nononce")) {210// no noce211} else {212tst.putInteger(nonce);213}214215DerOutputStream tstInfo = new DerOutputStream();216tstInfo.write(DerValue.tag_Sequence, tst);217218DerOutputStream tstInfo2 = new DerOutputStream();219tstInfo2.putOctetString(tstInfo.toByteArray());220221// Always use the same algorithm at timestamp signing222// so it is different from the hash algorithm.223String sigAlg = "SHA256withRSA";224Signature sig = Signature.getInstance(sigAlg);225sig.initSign((PrivateKey)(ks.getKey(226alias, "changeit".toCharArray())));227sig.update(tstInfo.toByteArray());228229ContentInfo contentInfo = new ContentInfo(new ObjectIdentifier(230"1.2.840.113549.1.9.16.1.4"),231new DerValue(tstInfo2.toByteArray()));232233System.out.println("# Signing...");234System.out.println("# " + new X500Name(signer235.getIssuerX500Principal().getName()));236System.out.println("# " + signer.getSerialNumber());237238SignerInfo signerInfo = new SignerInfo(239new X500Name(signer.getIssuerX500Principal().getName()),240signer.getSerialNumber(),241AlgorithmId.get(AlgorithmId.getDigAlgFromSigAlg(sigAlg)),242AlgorithmId.get(AlgorithmId.getEncAlgFromSigAlg(sigAlg)),243sig.sign());244245SignerInfo[] signerInfos = {signerInfo};246PKCS7 p7 = new PKCS7(algorithms, contentInfo,247signerCertificateChain, signerInfos);248ByteArrayOutputStream p7out = new ByteArrayOutputStream();249p7.encodeSignedData(p7out);250251DerOutputStream response = new DerOutputStream();252response.write(DerValue.tag_Sequence, statusInfo);253response.putDerValue(new DerValue(p7out.toByteArray()));254255DerOutputStream out = new DerOutputStream();256out.write(DerValue.tag_Sequence, response);257258return out.toByteArray();259}260261private Handler(HttpServer httpServer, String keystore) {262this.httpServer = httpServer;263this.keystore = keystore;264}265266/**267* Initialize TSA instance.268*269* Extended Key Info extension of certificate that is used for270* signing TSA responses should contain timeStamping value.271*/272static Handler init(int port, String keystore) throws IOException {273HttpServer httpServer = HttpServer.create(274new InetSocketAddress(port), 0);275Handler tsa = new Handler(httpServer, keystore);276httpServer.createContext("/", tsa);277return tsa;278}279280/**281* Start TSA service.282*/283void start() {284httpServer.start();285}286287/**288* Stop TSA service.289*/290void stop() {291httpServer.stop(0);292}293294/**295* Return server port number.296*/297int getPort() {298return httpServer.getAddress().getPort();299}300301@Override302public void close() throws Exception {303stop();304}305}306307public static void main(String[] args) throws Throwable {308309try (Handler tsa = Handler.init(0, "ks");) {310tsa.start();311int port = tsa.getPort();312313host = "http://localhost:" + port + "/";314315if (args.length == 0) { // Run this test316317prepare();318319sign("normal")320.shouldNotContain("Warning")321.shouldContain("The signer certificate will expire on")322.shouldContain("The timestamp will expire on")323.shouldHaveExitValue(0);324325verify("normal.jar")326.shouldNotContain("Warning")327.shouldHaveExitValue(0);328329verify("normal.jar", "-verbose")330.shouldNotContain("Warning")331.shouldContain("The signer certificate will expire on")332.shouldContain("The timestamp will expire on")333.shouldHaveExitValue(0);334335// Simulate signing at a previous date:336// 1. tsold will create a timestamp of 20 days ago.337// 2. oldsigner expired 10 days ago.338signVerbose("tsold", "unsigned.jar", "tsold.jar", "oldsigner")339.shouldNotContain("Warning")340.shouldMatch("signer certificate expired on .*. "341+ "However, the JAR will be valid")342.shouldHaveExitValue(0);343344// It verifies perfectly.345verify("tsold.jar", "-verbose", "-certs")346.shouldNotContain("Warning")347.shouldMatch("signer certificate expired on .*. "348+ "However, the JAR will be valid")349.shouldHaveExitValue(0);350351// No timestamp352signVerbose(null, "unsigned.jar", "none.jar", "signer")353.shouldContain("is not timestamped")354.shouldContain("The signer certificate will expire on")355.shouldHaveExitValue(0);356357verify("none.jar", "-verbose")358.shouldContain("do not include a timestamp")359.shouldContain("The signer certificate will expire on")360.shouldHaveExitValue(0);361362// Error cases363364signVerbose(null, "unsigned.jar", "badku.jar", "badku")365.shouldContain("KeyUsage extension doesn't allow code signing")366.shouldHaveExitValue(8);367checkBadKU("badku.jar");368369// 8180289: unvalidated TSA cert chain370sign("tsnoca")371.shouldContain("The TSA certificate chain is invalid. "372+ "Reason: Path does not chain with any of the trust anchors")373.shouldHaveExitValue(64);374375verify("tsnoca.jar", "-verbose", "-certs")376.shouldHaveExitValue(64)377.shouldContain("jar verified")378.shouldContain("Invalid TSA certificate chain: "379+ "Path does not chain with any of the trust anchors")380.shouldContain("TSA certificate chain is invalid."381+ " Reason: Path does not chain with any of the trust anchors");382383sign("nononce")384.shouldContain("Nonce missing in timestamp token")385.shouldHaveExitValue(1);386sign("diffnonce")387.shouldContain("Nonce changed in timestamp token")388.shouldHaveExitValue(1);389sign("baddigest")390.shouldContain("Digest octets changed in timestamp token")391.shouldHaveExitValue(1);392sign("diffalg")393.shouldContain("Digest algorithm not")394.shouldHaveExitValue(1);395396sign("fullchain")397.shouldHaveExitValue(0); // Success, 6543440 solved.398399sign("tsbad1")400.shouldContain("Certificate is not valid for timestamping")401.shouldHaveExitValue(1);402sign("tsbad2")403.shouldContain("Certificate is not valid for timestamping")404.shouldHaveExitValue(1);405sign("tsbad3")406.shouldContain("Certificate is not valid for timestamping")407.shouldHaveExitValue(1);408sign("nocert")409.shouldContain("Certificate not included in timestamp token")410.shouldHaveExitValue(1);411412sign("policy", "-tsapolicyid", "1.2.3")413.shouldHaveExitValue(0);414checkTimestamp("policy.jar", "1.2.3", "SHA-256");415416sign("diffpolicy", "-tsapolicyid", "1.2.3")417.shouldContain("TSAPolicyID changed in timestamp token")418.shouldHaveExitValue(1);419420sign("sha384alg", "-tsadigestalg", "SHA-384")421.shouldHaveExitValue(0);422checkTimestamp("sha384alg.jar", defaultPolicyId, "SHA-384");423424// Legacy algorithms425signVerbose(null, "unsigned.jar", "sha1alg.jar", "signer",426"-strict", "-digestalg", "SHA-1")427.shouldHaveExitValue(0)428.shouldContain("jar signed, with signer errors")429.shouldMatch("SHA-1.*-digestalg.*will be disabled");430verify("sha1alg.jar", "-strict")431.shouldHaveExitValue(0)432.shouldContain("jar verified, with signer errors")433.shouldContain("SHA-1 digest algorithm is considered a security risk")434.shouldContain("This algorithm will be disabled in a future update")435.shouldNotContain("is disabled");436437sign("sha1tsaalg", "-tsadigestalg", "SHA-1", "-strict")438.shouldHaveExitValue(0)439.shouldContain("jar signed, with signer errors")440.shouldMatch("SHA-1.*-tsadigestalg.*will be disabled")441.shouldNotContain("is disabled");442verify("sha1tsaalg.jar", "-strict")443.shouldHaveExitValue(0)444.shouldContain("jar verified, with signer errors")445.shouldContain("SHA-1 digest algorithm is considered a security risk")446.shouldNotContain("is disabled");447448// Disabled algorithms449sign("tsdisabled", "-digestalg", "MD5",450"-sigalg", "MD5withRSA", "-tsadigestalg", "MD5")451.shouldHaveExitValue(68)452.shouldContain("TSA certificate chain is invalid")453.shouldMatch("MD5.*-digestalg.*is disabled")454.shouldMatch("MD5.*-tsadigestalg.*is disabled")455.shouldMatch("MD5withRSA.*-sigalg.*is disabled");456checkDisabled("tsdisabled.jar");457458signVerbose("tsdisabled", "unsigned.jar", "tsdisabled2.jar", "signer")459.shouldHaveExitValue(64)460.shouldContain("TSA certificate chain is invalid");461462// Disabled timestamp is an error and jar treated unsigned463verify("tsdisabled2.jar", "-verbose")464.shouldHaveExitValue(16)465.shouldContain("treated as unsigned")466.shouldMatch("Timestamp.*512.*(disabled)");467468// Algorithm used in signing is disabled469signVerbose("normal", "unsigned.jar", "halfDisabled.jar", "signer",470"-digestalg", "MD5")471.shouldContain("-digestalg option is considered a security risk and is disabled")472.shouldHaveExitValue(4);473checkHalfDisabled("halfDisabled.jar");474475// sign with DSA key476signVerbose("normal", "unsigned.jar", "sign1.jar", "dsakey")477.shouldHaveExitValue(0);478479// sign with RSAkeysize < 1024480signVerbose("normal", "sign1.jar", "sign2.jar", "disabledkeysize")481.shouldContain("Algorithm constraints check failed on keysize")482.shouldHaveExitValue(4);483checkMultiple("sign2.jar");484485// Legacy algorithms486sign("tsweak", "-digestalg", "SHA1",487"-sigalg", "SHA1withRSA", "-tsadigestalg", "SHA1")488.shouldHaveExitValue(0)489.shouldMatch("SHA1.*-digestalg.*will be disabled")490.shouldMatch("SHA1.*-tsadigestalg.*will be disabled")491.shouldMatch("SHA1withRSA.*-sigalg.*will be disabled");492checkWeak("tsweak.jar");493494signVerbose("tsweak", "unsigned.jar", "tsweak2.jar", "signer")495.shouldHaveExitValue(0);496497verify("tsweak2.jar", "-verbose")498.shouldHaveExitValue(0)499.shouldContain("jar verified")500.shouldMatch("Timestamp.*1024.*(weak)");501502// Algorithm used in signing is weak503signVerbose("normal", "unsigned.jar", "halfWeak.jar", "signer",504"-digestalg", "SHA1")505.shouldContain("-digestalg option is considered a security risk.")506.shouldContain("This algorithm will be disabled in a future update.")507.shouldHaveExitValue(0);508checkHalfWeak("halfWeak.jar");509510// sign with DSA key511signVerbose("normal", "unsigned.jar", "sign1.jar", "dsakey")512.shouldHaveExitValue(0);513514// sign with RSAkeysize < 2048515signVerbose("normal", "sign1.jar", "sign2.jar", "weakkeysize")516.shouldNotContain("Algorithm constraints check failed on keysize")517.shouldHaveExitValue(0);518checkMultipleWeak("sign2.jar");519520521// 8191438: jarsigner should print when a timestamp will expire522checkExpiration();523524// When .SF or .RSA is missing or invalid525checkMissingOrInvalidFiles("normal.jar");526527if (Files.exists(Paths.get("ts2.cert"))) {528checkInvalidTsaCertKeyUsage();529}530} else { // Run as a standalone server531System.out.println("TSA started at " + host532+ ". Press Enter to quit server");533System.in.read();534}535}536}537538private static void checkExpiration() throws Exception {539540// Warning when expired or expiring541signVerbose(null, "unsigned.jar", "expired.jar", "expired")542.shouldContain("signer certificate has expired")543.shouldHaveExitValue(4);544verify("expired.jar")545.shouldContain("signer certificate has expired")546.shouldHaveExitValue(4);547signVerbose(null, "unsigned.jar", "expiring.jar", "expiring")548.shouldContain("signer certificate will expire within")549.shouldHaveExitValue(0);550verify("expiring.jar")551.shouldContain("signer certificate will expire within")552.shouldHaveExitValue(0);553// Info for long554signVerbose(null, "unsigned.jar", "long.jar", "long")555.shouldNotContain("signer certificate has expired")556.shouldNotContain("signer certificate will expire within")557.shouldContain("signer certificate will expire on")558.shouldHaveExitValue(0);559verify("long.jar")560.shouldNotContain("signer certificate has expired")561.shouldNotContain("signer certificate will expire within")562.shouldNotContain("The signer certificate will expire")563.shouldHaveExitValue(0);564verify("long.jar", "-verbose")565.shouldContain("The signer certificate will expire")566.shouldHaveExitValue(0);567568// Both expired569signVerbose("tsexpired", "unsigned.jar",570"tsexpired-expired.jar", "expired")571.shouldContain("The signer certificate has expired.")572.shouldContain("The timestamp has expired.")573.shouldHaveExitValue(4);574verify("tsexpired-expired.jar")575.shouldContain("signer certificate has expired")576.shouldContain("timestamp has expired.")577.shouldHaveExitValue(4);578579// TS expired but signer still good580signVerbose("tsexpired", "unsigned.jar",581"tsexpired-long.jar", "long")582.shouldContain("The timestamp expired on")583.shouldHaveExitValue(0);584verify("tsexpired-long.jar")585.shouldMatch("timestamp expired on.*However, the JAR will be valid")586.shouldNotContain("Error")587.shouldHaveExitValue(0);588589signVerbose("tsexpired", "unsigned.jar",590"tsexpired-ca.jar", "ca")591.shouldContain("The timestamp has expired.")592.shouldHaveExitValue(4);593verify("tsexpired-ca.jar")594.shouldNotContain("timestamp has expired")595.shouldNotContain("Error")596.shouldHaveExitValue(0);597598// Warning when expiring599sign("tsexpiring")600.shouldContain("timestamp will expire within")601.shouldHaveExitValue(0);602verify("tsexpiring.jar")603.shouldContain("timestamp will expire within")604.shouldNotContain("still valid")605.shouldHaveExitValue(0);606607signVerbose("tsexpiring", "unsigned.jar",608"tsexpiring-ca.jar", "ca")609.shouldContain("self-signed")610.stderrShouldNotMatch("The.*expir")611.shouldHaveExitValue(4); // self-signed612verify("tsexpiring-ca.jar")613.stderrShouldNotMatch("The.*expir")614.shouldHaveExitValue(0);615616signVerbose("tsexpiringsoon", "unsigned.jar",617"tsexpiringsoon-long.jar", "long")618.shouldContain("The timestamp will expire")619.shouldHaveExitValue(0);620verify("tsexpiringsoon-long.jar")621.shouldMatch("timestamp will expire.*However, the JAR will be valid until")622.shouldHaveExitValue(0);623624// Info for long625sign("tslong")626.shouldNotContain("timestamp has expired")627.shouldNotContain("timestamp will expire within")628.shouldContain("timestamp will expire on")629.shouldContain("signer certificate will expire on")630.shouldHaveExitValue(0);631verify("tslong.jar")632.shouldNotContain("timestamp has expired")633.shouldNotContain("timestamp will expire within")634.shouldNotContain("timestamp will expire on")635.shouldNotContain("signer certificate will expire on")636.shouldHaveExitValue(0);637verify("tslong.jar", "-verbose")638.shouldContain("timestamp will expire on")639.shouldContain("signer certificate will expire on")640.shouldHaveExitValue(0);641}642643private static void checkInvalidTsaCertKeyUsage() throws Exception {644645// Hack: Rewrite the TSA cert inside normal.jar into ts2.jar.646647// Both the cert and the serial number must be rewritten.648byte[] tsCert = Files.readAllBytes(Paths.get("ts.cert"));649byte[] ts2Cert = Files.readAllBytes(Paths.get("ts2.cert"));650byte[] tsSerial = getCert(tsCert)651.getSerialNumber().toByteArray();652byte[] ts2Serial = getCert(ts2Cert)653.getSerialNumber().toByteArray();654655byte[] oldBlock;656try (JarFile normal = new JarFile("normal.jar")) {657oldBlock = Utils.readAllBytes(normal.getInputStream(658normal.getJarEntry("META-INF/SIGNER.RSA")));659}660661JarUtils.updateJar("normal.jar", "ts2.jar",662mapOf("META-INF/SIGNER.RSA",663updateBytes(updateBytes(oldBlock, tsCert, ts2Cert),664tsSerial, ts2Serial)));665666verify("ts2.jar", "-verbose", "-certs")667.shouldHaveExitValue(64)668.shouldContain("jar verified")669.shouldContain("Invalid TSA certificate chain: Extended key usage does not permit use for TSA server");670}671672public static X509Certificate getCert(byte[] data)673throws CertificateException, IOException {674return (X509Certificate)675CertificateFactory.getInstance("X.509")676.generateCertificate(new ByteArrayInputStream(data));677}678679private static byte[] updateBytes(byte[] old, byte[] from, byte[] to) {680int pos = 0;681while (true) {682if (pos + from.length > old.length) {683return null;684}685if (Arrays.equals(Arrays.copyOfRange(old, pos, pos+from.length), from)) {686byte[] result = old.clone();687System.arraycopy(to, 0, result, pos, from.length);688return result;689}690pos++;691}692}693694private static void checkMissingOrInvalidFiles(String s)695throws Throwable {696697JarUtils.updateJar(s, "1.jar", mapOf("META-INF/SIGNER.SF", Boolean.FALSE));698verify("1.jar", "-verbose")699.shouldHaveExitValue(16)700.shouldContain("treated as unsigned")701.shouldContain("Missing signature-related file META-INF/SIGNER.SF");702JarUtils.updateJar(s, "2.jar", mapOf("META-INF/SIGNER.RSA", Boolean.FALSE));703verify("2.jar", "-verbose")704.shouldHaveExitValue(16)705.shouldContain("treated as unsigned")706.shouldContain("Missing block file for signature-related file META-INF/SIGNER.SF");707JarUtils.updateJar(s, "3.jar", mapOf("META-INF/SIGNER.SF", "dummy"));708verify("3.jar", "-verbose")709.shouldHaveExitValue(16)710.shouldContain("treated as unsigned")711.shouldContain("Unparsable signature-related file META-INF/SIGNER.SF");712JarUtils.updateJar(s, "4.jar", mapOf("META-INF/SIGNER.RSA", "dummy"));713verify("4.jar", "-verbose")714.shouldHaveExitValue(16)715.shouldContain("treated as unsigned")716.shouldContain("Unparsable signature-related file META-INF/SIGNER.RSA");717}718719static OutputAnalyzer jarsigner(List<String> extra)720throws Exception {721List<String> args = new ArrayList<>(722listOf("-keystore", "ks", "-storepass", "changeit"));723args.addAll(extra);724return SecurityTools.jarsigner(args);725}726727static OutputAnalyzer verify(String file, String... extra)728throws Exception {729List<String> args = new ArrayList<>();730args.add("-verify");731args.add("-strict");732args.add(file);733args.addAll(Arrays.asList(extra));734return jarsigner(args);735}736737static void checkBadKU(String file) throws Exception {738System.err.println("BadKU: " + file);739verify(file)740.shouldHaveExitValue(16)741.shouldContain("treated as unsigned")742.shouldContain("re-run jarsigner with debug enabled");743verify(file, "-verbose")744.shouldHaveExitValue(16)745.shouldContain("Signed by")746.shouldContain("treated as unsigned")747.shouldContain("re-run jarsigner with debug enabled");748verify(file, "-J-Djava.security.debug=jar")749.shouldHaveExitValue(16)750.shouldContain("SignatureException: Key usage restricted")751.shouldContain("treated as unsigned")752.shouldContain("re-run jarsigner with debug enabled");753}754755static void checkDisabled(String file) throws Exception {756verify(file)757.shouldHaveExitValue(16)758.shouldContain("treated as unsigned")759.shouldMatch("weak algorithm that is now disabled.")760.shouldMatch("Re-run jarsigner with the -verbose option for more details");761verify(file, "-verbose")762.shouldHaveExitValue(16)763.shouldContain("treated as unsigned")764.shouldMatch("weak algorithm that is now disabled by")765.shouldMatch("Digest algorithm: .*(disabled)")766.shouldMatch("Signature algorithm: .*(disabled)")767.shouldMatch("Timestamp digest algorithm: .*(disabled)")768.shouldNotMatch("Timestamp signature algorithm: .*(weak).*(weak)")769.shouldMatch("Timestamp signature algorithm: .*key.*(disabled)");770verify(file, "-J-Djava.security.debug=jar")771.shouldHaveExitValue(16)772.shouldMatch("SignatureException:.*keysize");773}774775static void checkHalfWeak(String file) throws Exception {776verify(file)777.shouldHaveExitValue(0)778.shouldNotContain("treated as unsigned");779verify(file, "-verbose")780.shouldHaveExitValue(0)781.shouldNotContain("treated as unsigned")782.shouldMatch("Digest algorithm: .*(weak)")783.shouldNotMatch("Signature algorithm: .*(weak)")784.shouldNotMatch("Signature algorithm: .*(disabled)")785.shouldNotMatch("Timestamp digest algorithm: .*(weak)")786.shouldNotMatch("Timestamp signature algorithm: .*(weak).*(weak)")787.shouldNotMatch("Timestamp signature algorithm: .*(disabled).*(disabled)")788.shouldNotMatch("Timestamp signature algorithm: .*key.*(weak)")789.shouldNotMatch("Timestamp signature algorithm: .*key.*(disabled)");790}791792static void checkMultiple(String file) throws Exception {793verify(file)794.shouldHaveExitValue(0)795.shouldContain("jar verified");796verify(file, "-verbose", "-certs")797.shouldHaveExitValue(0)798.shouldContain("jar verified")799.shouldMatch("X.509.*CN=dsakey")800.shouldNotMatch("X.509.*CN=disabledkeysize")801.shouldMatch("Signed by .*CN=dsakey")802.shouldMatch("Signed by .*CN=disabledkeysize")803.shouldMatch("Signature algorithm: .*key.*(disabled)");804}805806static void checkWeak(String file) throws Exception {807verify(file)808.shouldHaveExitValue(0)809.shouldNotContain("treated as unsigned");810verify(file, "-verbose")811.shouldHaveExitValue(0)812.shouldNotContain("treated as unsigned")813.shouldMatch("Digest algorithm: .*(weak)")814.shouldMatch("Signature algorithm: .*(weak)")815.shouldMatch("Timestamp digest algorithm: .*(weak)")816.shouldNotMatch("Timestamp signature algorithm: .*(weak).*(weak)")817.shouldMatch("Timestamp signature algorithm: .*key.*(weak)");818verify(file, "-J-Djava.security.debug=jar")819.shouldHaveExitValue(0)820.shouldNotMatch("SignatureException:.*disabled");821822// keytool should print out warnings when reading or823// generating cert/cert req using legacy algorithms.824String sout = SecurityTools.keytool("-printcert -jarfile " + file)825.stderrShouldContain("The TSA certificate uses a 1024-bit RSA key" +826" which is considered a security risk." +827" This key size will be disabled in a future update.")828.getStdout();829if (sout.indexOf("weak", sout.indexOf("Timestamp:")) < 0) {830throw new RuntimeException("timestamp not weak: " + sout);831}832}833834static void checkHalfDisabled(String file) throws Exception {835verify(file)836.shouldHaveExitValue(16)837.shouldContain("treated as unsigned")838.shouldMatch("weak algorithm that is now disabled.")839.shouldMatch("Re-run jarsigner with the -verbose option for more details");840verify(file, "-verbose")841.shouldHaveExitValue(16)842.shouldContain("treated as unsigned")843.shouldMatch("weak algorithm that is now disabled by")844.shouldMatch("Digest algorithm: .*(disabled)")845.shouldNotMatch("Signature algorithm: .*(weak)")846.shouldNotMatch("Signature algorithm: .*(disabled)")847.shouldNotMatch("Timestamp digest algorithm: .*(disabled)")848.shouldNotMatch("Timestamp signature algorithm: .*(weak).*(weak)")849.shouldNotMatch("Timestamp signature algorithm: .*(disabled).*(disabled)")850.shouldNotMatch("Timestamp signature algorithm: .*key.*(weak)")851.shouldNotMatch("Timestamp signature algorithm: .*key.*(disabled)");852}853854static void checkMultipleWeak(String file) throws Exception {855verify(file)856.shouldHaveExitValue(0)857.shouldContain("jar verified");858verify(file, "-verbose", "-certs")859.shouldHaveExitValue(0)860.shouldContain("jar verified")861.shouldMatch("X.509.*CN=dsakey")862.shouldMatch("X.509.*CN=weakkeysize")863.shouldMatch("Signed by .*CN=dsakey")864.shouldMatch("Signed by .*CN=weakkeysize")865.shouldMatch("Signature algorithm: .*key.*(weak)");866}867868static void checkTimestamp(String file, String policyId, String digestAlg)869throws Exception {870try (JarFile jf = new JarFile(file)) {871JarEntry je = jf.getJarEntry("META-INF/SIGNER.RSA");872try (InputStream is = jf.getInputStream(je)) {873byte[] content = IOUtils.readAllBytes(is);874PKCS7 p7 = new PKCS7(content);875SignerInfo[] si = p7.getSignerInfos();876if (si == null || si.length == 0) {877throw new Exception("Not signed");878}879PKCS9Attribute p9 = si[0].getUnauthenticatedAttributes()880.getAttribute(PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);881PKCS7 tsToken = new PKCS7((byte[]) p9.getValue());882TimestampToken tt =883new TimestampToken(tsToken.getContentInfo().getData());884if (!tt.getHashAlgorithm().toString().equals(digestAlg)) {885throw new Exception("Digest alg different");886}887if (!tt.getPolicyID().equals(policyId)) {888throw new Exception("policyId different");889}890}891}892}893894static int which = 0;895896/**897* Sign with a TSA path. Always use alias "signer" to sign "unsigned.jar".898* The signed jar name is always path.jar.899*900* @param extra more args given to jarsigner901*/902static OutputAnalyzer sign(String path, String... extra)903throws Exception {904return signVerbose(905path,906"unsigned.jar",907path + ".jar",908"signer",909extra);910}911912static OutputAnalyzer signVerbose(913String path, // TSA URL path914String oldJar,915String newJar,916String alias, // signer917String...extra) throws Exception {918which++;919System.out.println("\n>> Test #" + which);920List<String> args = new ArrayList<>();921args.add("-strict");922args.add("-verbose");923args.add("-debug");924args.add("-signedjar");925args.add(newJar);926args.add(oldJar);927args.add(alias);928if (path != null) {929args.add("-tsa");930args.add(host + path);931}932args.addAll(Arrays.asList(extra));933return jarsigner(args);934}935936static void prepare() throws Exception {937JarUtils.createJar("unsigned.jar", "A");938Files.deleteIfExists(Paths.get("ks"));939keytool("-alias signer -genkeypair -ext bc -dname CN=signer");940keytool("-alias oldsigner -genkeypair -dname CN=oldsigner");941keytool("-alias dsakey -genkeypair -keyalg DSA -dname CN=dsakey");942keytool("-alias weakkeysize -genkeypair -keysize 1024 -dname CN=weakkeysize");943keytool("-alias disabledkeysize -genkeypair -keysize 512 -dname CN=disabledkeysize");944keytool("-alias badku -genkeypair -dname CN=badku");945keytool("-alias ts -genkeypair -dname CN=ts");946keytool("-alias tsold -genkeypair -dname CN=tsold");947keytool("-alias tsweak -genkeypair -keysize 1024 -dname CN=tsweak");948keytool("-alias tsdisabled -genkeypair -keysize 512 -dname CN=tsdisabled");949keytool("-alias tsbad1 -genkeypair -dname CN=tsbad1");950keytool("-alias tsbad2 -genkeypair -dname CN=tsbad2");951keytool("-alias tsbad3 -genkeypair -dname CN=tsbad3");952keytool("-alias tsnoca -genkeypair -dname CN=tsnoca");953954keytool("-alias expired -genkeypair -dname CN=expired");955keytool("-alias expiring -genkeypair -dname CN=expiring");956keytool("-alias long -genkeypair -dname CN=long");957keytool("-alias tsexpired -genkeypair -dname CN=tsexpired");958keytool("-alias tsexpiring -genkeypair -dname CN=tsexpiring");959keytool("-alias tsexpiringsoon -genkeypair -dname CN=tsexpiringsoon");960keytool("-alias tslong -genkeypair -dname CN=tslong");961962// tsnoca's issuer will be removed from keystore later963keytool("-alias ca -genkeypair -ext bc -dname CN=CA");964gencert("tsnoca", "-ext eku:critical=ts");965keytool("-delete -alias ca");966keytool("-alias ca -genkeypair -ext bc -dname CN=CA -startdate -40d");967968gencert("signer");969gencert("oldsigner", "-startdate -30d -validity 20");970gencert("dsakey");971gencert("weakkeysize");972gencert("disabledkeysize");973gencert("badku", "-ext ku:critical=keyAgreement");974gencert("ts", "-ext eku:critical=ts -validity 500");975976gencert("expired", "-validity 10 -startdate -12d");977gencert("expiring", "-validity 178");978gencert("long", "-validity 182");979gencert("tsexpired", "-ext eku:critical=ts -validity 10 -startdate -12d");980gencert("tsexpiring", "-ext eku:critical=ts -validity 364");981gencert("tsexpiringsoon", "-ext eku:critical=ts -validity 170"); // earlier than expiring982gencert("tslong", "-ext eku:critical=ts -validity 367");983984985for (int i = 0; i < 5; i++) {986// Issue another cert for "ts" with a different EKU.987// Length might be different because serial number is988// random. Try several times until a cert with the same989// length is generated so we can substitute ts.cert990// embedded in the PKCS7 block with ts2.cert.991// If cannot create one, related test will be ignored.992keytool("-gencert -alias ca -infile ts.req -outfile ts2.cert " +993"-ext eku:critical=1.3.6.1.5.5.7.3.9");994if (Files.size(Paths.get("ts.cert")) != Files.size(Paths.get("ts2.cert"))) {995Files.delete(Paths.get("ts2.cert"));996System.out.println("Warning: cannot create same length");997} else {998break;999}1000}10011002gencert("tsold", "-ext eku:critical=ts -startdate -40d -validity 500");10031004gencert("tsweak", "-ext eku:critical=ts");1005gencert("tsdisabled", "-ext eku:critical=ts");1006gencert("tsbad1");1007gencert("tsbad2", "-ext eku=ts");1008gencert("tsbad3", "-ext eku:critical=cs");1009}10101011static void gencert(String alias, String... extra) throws Exception {1012keytool("-alias " + alias + " -certreq -file " + alias + ".req");1013String genCmd = "-gencert -alias ca -infile " +1014alias + ".req -outfile " + alias + ".cert";1015for (String s : extra) {1016genCmd += " " + s;1017}1018keytool(genCmd);1019keytool("-alias " + alias + " -importcert -file " + alias + ".cert");1020}10211022static void keytool(String cmd) throws Exception {1023cmd = "-keystore ks -storepass changeit -keypass changeit " +1024"-keyalg rsa -validity 200 " + cmd;1025sun.security.tools.keytool.Main.main(cmd.split(" "));1026}10271028static <K,V> Map<K,V> mapOf(K k1, V v1) {1029return Collections.singletonMap(k1, v1);1030}10311032static <E> List<E> listOf(E... elements) {1033return Arrays.asList(elements);1034}1035}103610371038