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 }