1 /* 2 * Copyright (c) 2015, 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 /* 25 * @test 26 * @bug 8046321 27 * @summary OCSP Stapling for TLS (ResponderId tests) 28 * @modules java.base/sun.security.provider.certpath 29 * java.base/sun.security.x509 30 */ 31 32 import java.io.*; 33 import java.security.cert.*; 34 import java.security.KeyPair; 35 import java.security.KeyPairGenerator; 36 import java.util.AbstractMap; 37 import java.util.Arrays; 38 import java.util.Map; 39 import java.util.List; 40 import java.util.ArrayList; 41 import javax.security.auth.x500.X500Principal; 42 import sun.security.x509.KeyIdentifier; 43 import sun.security.provider.certpath.ResponderId; 44 45 /* 46 * NOTE: this test uses Sun private classes which are subject to change. 47 */ 48 public class ResponderIdTests { 49 50 private static final boolean debug = true; 51 52 // Source certificate created with the following command: 53 // keytool -genkeypair -alias test1 -keyalg rsa -keysize 2048 \ 54 // -validity 7300 -keystore test1.jks \ 55 // -dname "CN=SelfSignedResponder, OU=Validation Services, O=FakeCompany" 56 private static final String RESP_CERT_1 = 57 "-----BEGIN CERTIFICATE-----\n" + 58 "MIIDQzCCAiugAwIBAgIEXTqCCjANBgkqhkiG9w0BAQsFADBSMRQwEgYDVQQKEwtG\n" + 59 "YWtlQ29tcGFueTEcMBoGA1UECxMTVmFsaWRhdGlvbiBTZXJ2aWNlczEcMBoGA1UE\n" + 60 "AxMTU2VsZlNpZ25lZFJlc3BvbmRlcjAeFw0xNDA4MTcwNDM2MzBaFw0zNDA4MTIw\n" + 61 "NDM2MzBaMFIxFDASBgNVBAoTC0Zha2VDb21wYW55MRwwGgYDVQQLExNWYWxpZGF0\n" + 62 "aW9uIFNlcnZpY2VzMRwwGgYDVQQDExNTZWxmU2lnbmVkUmVzcG9uZGVyMIIBIjAN\n" + 63 "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApt2Cmw2k9tviLxaxE8aWNuoosWKL\n" + 64 "h+K4mNcDGKSoiChsqRqeJEnOxijDZqyFwfkaXvpAduFqYjz+Lij2HumvAjHDTui6\n" + 65 "bGcbsndRDPjvVo1S7f1oWsg7oiA8Lzmjl452S7UNBsDX5Dt1e84Xxwi40B1J2y8D\n" + 66 "FRPfYRWRlC1Z4kzqkBBa7JhANS+W8KDstFZxL4AwWH/byNwB5dl2j04ohg/Ar54e\n" + 67 "mu08PIH3hmi0pAu5wn9ariA7UA5lFWRJzvgGXV5J+QVEFuvKmeJ/Q6tU5OBJGw98\n" + 68 "zjd7F5B0iE+rJHTNF1aGaQfIorz04onV2WjH2VZA18AaMwqlY2br1SBdTQIDAQAB\n" + 69 "oyEwHzAdBgNVHQ4EFgQUG09HasSTYaTIh/CxxV/rcJV1LvowDQYJKoZIhvcNAQEL\n" + 70 "BQADggEBAIcUomNpZxGkocIzzybLyeyC6vLF1k0/unuPAHZLDP3o2JTstPhLHOCg\n" + 71 "FYw1VG2i23pjwKK2x/o80tJAOmW6vowbAPnNmtNIYO3gB/ZGiKeORoGKBCRDNvFa\n" + 72 "6ZrWxwTzT3EpVwRe7ameES0uP8+S4q2P5LhwMIMw7vGHoOQJgkAh/NUiCli1qRnJ\n" + 73 "FYd6cHMJJK5gF2FqQ7tdbA26pS06bkIEvil2M5wyKKWOydOa/pr1LgMf9KxljJ8J\n" + 74 "XlAOO/mGZGkYmWnQaQuBIDyWunWYlhsyCXMa8AScgs0uUeQp19tO7R0f03q/JXoZ\n" + 75 "1At1gZiMS7SdQaRWP5q+FunAeFWjsFE=\n" + 76 "-----END CERTIFICATE-----"; 77 78 private static final String RESP_CERT_1_SUBJ = 79 "CN=SelfSignedResponder, OU=Validation Services, O=FakeCompany"; 80 81 private static X509Certificate cert = null; 82 83 // The expected DER-encoding for a byName ResponderId derived 84 // from RESP_CERT_1 85 private static final byte[] EXP_NAME_ID_BYTES = { 86 -95, 84, 48, 82, 49, 20, 48, 18, 87 6, 3, 85, 4, 10, 19, 11, 70, 88 97, 107, 101, 67, 111, 109, 112, 97, 89 110, 121, 49, 28, 48, 26, 6, 3, 90 85, 4, 11, 19, 19, 86, 97, 108, 91 105, 100, 97, 116, 105, 111, 110, 32, 92 83, 101, 114, 118, 105, 99, 101, 115, 93 49, 28, 48, 26, 6, 3, 85, 4, 94 3, 19, 19, 83, 101, 108, 102, 83, 95 105, 103, 110, 101, 100, 82, 101, 115, 96 112, 111, 110, 100, 101, 114 97 }; 98 99 // The expected DER-encoding for a byKey ResponderId derived 100 // from RESP_CERT_1 101 private static final byte[] EXP_KEY_ID_BYTES = { 102 -94, 22, 4, 20, 27, 79, 71, 106, 103 -60, -109, 97, -92, -56, -121, -16, -79, 104 -59, 95, -21, 112, -107, 117, 46, -6 105 }; 106 107 // The DER encoding of a byKey ResponderId, but using an 108 // incorrect explicit tagging (CONTEXT CONSTRUCTED 3) 109 private static final byte[] INV_EXPLICIT_TAG_KEY_ID = { 110 -93, 22, 4, 20, 27, 79, 71, 106, 111 -60, -109, 97, -92, -56, -121, -16, -79, 112 -59, 95, -21, 112, -107, 117, 46, -6 113 }; 114 115 // These two ResponderId objects will have objects attached to them 116 // after the pos_CtorByName and pos_CtorByKeyId tests run. Those 117 // two tests should always be the first two that run. 118 public static ResponderId respByName; 119 public static ResponderId respByKeyId; 120 main(String[] args)121 public static void main(String[] args) throws Exception { 122 List<TestCase> testList = new ArrayList<>(); 123 124 testList.add(pos_CtorByName); 125 testList.add(pos_CtorByKeyId); 126 testList.add(pos_CtorByEncoding); 127 testList.add(neg_CtorByEncoding); 128 testList.add(pos_Equality); 129 testList.add(pos_GetEncoded); 130 testList.add(pos_GetRespName); 131 testList.add(pos_GetRespKeyId); 132 133 // Load the certificate object we can use for subsequent tests 134 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 135 cert = (X509Certificate)cf.generateCertificate( 136 new ByteArrayInputStream(RESP_CERT_1.getBytes())); 137 138 System.out.println("============ Tests ============"); 139 int testNo = 0; 140 int numberFailed = 0; 141 Map.Entry<Boolean, String> result; 142 for (TestCase test : testList) { 143 System.out.println("Test " + ++testNo + ": " + test.getName()); 144 result = test.runTest(); 145 System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL")); 146 System.out.println(" " + 147 (result.getValue() != null ? result.getValue() : "")); 148 System.out.println("-------------------------------------------"); 149 if (!result.getKey()) { 150 numberFailed++; 151 } 152 } 153 System.out.println("End Results: " + (testList.size() - numberFailed) + 154 " Passed" + ", " + numberFailed + " Failed."); 155 if (numberFailed > 0) { 156 throw new RuntimeException( 157 "One or more tests failed, see test output for details"); 158 } 159 } 160 dumpHexBytes(byte[] data)161 private static void dumpHexBytes(byte[] data) { 162 if (data != null) { 163 for (int i = 0; i < data.length; i++) { 164 if (i % 16 == 0 && i != 0) { 165 System.out.print("\n"); 166 } 167 System.out.print(String.format("%02X ", data[i])); 168 } 169 System.out.print("\n"); 170 } 171 } 172 173 public interface TestCase { getName()174 String getName(); runTest()175 Map.Entry<Boolean, String> runTest(); 176 } 177 178 public static final TestCase pos_CtorByName = new TestCase() { 179 @Override 180 public String getName() { 181 return "CTOR Test (by-name)"; 182 } 183 184 @Override 185 public Map.Entry<Boolean, String> runTest() { 186 Boolean pass = Boolean.FALSE; 187 String message = null; 188 try { 189 respByName = new ResponderId(cert.getSubjectX500Principal()); 190 pass = Boolean.TRUE; 191 } catch (Exception e) { 192 e.printStackTrace(System.out); 193 message = e.getClass().getName(); 194 } 195 196 return new AbstractMap.SimpleEntry<>(pass, message); 197 } 198 }; 199 200 public static final TestCase pos_CtorByKeyId = new TestCase() { 201 @Override 202 public String getName() { 203 return "CTOR Test (by-keyID)"; 204 } 205 206 @Override 207 public Map.Entry<Boolean, String> runTest() { 208 Boolean pass = Boolean.FALSE; 209 String message = null; 210 try { 211 respByKeyId = new ResponderId(cert.getPublicKey()); 212 pass = Boolean.TRUE; 213 } catch (Exception e) { 214 e.printStackTrace(System.out); 215 message = e.getClass().getName(); 216 } 217 218 return new AbstractMap.SimpleEntry<>(pass, message); 219 } 220 }; 221 222 public static final TestCase pos_CtorByEncoding = new TestCase() { 223 @Override 224 public String getName() { 225 return "CTOR Test (encoded bytes)"; 226 } 227 228 @Override 229 public Map.Entry<Boolean, String> runTest() { 230 Boolean pass = Boolean.FALSE; 231 String message = null; 232 try { 233 ResponderId ridByNameBytes = new ResponderId(EXP_NAME_ID_BYTES); 234 ResponderId ridByKeyIdBytes = new ResponderId(EXP_KEY_ID_BYTES); 235 236 if (!ridByNameBytes.equals(respByName)) { 237 throw new RuntimeException( 238 "Equals failed: respNameFromBytes vs. respByName"); 239 } else if (!ridByKeyIdBytes.equals(respByKeyId)) { 240 throw new RuntimeException( 241 "Equals failed: respKeyFromBytes vs. respByKeyId"); 242 } 243 pass = Boolean.TRUE; 244 } catch (Exception e) { 245 e.printStackTrace(System.out); 246 message = e.getClass().getName(); 247 } 248 249 return new AbstractMap.SimpleEntry<>(pass, message); 250 } 251 }; 252 253 public static final TestCase neg_CtorByEncoding = new TestCase() { 254 @Override 255 public String getName() { 256 return "CTOR Test (by encoding, unknown explicit tag)"; 257 } 258 259 @Override 260 public Map.Entry<Boolean, String> runTest() { 261 Boolean pass = Boolean.FALSE; 262 String message = null; 263 try { 264 ResponderId ridByKeyIdBytes = 265 new ResponderId(INV_EXPLICIT_TAG_KEY_ID); 266 throw new RuntimeException("Expected IOException not thrown"); 267 } catch (IOException ioe) { 268 // Make sure it's the IOException we're looking for 269 if (ioe.getMessage().contains("Invalid ResponderId content")) { 270 pass = Boolean.TRUE; 271 } else { 272 ioe.printStackTrace(System.out); 273 message = ioe.getClass().getName(); 274 } 275 } catch (Exception e) { 276 e.printStackTrace(System.out); 277 message = e.getClass().getName(); 278 } 279 280 return new AbstractMap.SimpleEntry<>(pass, message); 281 } 282 }; 283 284 285 public static final TestCase pos_Equality = new TestCase() { 286 @Override 287 public String getName() { 288 return "Simple Equality Test"; 289 } 290 291 @Override 292 public Map.Entry<Boolean, String> runTest() { 293 Boolean pass = Boolean.FALSE; 294 String message = null; 295 296 try { 297 // byName ResponderId equality test 298 ResponderId compName = 299 new ResponderId(new X500Principal(RESP_CERT_1_SUBJ)); 300 if (!respByName.equals(compName)) { 301 message = "ResponderId mismatch in byName comparison"; 302 } else if (respByKeyId.equals(compName)) { 303 message = "Invalid ResponderId match in byKeyId comparison"; 304 } else { 305 pass = Boolean.TRUE; 306 } 307 } catch (Exception e) { 308 e.printStackTrace(System.out); 309 message = e.getClass().getName(); 310 } 311 312 return new AbstractMap.SimpleEntry<>(pass, message); 313 } 314 }; 315 316 public static final TestCase pos_GetEncoded = new TestCase() { 317 @Override 318 public String getName() { 319 return "Get Encoded Value"; 320 } 321 322 @Override 323 public Map.Entry<Boolean, String> runTest() { 324 Boolean pass = Boolean.FALSE; 325 String message = null; 326 327 try { 328 // Pull out byName and byKey encodings, they should match 329 // the expected values 330 if (!Arrays.equals(respByName.getEncoded(), EXP_NAME_ID_BYTES)) { 331 message = "ResponderId byName encoding did not " + 332 "match expected value"; 333 } else if (!Arrays.equals(respByKeyId.getEncoded(), EXP_KEY_ID_BYTES)) { 334 message = "ResponderId byKeyId encoding did not " + 335 "match expected value"; 336 } else { 337 pass = Boolean.TRUE; 338 } 339 } catch (Exception e) { 340 e.printStackTrace(System.out); 341 message = e.getClass().getName(); 342 } 343 344 return new AbstractMap.SimpleEntry<>(pass, message); 345 } 346 }; 347 348 public static final TestCase pos_GetRespName = new TestCase() { 349 @Override 350 public String getName() { 351 return "Get Underlying Responder Name"; 352 } 353 354 @Override 355 public Map.Entry<Boolean, String> runTest() { 356 Boolean pass = Boolean.FALSE; 357 String message = null; 358 359 try { 360 // Test methods for pulling out the underlying 361 // X500Principal object 362 X500Principal testPrincipal = 363 new X500Principal(RESP_CERT_1_SUBJ); 364 if (!respByName.getResponderName().equals(testPrincipal)) { 365 message = "ResponderId Name did not match expected value"; 366 } else if (respByKeyId.getResponderName() != null) { 367 message = "Non-null responder name returned from " + 368 "ResponderId constructed byKey"; 369 } else { 370 pass = Boolean.TRUE; 371 } 372 } catch (Exception e) { 373 e.printStackTrace(System.out); 374 message = e.getClass().getName(); 375 } 376 377 return new AbstractMap.SimpleEntry<>(pass, message); 378 } 379 }; 380 381 public static final TestCase pos_GetRespKeyId = new TestCase() { 382 @Override 383 public String getName() { 384 return "Get Underlying Responder Key ID"; 385 } 386 387 @Override 388 public Map.Entry<Boolean, String> runTest() { 389 Boolean pass = Boolean.FALSE; 390 String message = null; 391 392 try { 393 // Test methods for pulling out the underlying 394 // KeyIdentifier object. Note: There is a minute chance that 395 // an RSA public key, once hashed into a key ID might collide 396 // with the one extracted from the certificate used to create 397 // respByKeyId. This is so unlikely to happen it is considered 398 // virtually impossible. 399 KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); 400 kpg.initialize(2048); 401 KeyPair rsaKey = kpg.generateKeyPair(); 402 KeyIdentifier testKeyId = new KeyIdentifier(rsaKey.getPublic()); 403 404 if (respByKeyId.getKeyIdentifier().equals(testKeyId)) { 405 message = "Unexpected match in ResponderId Key ID"; 406 } else if (respByName.getKeyIdentifier() != null) { 407 message = "Non-null key ID returned from " + 408 "ResponderId constructed byName"; 409 } else { 410 pass = Boolean.TRUE; 411 } 412 } catch (Exception e) { 413 e.printStackTrace(System.out); 414 message = e.getClass().getName(); 415 } 416 417 return new AbstractMap.SimpleEntry<>(pass, message); 418 } 419 }; 420 421 } 422