1 /*
   2  * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   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.BufferedWriter;
  27 import java.io.ByteArrayOutputStream;
  28 import java.io.File;
  29 import java.io.FileInputStream;
  30 import java.io.FileOutputStream;
  31 import java.io.IOException;
  32 import java.io.InputStreamReader;
  33 import java.io.OutputStream;
  34 import java.io.OutputStreamWriter;
  35 import java.math.BigInteger;
  36 import java.net.InetSocketAddress;
  37 import java.security.KeyStore;
  38 import java.security.PrivateKey;
  39 import java.security.Signature;
  40 import java.security.SignatureException;
  41 import java.security.cert.Certificate;
  42 import java.security.cert.X509Certificate;
  43 import java.util.ArrayList;
  44 import java.util.Arrays;
  45 import java.util.Calendar;
  46 import java.util.Date;
  47 import java.util.List;
  48 import sun.security.pkcs.ContentInfo;
  49 import sun.security.pkcs.PKCS7;
  50 import sun.security.pkcs.SignerInfo;
  51 import sun.security.util.DerOutputStream;
  52 import sun.security.util.DerValue;
  53 import sun.security.util.ObjectIdentifier;
  54 import sun.security.x509.AlgorithmId;
  55 import sun.security.x509.X500Name;
  56 
  57 import org.openjdk.jigsaw.cli.Librarian;
  58 import org.openjdk.jigsaw.cli.Packager;
  59 import org.openjdk.jigsaw.cli.Signer;
  60 
  61 // copied from test/sun/security/tools/jarsigner and adapted for use with
  62 // jigsaw signed modules
  63 public class TimestampTest {
  64     private static final String BASE_DIR = System.getProperty("test.src", ".");
  65     private static final String MNAME = "test.security";
  66     private int port;
  67     private String[] jsignArgs = {
  68         "-v",
  69         "--keystore",
  70         "keystore.jks"
  71     };
  72 
  73     private String[] jmodArgs = {
  74         "-L",
  75         "z.lib",
  76         "install",
  77         MNAME + "@0.1.jmod"
  78     };
  79 
  80     private String[] jpkgArgs = {
  81         "-L",
  82         "z.lib",
  83         "-m",
  84         "z.modules/" + MNAME,
  85         "jmod",
  86         MNAME
  87     };
  88 
  89     private File moduleDir = new File("z.lib", MNAME);
  90 
  91     public static void main(String[] args) throws Exception {
  92         new TimestampTest().run();
  93     }
  94 
  95     void run() throws Exception {
  96 
  97         Handler h = new Handler();
  98         HttpServer server = HttpServer.create(new InetSocketAddress(0), 0);
  99         port = server.getAddress().getPort();
 100         HttpContext ctx = server.createContext("/", h);
 101         server.start();
 102 
 103         System.setProperty("org.openjdk.system.security.cacerts",
 104                            "keystore.jks");
 105         System.setProperty("java.security.egd", "file:/dev/./urandom");
 106         try {
 107             test();
 108         } finally {
 109             server.stop(0);
 110         }
 111         System.out.println("All tests passed");
 112     }
 113 
 114     void test() throws Exception {
 115         Librarian.main(new String[] { "-L", "z.lib", "create" });
 116         testExpiredWithValidTimestamp();
 117         testNonExpiredWithValidTimestamp();
 118         testExpiredWithInvalidTimestamp();
 119     }
 120 
 121     void testNonExpiredWithValidTimestamp() throws Exception {
 122         reset();
 123         sign("signer", 0);
 124         install();
 125     }
 126 
 127     void testExpiredWithInvalidTimestamp() throws Exception {
 128         reset();
 129         sign("expired-signer", 0);
 130         try {
 131             install();
 132             throw new Exception("Expected SignatureException");
 133         } catch (Exception e) {
 134             if (!(e.getCause() instanceof SignatureException))
 135                 throw e;
 136             System.out.println(e.getCause());
 137         }
 138     }
 139 
 140     void testExpiredWithValidTimestamp() throws Exception {
 141         reset();
 142         sign("expired-signer", 1);
 143         install();
 144     }
 145 
 146     void reset() {
 147         if (moduleDir.exists())
 148             deleteAll(moduleDir);
 149     }
 150 
 151     /**
 152      * Delete a file or a directory (including all its contents).
 153      */
 154     boolean deleteAll(File file) {
 155         if (file.isDirectory()) {
 156             for (File f: file.listFiles())
 157                 deleteAll(f);
 158         }
 159         return file.delete();
 160     }
 161 
 162     void sign(String alias, int type) throws Exception {
 163         List<String> args = new ArrayList<>();
 164         args.addAll(Arrays.asList(jpkgArgs));
 165         Packager.run(args.toArray(new String[0]));
 166         args = new ArrayList<>();
 167         args.addAll(Arrays.asList(jsignArgs));
 168         args.add("--tsa");
 169         args.add("http://localhost:" + port + "/" + type);
 170         args.add(MNAME + "@0.1.jmod");
 171         args.add(alias);
 172         Signer.run(args.toArray(new String[0]));
 173     }
 174 
 175     void install() throws Exception {
 176         List<String> args = new ArrayList<>();
 177         args.addAll(Arrays.asList(jmodArgs));
 178         Librarian.run(args.toArray(new String[0]));
 179     }
 180 
 181     static class Handler implements HttpHandler {
 182         public void handle(HttpExchange t) throws IOException {
 183             System.out.println("Got request");
 184             int len = 0;
 185             for (String h: t.getRequestHeaders().keySet()) {
 186                 if (h.equalsIgnoreCase("Content-length")) {
 187                     len = Integer.valueOf(t.getRequestHeaders().get(h).get(0));
 188                 }
 189             }
 190             byte[] input = new byte[len];
 191             t.getRequestBody().read(input);
 192 
 193             try {
 194                 int path = 0;
 195                 if (t.getRequestURI().getPath().length() > 1) {
 196                     path = Integer.parseInt(
 197                             t.getRequestURI().getPath().substring(1));
 198                 }
 199                 byte[] output = sign(input, path);
 200                 Headers out = t.getResponseHeaders();
 201                 out.set("Content-Type", "application/timestamp-reply");
 202 
 203                 t.sendResponseHeaders(200, output.length);
 204                 OutputStream os = t.getResponseBody();
 205                 os.write(output);
 206             } catch (Exception e) {
 207                 e.printStackTrace();
 208                 t.sendResponseHeaders(500, 0);
 209             }
 210             t.close();
 211         }
 212 
 213         /**
 214          * @param input The data to sign
 215          * @param path different cases to simulate, impl on URL path
 216          * 0: normal
 217          * 1: backdate timestamp
 218          * @returns the signed
 219          */
 220         byte[] sign(byte[] input, int path) throws Exception {
 221             // Read TSRequest
 222             DerValue value = new DerValue(input);
 223             System.err.println("\nIncoming Request\n===================");
 224             System.err.println("Version: " + value.data.getInteger());
 225             DerValue messageImprint = value.data.getDerValue();
 226             AlgorithmId aid = AlgorithmId.parse(
 227                     messageImprint.data.getDerValue());
 228             System.err.println("AlgorithmId: " + aid);
 229 
 230             BigInteger nonce = null;
 231             while (value.data.available() > 0) {
 232                 DerValue v = value.data.getDerValue();
 233                 if (v.tag == DerValue.tag_Integer) {
 234                     nonce = v.getBigInteger();
 235                     System.err.println("nonce: " + nonce);
 236                 } else if (v.tag == DerValue.tag_Boolean) {
 237                     System.err.println("certReq: " + v.getBoolean());
 238                 }
 239             }
 240 
 241             // Write TSResponse
 242             System.err.println("\nResponse\n===================");
 243             KeyStore ks = KeyStore.getInstance("JKS");
 244             ks.load(new FileInputStream("keystore.jks"),
 245                     "test123".toCharArray());
 246 
 247             String alias = "tsa";
 248 
 249             DerOutputStream statusInfo = new DerOutputStream();
 250             statusInfo.putInteger(0);
 251 
 252             DerOutputStream token = new DerOutputStream();
 253             AlgorithmId[] algorithms = {aid};
 254             Certificate[] chain = ks.getCertificateChain(alias);
 255             X509Certificate signer = (X509Certificate)chain[0];
 256             X509Certificate[] signerCertificateChain = new X509Certificate[1];
 257             signerCertificateChain[0] = signer;
 258 
 259             DerOutputStream tst = new DerOutputStream();
 260 
 261             tst.putInteger(1);
 262             tst.putOID(new ObjectIdentifier("1.2.3.4"));    // policy
 263 
 264             tst.putDerValue(messageImprint);
 265             tst.putInteger(1);
 266 
 267             if (path != 1) {
 268                 Calendar cal = Calendar.getInstance();
 269                 tst.putGeneralizedTime(cal.getTime());
 270             } else {
 271                 // expired-signed is Valid from: Sat Jan 01 11:32:36 EST 2005 
 272                 // until: Sun Jan 01 11:32:36 EST 2006
 273                 // timestamp with a date within that validity period
 274                 tst.putGeneralizedTime(new Date(1120190400000l)); // 07/01/2005
 275             }
 276 
 277             tst.putInteger(nonce);
 278 
 279             DerOutputStream tstInfo = new DerOutputStream();
 280             tstInfo.write(DerValue.tag_Sequence, tst);
 281 
 282             DerOutputStream tstInfo2 = new DerOutputStream();
 283             tstInfo2.putOctetString(tstInfo.toByteArray());
 284 
 285             Signature sig = Signature.getInstance("SHA1withDSA");
 286             sig.initSign((PrivateKey)(ks.getKey(
 287                     alias, "test123".toCharArray())));
 288             sig.update(tstInfo.toByteArray());
 289 
 290             ContentInfo contentInfo = new ContentInfo(new ObjectIdentifier(
 291                     "1.2.840.113549.1.9.16.1.4"),
 292                     new DerValue(tstInfo2.toByteArray()));
 293 
 294             System.err.println("Signing...");
 295             System.err.println(new X500Name(signer
 296                     .getIssuerX500Principal().getName()));
 297             System.err.println(signer.getSerialNumber());
 298 
 299             SignerInfo signerInfo = new SignerInfo(
 300                     new X500Name(signer.getIssuerX500Principal().getName()),
 301                     signer.getSerialNumber(),
 302                     AlgorithmId.get("SHA1"), AlgorithmId.get("DSA"), sig.sign());
 303 
 304             SignerInfo[] signerInfos = {signerInfo};
 305             PKCS7 p7 =
 306                     new PKCS7(algorithms, contentInfo, signerCertificateChain,
 307                     signerInfos);
 308             ByteArrayOutputStream p7out = new ByteArrayOutputStream();
 309             p7.encodeSignedData(p7out);
 310 
 311             DerOutputStream response = new DerOutputStream();
 312             response.write(DerValue.tag_Sequence, statusInfo);
 313             response.putDerValue(new DerValue(p7out.toByteArray()));
 314 
 315             DerOutputStream out = new DerOutputStream();
 316             out.write(DerValue.tag_Sequence, response);
 317 
 318             return out.toByteArray();
 319         }
 320     }
 321 }