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 Unit tests for OCSPNonceExtension objects 28 * @modules java.base/sun.security.provider.certpath 29 * java.base/sun.security.util 30 * java.base/sun.security.x509 31 */ 32 33import java.security.cert.Extension; 34import java.io.ByteArrayOutputStream; 35import java.io.IOException; 36import java.util.*; 37 38import sun.security.util.DerValue; 39import sun.security.util.DerInputStream; 40import sun.security.util.ObjectIdentifier; 41import sun.security.provider.certpath.OCSPNonceExtension; 42import sun.security.x509.PKIXExtensions; 43 44public class OCSPNonceExtensionTests { 45 public static final boolean DEBUG = true; 46 public static final String OCSP_NONCE_OID = "1.3.6.1.5.5.7.48.1.2"; 47 public static final String ELEMENT_NONCE = "nonce"; 48 public static final String EXT_NAME = "OCSPNonce"; 49 50 // DER encoding for OCSP nonce extension: 51 // OID = 1.3.6.1.5.5.7.48.1.2 52 // Critical = true 53 // 48 bytes of 0xDEADBEEF 54 public static final byte[] OCSP_NONCE_DER = { 55 48, 66, 6, 9, 43, 6, 1, 5, 56 5, 7, 48, 1, 2, 1, 1, -1, 57 4, 50, 4, 48, -34, -83, -66, -17, 58 -34, -83, -66, -17, -34, -83, -66, -17, 59 -34, -83, -66, -17, -34, -83, -66, -17, 60 -34, -83, -66, -17, -34, -83, -66, -17, 61 -34, -83, -66, -17, -34, -83, -66, -17, 62 -34, -83, -66, -17, -34, -83, -66, -17, 63 -34, -83, -66, -17, 64 }; 65 66 // 16 bytes of 0xDEADBEEF 67 public static final byte[] DEADBEEF_16 = { 68 -34, -83, -66, -17, -34, -83, -66, -17, 69 -34, -83, -66, -17, -34, -83, -66, -17, 70 }; 71 72 // DER encoded extension using 16 bytes of DEADBEEF 73 public static final byte[] OCSP_NONCE_DB16 = { 74 48, 31, 6, 9, 43, 6, 1, 5, 75 5, 7, 48, 1, 2, 4, 18, 4, 76 16, -34, -83, -66, -17, -34, -83, -66, 77 -17, -34, -83, -66, -17, -34, -83, -66, 78 -17 79 }; 80 81 public static void main(String [] args) throws Exception { 82 Map<String, TestCase> testList = 83 new LinkedHashMap<String, TestCase>() {{ 84 put("CTOR Test (provide length)", testCtorByLength); 85 put("CTOR Test (provide nonce bytes)", testCtorByValue); 86 put("CTOR Test (set criticality forms)", testCtorCritForms); 87 put("CTOR Test (provide extension DER encoding)", 88 testCtorSuperByDerValue); 89 put("Test getName() method", testGetName); 90 }}; 91 92 System.out.println("============ Tests ============"); 93 int testNo = 0; 94 int numberFailed = 0; 95 Map.Entry<Boolean, String> result; 96 for (String testName : testList.keySet()) { 97 System.out.println("Test " + ++testNo + ": " + testName); 98 result = testList.get(testName).runTest(); 99 System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL")); 100 System.out.println(" " + 101 (result.getValue() != null ? result.getValue() : "")); 102 System.out.println("-------------------------------------------"); 103 if (!result.getKey()) { 104 numberFailed++; 105 } 106 } 107 System.out.println("End Results: " + (testList.size() - numberFailed) + 108 " Passed" + ", " + numberFailed + " Failed."); 109 if (numberFailed > 0) { 110 throw new RuntimeException( 111 "One or more tests failed, see test output for details"); 112 } 113 } 114 115 private static void dumpHexBytes(byte[] data) { 116 if (data != null) { 117 for (int i = 0; i < data.length; i++) { 118 if (i % 16 == 0 && i != 0) { 119 System.out.print("\n"); 120 } 121 System.out.print(String.format("%02X ", data[i])); 122 } 123 System.out.print("\n"); 124 } 125 } 126 127 private static void debuglog(String message) { 128 if (DEBUG) { 129 System.out.println(message); 130 } 131 } 132 133 public static void verifyExtStructure(byte[] derData) throws IOException { 134 debuglog("verifyASN1Extension() received " + derData.length + " bytes"); 135 DerInputStream dis = new DerInputStream(derData); 136 137 // The sequenceItems array should be either two or three elements 138 // long. If three, then the criticality bit setting has been asserted. 139 DerValue[] sequenceItems = dis.getSequence(3); 140 debuglog("Found sequence containing " + sequenceItems.length + 141 " elements"); 142 if (sequenceItems.length != 2 && sequenceItems.length != 3) { 143 throw new RuntimeException("Incorrect number of items found in " + 144 "the SEQUENCE (Got " + sequenceItems.length + 145 ", expected 2 or 3 items)"); 146 } 147 148 int seqIndex = 0; 149 ObjectIdentifier extOid = sequenceItems[seqIndex++].getOID(); 150 debuglog("Found OID: " + extOid.toString()); 151 if (!extOid.equals((Object)PKIXExtensions.OCSPNonce_Id)) { 152 throw new RuntimeException("Incorrect OID (Got " + 153 extOid.toString() + ", expected " + 154 PKIXExtensions.OCSPNonce_Id.toString() + ")"); 155 } 156 157 if (sequenceItems.length == 3) { 158 // Non-default criticality bit setting should be at index 1 159 boolean isCrit = sequenceItems[seqIndex++].getBoolean(); 160 debuglog("Found BOOLEAN (critical): " + isCrit); 161 } 162 163 // The extnValue is an encapsulating OCTET STRING that contains the 164 // extension's value. For the OCSP Nonce, that value itself is also 165 // an OCTET STRING consisting of the random bytes. 166 DerValue extnValue = 167 new DerValue(sequenceItems[seqIndex++].getOctetString()); 168 byte[] nonceData = extnValue.getOctetString(); 169 debuglog("Found " + nonceData.length + " bytes of nonce data"); 170 } 171 172 public interface TestCase { 173 Map.Entry<Boolean, String> runTest(); 174 } 175 176 public static final TestCase testCtorByLength = new TestCase() { 177 @Override 178 public Map.Entry<Boolean, String> runTest() { 179 Boolean pass = Boolean.FALSE; 180 String message = null; 181 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 182 // Try sending in a negative length 183 try { 184 Extension negLenNonce = new OCSPNonceExtension(-8); 185 throw new RuntimeException( 186 "Accepted a negative length nonce"); 187 } catch (IllegalArgumentException iae) { } 188 189 // How about a zero length? 190 try { 191 Extension zeroLenNonce = new OCSPNonceExtension(0); 192 throw new RuntimeException("Accepted a zero length nonce"); 193 } catch (IllegalArgumentException iae) { } 194 195 // Valid input to constructor 196 Extension nonceByLen = new OCSPNonceExtension(32); 197 198 // Verify overall encoded extension structure 199 nonceByLen.encode(baos); 200 verifyExtStructure(baos.toByteArray()); 201 202 // Verify the name, elements, and data conform to 203 // expected values for this specific object. 204 boolean crit = nonceByLen.isCritical(); 205 String oid = nonceByLen.getId(); 206 DerValue nonceData = new DerValue(nonceByLen.getValue()); 207 208 if (crit) { 209 message = "Extension incorrectly marked critical"; 210 } else if (!oid.equals(OCSP_NONCE_OID)) { 211 message = "Incorrect OID (Got " + oid + ", Expected " + 212 OCSP_NONCE_OID + ")"; 213 } else if (nonceData.getTag() != DerValue.tag_OctetString) { 214 message = "Incorrect nonce data tag type (Got " + 215 String.format("0x%02X", nonceData.getTag()) + 216 ", Expected 0x04)"; 217 } else if (nonceData.getOctetString().length != 32) { 218 message = "Incorrect nonce byte length (Got " + 219 nonceData.getOctetString().length + 220 ", Expected 32)"; 221 } else { 222 pass = Boolean.TRUE; 223 } 224 } catch (Exception e) { 225 e.printStackTrace(System.out); 226 message = e.getClass().getName(); 227 } 228 229 return new AbstractMap.SimpleEntry<>(pass, message); 230 } 231 }; 232 233 public static final TestCase testCtorByValue = new TestCase() { 234 @Override 235 public Map.Entry<Boolean, String> runTest() { 236 Boolean pass = Boolean.FALSE; 237 String message = null; 238 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 239 240 // Try giving a null value for the nonce 241 try { 242 Extension nullNonce = new OCSPNonceExtension(null); 243 throw new RuntimeException("Accepted a null nonce"); 244 } catch (NullPointerException npe) { } 245 246 // How about a zero-length byte array? 247 try { 248 Extension zeroLenNonce = 249 new OCSPNonceExtension(new byte[0]); 250 throw new RuntimeException("Accepted a zero length nonce"); 251 } catch (IllegalArgumentException iae) { } 252 253 OCSPNonceExtension nonceByValue = 254 new OCSPNonceExtension(DEADBEEF_16); 255 256 // Verify overall encoded extension structure 257 nonceByValue.encode(baos); 258 verifyExtStructure(baos.toByteArray()); 259 260 // Verify the name, elements, and data conform to 261 // expected values for this specific object. 262 boolean crit = nonceByValue.isCritical(); 263 String oid = nonceByValue.getId(); 264 byte[] nonceData = nonceByValue.getNonceValue(); 265 266 if (crit) { 267 message = "Extension incorrectly marked critical"; 268 } else if (!oid.equals(OCSP_NONCE_OID)) { 269 message = "Incorrect OID (Got " + oid + ", Expected " + 270 OCSP_NONCE_OID + ")"; 271 } else if (!Arrays.equals(nonceData, DEADBEEF_16)) { 272 message = "Returned nonce value did not match input"; 273 } else { 274 pass = Boolean.TRUE; 275 } 276 } catch (Exception e) { 277 e.printStackTrace(System.out); 278 message = e.getClass().getName(); 279 } 280 281 return new AbstractMap.SimpleEntry<>(pass, message); 282 } 283 }; 284 285 public static final TestCase testCtorCritForms = new TestCase() { 286 @Override 287 public Map.Entry<Boolean, String> runTest() { 288 Boolean pass = Boolean.FALSE; 289 String message = null; 290 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 291 Extension nonceByLength = new OCSPNonceExtension(true, 32); 292 Extension nonceByValue = 293 new OCSPNonceExtension(true, DEADBEEF_16); 294 pass = nonceByLength.isCritical() && nonceByValue.isCritical(); 295 if (!pass) { 296 message = "nonceByLength or nonceByValue was not marked " + 297 "critical as expected"; 298 } 299 } catch (Exception e) { 300 e.printStackTrace(System.out); 301 message = e.getClass().getName(); 302 } 303 304 return new AbstractMap.SimpleEntry<>(pass, message); 305 } 306 }; 307 308 309 public static final TestCase testCtorSuperByDerValue = new TestCase() { 310 @Override 311 public Map.Entry<Boolean, String> runTest() { 312 Boolean pass = Boolean.FALSE; 313 String message = null; 314 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 315 Extension nonceByDer = new sun.security.x509.Extension( 316 new DerValue(OCSP_NONCE_DER)); 317 318 // Verify overall encoded extension structure 319 nonceByDer.encode(baos); 320 verifyExtStructure(baos.toByteArray()); 321 322 // Verify the name, elements, and data conform to 323 // expected values for this specific object. 324 boolean crit = nonceByDer.isCritical(); 325 String oid = nonceByDer.getId(); 326 DerValue nonceData = new DerValue(nonceByDer.getValue()); 327 328 if (!crit) { 329 message = "Extension lacks expected criticality setting"; 330 } else if (!oid.equals(OCSP_NONCE_OID)) { 331 message = "Incorrect OID (Got " + oid + ", Expected " + 332 OCSP_NONCE_OID + ")"; 333 } else if (nonceData.getTag() != DerValue.tag_OctetString) { 334 message = "Incorrect nonce data tag type (Got " + 335 String.format("0x%02X", nonceData.getTag()) + 336 ", Expected 0x04)"; 337 } else if (nonceData.getOctetString().length != 48) { 338 message = "Incorrect nonce byte length (Got " + 339 nonceData.getOctetString().length + 340 ", Expected 48)"; 341 } else { 342 pass = Boolean.TRUE; 343 } 344 } catch (Exception e) { 345 e.printStackTrace(System.out); 346 message = e.getClass().getName(); 347 } 348 349 return new AbstractMap.SimpleEntry<>(pass, message); 350 } 351 }; 352 353 public static final TestCase testGetName = new TestCase() { 354 @Override 355 public Map.Entry<Boolean, String> runTest() { 356 Boolean pass = Boolean.FALSE; 357 String message = null; 358 try { 359 OCSPNonceExtension nonceByLen = new OCSPNonceExtension(32); 360 pass = new Boolean(nonceByLen.getName().equals(EXT_NAME)); 361 } catch (Exception e) { 362 e.printStackTrace(System.out); 363 message = e.getClass().getName(); 364 } 365 366 return new AbstractMap.SimpleEntry<>(pass, message); 367 } 368 }; 369} 370