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