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 }