test/sun/security/tools/jarsigner/TimestampCheck.java

Print this page




   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 
  43 import sun.misc.IOUtils;


  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.


 276 
 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     public static void main(String[] args) throws Exception {
 297         try (Handler tsa = Handler.init(0, TSKS);) {




 298             tsa.start();
 299             int port = tsa.getPort();
 300 
 301             String cmd;
 302             // Use -J-Djava.security.egd=file:/dev/./urandom to speed up
 303             // nonce generation in timestamping request. Not avaibale on
 304             // Windows and defaults to thread seed generator, not too bad.
 305             if (System.getProperty("java.home").endsWith("jre")) {
 306                 cmd = System.getProperty("java.home") + "/../bin/jarsigner";
 307             } else {
 308                 cmd = System.getProperty("java.home") + "/bin/jarsigner";
 309             }
 310 
 311             cmd += " -J-Djava.security.egd=file:/dev/./urandom"
 312                     + " -debug -keystore " + TSKS + " -storepass changeit"
 313                     + " -tsa http://localhost:" + port + "/%d"
 314                     + " -signedjar new_%d.jar " + JAR + " old";
 315 
 316             if (args.length == 0) {         // Run this test
 317                 jarsigner(cmd, 0, true);    // Success, normal call
 318                 jarsigner(cmd, 1, false);   // These 4 should fail
 319                 jarsigner(cmd, 2, false);
 320                 jarsigner(cmd, 3, false);
 321                 jarsigner(cmd, 4, false);
 322                 jarsigner(cmd, 5, true);    // Success, 6543440 solved.
 323                 jarsigner(cmd, 6, false);   // tsbad1
 324                 jarsigner(cmd, 7, false);   // tsbad2
 325                 jarsigner(cmd, 8, false);   // tsbad3
 326                 jarsigner(cmd, 9, false);   // no cert in timestamp
 327                 jarsigner(cmd + " -tsapolicyid 1.2.3.4", 10, true);
 328                 checkTimestamp("new_10.jar", "1.2.3.4", "SHA-256");
 329                 jarsigner(cmd + " -tsapolicyid 1.2.3.5", 11, false);
 330                 jarsigner(cmd + " -tsadigestalg SHA", 12, true);
 331                 checkTimestamp("new_12.jar", defaultPolicyId, "SHA-1");


































 332             } else {                        // Run as a standalone server
 333                 System.err.println("Press Enter to quit server");
 334                 System.in.read();
 335             }
 336         }
 337     }
 338 































































































 339     static void checkTimestamp(String file, String policyId, String digestAlg)
 340             throws Exception {
 341         try (JarFile jf = new JarFile(file)) {
 342             JarEntry je = jf.getJarEntry("META-INF/OLD.RSA");
 343             try (InputStream is = jf.getInputStream(je)) {
 344                 byte[] content = IOUtils.readFully(is, -1, true);
 345                 PKCS7 p7 = new PKCS7(content);
 346                 SignerInfo[] si = p7.getSignerInfos();
 347                 if (si == null || si.length == 0) {
 348                     throw new Exception("Not signed");
 349                 }
 350                 PKCS9Attribute p9 = si[0].getUnauthenticatedAttributes()
 351                         .getAttribute(PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
 352                 PKCS7 tsToken = new PKCS7((byte[]) p9.getValue());
 353                 TimestampToken tt =
 354                         new TimestampToken(tsToken.getContentInfo().getData());
 355                 if (!tt.getHashAlgorithm().toString().equals(digestAlg)) {
 356                     throw new Exception("Digest alg different");
 357                 }
 358                 if (!tt.getPolicyID().equals(policyId)) {
 359                     throw new Exception("policyId different");
 360                 }
 361             }
 362         }
 363     }
 364 


 365     /**
 366      * @param cmd the command line (with a hole to plug in)
 367      * @param path the path in the URL, i.e, http://localhost/path
 368      * @param expected if this command should succeed
 369      */
 370     static void jarsigner(String cmd, int path, boolean expected)
 371             throws Exception {
 372         System.err.println("Test " + path);
 373         Process p = Runtime.getRuntime().exec(String.format(cmd, path, path));
 374         BufferedReader reader = new BufferedReader(
 375                 new InputStreamReader(p.getErrorStream()));
 376         while (true) {
 377             String s = reader.readLine();
 378             if (s == null) break;
 379             System.err.println(s);




 380         }



 381 
 382         // Will not see noTimestamp warning
 383         boolean seeWarning = false;
 384         reader = new BufferedReader(
 385                 new InputStreamReader(p.getInputStream()));
 386         while (true) {
 387             String s = reader.readLine();
 388             if (s == null) break;
 389             System.err.println(s);
 390             if (s.indexOf("Warning:") >= 0) {
 391                 seeWarning = true;









 392             }







 393         }
 394         int result = p.waitFor();
 395         if (expected && result != 0 || !expected && result == 0) {
 396             throw new Exception("Failed");
 397         }
 398         if (seeWarning) {
 399             throw new Exception("See warning");



 400         }
 401     }
 402 }


   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.FileInputStream;
  28 import java.io.IOException;
  29 import java.io.InputStream;

  30 import java.io.OutputStream;
  31 import java.math.BigInteger;
  32 import java.net.InetSocketAddress;
  33 import java.nio.file.Files;
  34 import java.nio.file.Paths;
  35 import java.security.KeyStore;
  36 import java.security.PrivateKey;
  37 import java.security.Signature;
  38 import java.security.cert.Certificate;
  39 import java.security.cert.X509Certificate;
  40 import java.util.ArrayList;
  41 import java.util.Arrays;
  42 import java.util.Calendar;
  43 import java.util.List;
  44 import java.util.jar.JarEntry;
  45 import java.util.jar.JarFile;
  46 
  47 import sun.misc.IOUtils;
  48 import jdk.testlibrary.*;
  49 import jdk.testlibrary.JarUtils;
  50 import sun.security.pkcs.ContentInfo;
  51 import sun.security.pkcs.PKCS7;
  52 import sun.security.pkcs.PKCS9Attribute;
  53 import sun.security.pkcs.SignerInfo;
  54 import sun.security.timestamp.TimestampToken;
  55 import sun.security.util.DerOutputStream;
  56 import sun.security.util.DerValue;
  57 import sun.security.util.ObjectIdentifier;
  58 import sun.security.x509.AlgorithmId;
  59 import sun.security.x509.X500Name;
  60 
  61 /*
  62  * @test
  63  * @bug 6543842 6543440 6939248 8009636 8024302 8163304
  64  * @summary checking response of timestamp
  65  * @modules java.base/sun.security.pkcs
  66  *          java.base/sun.security.timestamp
  67  *          java.base/sun.security.x509
  68  *          java.base/sun.security.util
  69  *          java.base/sun.security.tools.keytool
  70  * @library /lib/testlibrary
  71  * @run main/timeout=600 TimestampCheck
  72  */
  73 public class TimestampCheck {


  74 
  75     static final String defaultPolicyId = "2.3.4";
  76     static String host = null;
  77 
  78     static class Handler implements HttpHandler, AutoCloseable {
  79 
  80         private final HttpServer httpServer;
  81         private final String keystore;
  82 
  83         @Override
  84         public void handle(HttpExchange t) throws IOException {
  85             int len = 0;
  86             for (String h: t.getRequestHeaders().keySet()) {
  87                 if (h.equalsIgnoreCase("Content-length")) {
  88                     len = Integer.valueOf(t.getRequestHeaders().get(h).get(0));
  89                 }
  90             }
  91             byte[] input = new byte[len];
  92             t.getRequestBody().read(input);
  93 
  94             try {
  95                 String path = t.getRequestURI().getPath().substring(1);




  96                 byte[] output = sign(input, path);
  97                 Headers out = t.getResponseHeaders();
  98                 out.set("Content-Type", "application/timestamp-reply");
  99 
 100                 t.sendResponseHeaders(200, output.length);
 101                 OutputStream os = t.getResponseBody();
 102                 os.write(output);
 103             } catch (Exception e) {
 104                 e.printStackTrace();
 105                 t.sendResponseHeaders(500, 0);
 106             }
 107             t.close();
 108         }
 109 
 110         /**
 111          * @param input The data to sign
 112          * @param path different cases to simulate, impl on URL path














 113          * @returns the signed
 114          */
 115         byte[] sign(byte[] input, String path) throws Exception {

 116             DerValue value = new DerValue(input);
 117             System.err.println("\nIncoming Request\n===================");
 118             System.err.println("Version: " + value.data.getInteger());
 119             DerValue messageImprint = value.data.getDerValue();
 120             AlgorithmId aid = AlgorithmId.parse(
 121                     messageImprint.data.getDerValue());
 122             System.err.println("AlgorithmId: " + aid);
 123 
 124             ObjectIdentifier policyId = new ObjectIdentifier(defaultPolicyId);
 125             BigInteger nonce = null;
 126             while (value.data.available() > 0) {
 127                 DerValue v = value.data.getDerValue();
 128                 if (v.tag == DerValue.tag_Integer) {
 129                     nonce = v.getBigInteger();
 130                     System.err.println("nonce: " + nonce);
 131                 } else if (v.tag == DerValue.tag_Boolean) {
 132                     System.err.println("certReq: " + v.getBoolean());
 133                 } else if (v.tag == DerValue.tag_ObjectId) {
 134                     policyId = v.getOID();
 135                     System.err.println("PolicyID: " + policyId);
 136                 }
 137             }
 138 

 139             System.err.println("\nResponse\n===================");
 140             FileInputStream is = new FileInputStream(keystore);
 141             KeyStore ks = KeyStore.getInstance("JCEKS");
 142             ks.load(is, "changeit".toCharArray());
 143             is.close();
 144 
 145             String alias = "ts";
 146             if (path.startsWith("bad") || path.equals("weak")) {
 147                 alias = "ts" + path;
 148             }
 149 
 150             if (path.equals("diffpolicy")) {
 151                 policyId = new ObjectIdentifier(defaultPolicyId);
 152             }
 153 
 154             DerOutputStream statusInfo = new DerOutputStream();
 155             statusInfo.putInteger(0);
 156 

 157             AlgorithmId[] algorithms = {aid};
 158             Certificate[] chain = ks.getCertificateChain(alias);
 159             X509Certificate[] signerCertificateChain;
 160             X509Certificate signer = (X509Certificate)chain[0];
 161 
 162             if (path.equals("fullchain")) {   // Only case 5 uses full chain
 163                 signerCertificateChain = new X509Certificate[chain.length];
 164                 for (int i=0; i<chain.length; i++) {
 165                     signerCertificateChain[i] = (X509Certificate)chain[i];
 166                 }
 167             } else if (path.equals("nocert")) {
 168                 signerCertificateChain = new X509Certificate[0];
 169             } else {
 170                 signerCertificateChain = new X509Certificate[1];
 171                 signerCertificateChain[0] = (X509Certificate)chain[0];
 172             }
 173 
 174             DerOutputStream tst = new DerOutputStream();
 175 
 176             tst.putInteger(1);
 177             tst.putOID(policyId);
 178 
 179             if (!path.equals("baddigest") && !path.equals("diffalg")) {
 180                 tst.putDerValue(messageImprint);
 181             } else {
 182                 byte[] data = messageImprint.toByteArray();
 183                 if (path.equals("diffalg")) {
 184                     data[6] = (byte)0x01;
 185                 } else {
 186                     data[data.length-1] = (byte)0x01;
 187                     data[data.length-2] = (byte)0x02;
 188                     data[data.length-3] = (byte)0x03;
 189                 }
 190                 tst.write(data);
 191             }
 192 
 193             tst.putInteger(1);
 194 
 195             Calendar cal = Calendar.getInstance();
 196             tst.putGeneralizedTime(cal.getTime());
 197 
 198             if (path.equals("diffnonce")) {
 199                 tst.putInteger(1234);
 200             } else if (path.equals("nononce")) {
 201                 // no noce
 202             } else {
 203                 tst.putInteger(nonce);
 204             }
 205 
 206             DerOutputStream tstInfo = new DerOutputStream();
 207             tstInfo.write(DerValue.tag_Sequence, tst);
 208 
 209             DerOutputStream tstInfo2 = new DerOutputStream();
 210             tstInfo2.putOctetString(tstInfo.toByteArray());
 211 
 212             // Always use the same algorithm at timestamp signing
 213             // so it is different from the hash algorithm.
 214             Signature sig = Signature.getInstance("SHA1withRSA");
 215             sig.initSign((PrivateKey)(ks.getKey(
 216                     alias, "changeit".toCharArray())));
 217             sig.update(tstInfo.toByteArray());
 218 
 219             ContentInfo contentInfo = new ContentInfo(new ObjectIdentifier(
 220                     "1.2.840.113549.1.9.16.1.4"),
 221                     new DerValue(tstInfo2.toByteArray()));
 222 
 223             System.err.println("Signing...");
 224             System.err.println(new X500Name(signer
 225                     .getIssuerX500Principal().getName()));
 226             System.err.println(signer.getSerialNumber());
 227 
 228             SignerInfo signerInfo = new SignerInfo(
 229                     new X500Name(signer.getIssuerX500Principal().getName()),
 230                     signer.getSerialNumber(),
 231                     AlgorithmId.get("SHA-1"), AlgorithmId.get("RSA"), sig.sign());
 232 
 233             SignerInfo[] signerInfos = {signerInfo};
 234             PKCS7 p7 = new PKCS7(algorithms, contentInfo,
 235                     signerCertificateChain, signerInfos);

 236             ByteArrayOutputStream p7out = new ByteArrayOutputStream();
 237             p7.encodeSignedData(p7out);
 238 
 239             DerOutputStream response = new DerOutputStream();
 240             response.write(DerValue.tag_Sequence, statusInfo);
 241             response.putDerValue(new DerValue(p7out.toByteArray()));
 242 
 243             DerOutputStream out = new DerOutputStream();
 244             out.write(DerValue.tag_Sequence, response);
 245 
 246             return out.toByteArray();
 247         }
 248 
 249         private Handler(HttpServer httpServer, String keystore) {
 250             this.httpServer = httpServer;
 251             this.keystore = keystore;
 252         }
 253 
 254         /**
 255          * Initialize TSA instance.


 274 
 275         /**
 276          * Stop TSA service.
 277          */
 278         void stop() {
 279             httpServer.stop(0);
 280         }
 281 
 282         /**
 283          * Return server port number.
 284          */
 285         int getPort() {
 286             return httpServer.getAddress().getPort();
 287         }
 288 
 289         @Override
 290         public void close() throws Exception {
 291             stop();
 292         }
 293     }
 294 
 295     public static void main(String[] args) throws Throwable {
 296 
 297         prepare();
 298 
 299         try (Handler tsa = Handler.init(0, "tsks");) {
 300             tsa.start();
 301             int port = tsa.getPort();
 302 
 303             host = "http://localhost:" + port + "/";








 304 





 305             if (args.length == 0) {         // Run this test
 306                 sign("none")
 307                         .shouldContain("is not timestamped")
 308                         .shouldHaveExitValue(0);
 309 
 310                 sign("badku")
 311                         .shouldHaveExitValue(0);
 312                 checkBadKU("badku.jar");
 313 
 314                 sign("normal")
 315                         .shouldNotContain("is not timestamped")
 316                         .shouldHaveExitValue(0);
 317 
 318                 sign("nononce")
 319                         .shouldHaveExitValue(1);
 320                 sign("diffnonce")
 321                         .shouldHaveExitValue(1);
 322                 sign("baddigest")
 323                         .shouldHaveExitValue(1);
 324                 sign("diffalg")
 325                         .shouldHaveExitValue(1);
 326                 sign("fullchain")
 327                         .shouldHaveExitValue(0);   // Success, 6543440 solved.
 328                 sign("bad1")
 329                         .shouldHaveExitValue(1);
 330                 sign("bad2")
 331                         .shouldHaveExitValue(1);
 332                 sign("bad3")
 333                         .shouldHaveExitValue(1);
 334                 sign("nocert")
 335                         .shouldHaveExitValue(1);
 336 
 337                 sign("policy", "-tsapolicyid",  "1.2.3")
 338                         .shouldHaveExitValue(0);
 339                 checkTimestamp("policy.jar", "1.2.3", "SHA-256");
 340 
 341                 sign("diffpolicy", "-tsapolicyid", "1.2.3")
 342                         .shouldHaveExitValue(1);
 343 
 344                 sign("tsaalg", "-tsadigestalg", "SHA")
 345                         .shouldHaveExitValue(0);
 346                 checkTimestamp("tsaalg.jar", defaultPolicyId, "SHA-1");
 347 
 348                 sign("weak", "-digestalg", "MD5",
 349                                 "-sigalg", "MD5withRSA", "-tsadigestalg", "MD5")
 350                         .shouldHaveExitValue(0);
 351                 checkWeak("weak.jar");
 352 
 353                 // When .SF or .RSA is missing or invalid
 354                 checkMissingOrInvalidFiles("normal.jar");
 355             } else {                        // Run as a standalone server
 356                 System.err.println("Press Enter to quit server");
 357                 System.in.read();
 358             }
 359         }
 360     }
 361 
 362     private static void checkMissingOrInvalidFiles(String s)
 363             throws Throwable {
 364         JarUtils.updateJar(s, "1.jar", "-", "META-INF/OLD.SF");
 365         verify("1.jar", "-verbose")
 366                 .shouldHaveExitValue(0)
 367                 .shouldContain("treated as unsigned")
 368                 .shouldContain("Missing signature-related file META-INF/OLD.SF");
 369         JarUtils.updateJar(s, "2.jar", "-", "META-INF/OLD.RSA");
 370         verify("2.jar", "-verbose")
 371                 .shouldHaveExitValue(0)
 372                 .shouldContain("treated as unsigned")
 373                 .shouldContain("Missing block file for signature-related file META-INF/OLD.SF");
 374         JarUtils.updateJar(s, "3.jar", "META-INF/OLD.SF");
 375         verify("3.jar", "-verbose")
 376                 .shouldHaveExitValue(0)
 377                 .shouldContain("treated as unsigned")
 378                 .shouldContain("Unparsable signature-related file META-INF/OLD.SF");
 379         JarUtils.updateJar(s, "4.jar", "META-INF/OLD.RSA");
 380         verify("4.jar", "-verbose")
 381                 .shouldHaveExitValue(0)
 382                 .shouldContain("treated as unsigned")
 383                 .shouldContain("Unparsable signature-related file META-INF/OLD.RSA");
 384     }
 385 
 386     static OutputAnalyzer jarsigner(List<String> extra)
 387             throws Throwable {
 388         JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner")
 389                 .addVMArg("-Duser.language=en")
 390                 .addVMArg("-Duser.country=US")
 391                 .addToolArg("-keystore")
 392                 .addToolArg("tsks")
 393                 .addToolArg("-storepass")
 394                 .addToolArg("changeit");
 395         for (String s : extra) {
 396             if (s.startsWith("-J")) {
 397                 launcher.addVMArg(s.substring(2));
 398             } else {
 399                 launcher.addToolArg(s);
 400             }
 401         }
 402         System.err.println("COMMAND: ");
 403         for (String cmd : launcher.getCommand()) {
 404             System.err.print(cmd + " ");
 405         }
 406         System.err.println();
 407         return ProcessTools.executeCommand(launcher.getCommand());
 408     }
 409 
 410     static OutputAnalyzer verify(String file, String... extra)
 411             throws Throwable {
 412         List<String> args = new ArrayList<>();
 413         args.add("-verify");
 414         args.add(file);
 415         args.addAll(Arrays.asList(extra));
 416         return jarsigner(args);
 417     }
 418 
 419     static void checkBadKU(String file) throws Throwable {
 420         System.err.println("BadKU: " + file);
 421         verify(file)
 422                 .shouldHaveExitValue(0)
 423                 .shouldContain("treated as unsigned")
 424                 .shouldContain("re-run jarsigner with debug enabled");
 425         verify(file, "-verbose")
 426                 .shouldHaveExitValue(0)
 427                 .shouldContain("Signed by")
 428                 .shouldContain("treated as unsigned")
 429                 .shouldContain("re-run jarsigner with debug enabled");
 430         verify(file, "-J-Djava.security.debug=jar")
 431                 .shouldHaveExitValue(0)
 432                 .shouldContain("SignatureException: Key usage restricted")
 433                 .shouldContain("treated as unsigned")
 434                 .shouldContain("re-run jarsigner with debug enabled");
 435     }
 436 
 437     static void checkWeak(String file) throws Throwable {
 438         verify(file)
 439                 .shouldHaveExitValue(0)
 440                 .shouldContain("treated as unsigned")
 441                 .shouldMatch("weak algorithm that is now disabled.")
 442                 .shouldMatch("Re-run jarsigner with the -verbose option for more details");
 443         verify(file, "-verbose")
 444                 .shouldHaveExitValue(0)
 445                 .shouldContain("treated as unsigned")
 446                 .shouldMatch("weak algorithm that is now disabled by")
 447                 .shouldMatch("Digest algorithm: .*weak")
 448                 .shouldMatch("Signature algorithm: .*weak")
 449                 .shouldMatch("Timestamp digest algorithm: .*weak")
 450                 .shouldNotMatch("Timestamp signature algorithm: .*weak.*weak")
 451                 .shouldMatch("Timestamp signature algorithm: .*key.*weak");
 452         verify(file, "-J-Djava.security.debug=jar")
 453                 .shouldHaveExitValue(0)
 454                 .shouldMatch("SignatureException:.*Disabled");
 455     }
 456 
 457     static void checkTimestamp(String file, String policyId, String digestAlg)
 458             throws Exception {
 459         try (JarFile jf = new JarFile(file)) {
 460             JarEntry je = jf.getJarEntry("META-INF/OLD.RSA");
 461             try (InputStream is = jf.getInputStream(je)) {
 462                 byte[] content = IOUtils.readFully(is, -1, true);
 463                 PKCS7 p7 = new PKCS7(content);
 464                 SignerInfo[] si = p7.getSignerInfos();
 465                 if (si == null || si.length == 0) {
 466                     throw new Exception("Not signed");
 467                 }
 468                 PKCS9Attribute p9 = si[0].getUnauthenticatedAttributes()
 469                         .getAttribute(PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
 470                 PKCS7 tsToken = new PKCS7((byte[]) p9.getValue());
 471                 TimestampToken tt =
 472                         new TimestampToken(tsToken.getContentInfo().getData());
 473                 if (!tt.getHashAlgorithm().toString().equals(digestAlg)) {
 474                     throw new Exception("Digest alg different");
 475                 }
 476                 if (!tt.getPolicyID().equals(policyId)) {
 477                     throw new Exception("policyId different");
 478                 }
 479             }
 480         }
 481     }
 482 
 483     static int which = 0;
 484 
 485     /**
 486      * @param extra more args given to jarsigner


 487      */
 488     static OutputAnalyzer sign(String path, String... extra)
 489             throws Throwable {
 490         which++;
 491         System.err.println("\n>> Test #" + which + ": " + Arrays.toString(extra));
 492         List<String> args = new ArrayList<>();
 493         args.add("-J-Djava.security.egd=file:/dev/./urandom");
 494         args.add("-debug");
 495         args.add("-signedjar");
 496         args.add(path + ".jar");
 497         args.add("old.jar");
 498         args.add(path.equals("badku") ? "badku" : "old");
 499         if (!path.equals("none") && !path.equals("badku")) {
 500             args.add("-tsa");
 501             args.add(host + path);
 502          }
 503         args.addAll(Arrays.asList(extra));
 504         return jarsigner(args);
 505     }
 506 
 507     static void prepare() throws Exception {
 508         jdk.testlibrary.JarUtils.createJar("old.jar", "A");
 509         Files.deleteIfExists(Paths.get("tsks"));
 510         keytool("-alias ca -genkeypair -ext bc -dname CN=CA");
 511         keytool("-alias old -genkeypair -dname CN=old");
 512         keytool("-alias badku -genkeypair -dname CN=badku");
 513         keytool("-alias ts -genkeypair -dname CN=ts");
 514         keytool("-alias tsweak -genkeypair -keysize 512 -dname CN=tsbad1");
 515         keytool("-alias tsbad1 -genkeypair -dname CN=tsbad1");
 516         keytool("-alias tsbad2 -genkeypair -dname CN=tsbad2");
 517         keytool("-alias tsbad3 -genkeypair -dname CN=tsbad3");
 518 
 519         gencert("old");
 520         gencert("badku", "-ext ku:critical=keyAgreement");
 521         gencert("ts", "-ext eku:critical=ts");
 522         gencert("tsweak", "-ext eku:critical=ts");
 523         gencert("tsbad1");
 524         gencert("tsbad2", "-ext eku=ts");
 525         gencert("tsbad3", "-ext eku:critical=cs");
 526     }
 527 
 528     static void gencert(String alias, String... extra) throws Exception {
 529         keytool("-alias " + alias + " -certreq -file " + alias + ".req");
 530         String genCmd = "-gencert -alias ca -infile " +
 531                 alias + ".req -outfile " + alias + ".cert";
 532         for (String s : extra) {
 533             genCmd += " " + s;
 534         }
 535         keytool(genCmd);
 536         keytool("-alias " + alias + " -importcert -file " + alias + ".cert");

 537     }
 538 
 539     static void keytool(String cmd) throws Exception {
 540         cmd = "-keystore tsks -storepass changeit -keypass changeit " +
 541                 "-keyalg rsa -validity 200 " + cmd;
 542         sun.security.tools.keytool.Main.main(cmd.split(" "));
 543     }

 544 }