1/* 2 * Copyright (c) 2015, 2016, 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 24package sun.security.ssl; 25 26import java.io.IOException; 27import java.util.*; 28import java.nio.ByteBuffer; 29 30/* 31 * Checks that the hash value for a certificate's issuer name is generated 32 * correctly. Requires any certificate that is not self-signed. 33 * 34 * NOTE: this test uses Sun private classes which are subject to change. 35 */ 36public class CertStatusReqExtensionTests { 37 38 private static final boolean debug = false; 39 40 // Default status_request extension (type = ocsp, OCSPStatusRequest 41 // with no responder IDs or extensions 42 private static final byte[] CSRE_DEF_OSR = {1, 0, 0, 0, 0}; 43 44 // A status_request extension using a user-defined type (0xFF) and 45 // an underlying no-Responder ID/no-extension OCSPStatusRequest 46 private static final byte[] CSRE_TYPE_FF = {-1, 0, 0, 0, 0}; 47 48 // A CertStatusReqExtension with 5 ResponderIds and 1 Extension 49 private static final byte[] CSRE_REQ_RID_EXTS = { 50 1, 0, -13, 0, 59, -95, 57, 48, 51 55, 49, 16, 48, 14, 6, 3, 85, 52 4, 10, 19, 7, 83, 111, 109, 101, 53 73, 110, 99, 49, 16, 48, 14, 6, 54 3, 85, 4, 11, 19, 7, 83, 111, 55 109, 101, 80, 75, 73, 49, 17, 48, 56 15, 6, 3, 85, 4, 3, 19, 8, 57 83, 111, 109, 101, 79, 67, 83, 80, 58 0, 68, -95, 66, 48, 64, 49, 13, 59 48, 11, 6, 3, 85, 4, 10, 19, 60 4, 79, 104, 77, 121, 49, 14, 48, 61 12, 6, 3, 85, 4, 11, 19, 5, 62 66, 101, 97, 114, 115, 49, 15, 48, 63 13, 6, 3, 85, 4, 11, 19, 6, 64 84, 105, 103, 101, 114, 115, 49, 14, 65 48, 12, 6, 3, 85, 4, 3, 19, 66 5, 76, 105, 111, 110, 115, 0, 58, 67 -95, 56, 48, 54, 49, 16, 48, 14, 68 6, 3, 85, 4, 10, 19, 7, 67, 69 111, 109, 112, 97, 110, 121, 49, 13, 70 48, 11, 6, 3, 85, 4, 11, 19, 71 4, 87, 101, 115, 116, 49, 19, 48, 72 17, 6, 3, 85, 4, 3, 19, 10, 73 82, 101, 115, 112, 111, 110, 100, 101, 74 114, 49, 0, 24, -94, 22, 4, 20, 75 -67, -36, 114, 121, 92, -79, 116, -1, 76 102, -107, 7, -21, 18, -113, 64, 76, 77 96, -7, -66, -63, 0, 24, -94, 22, 78 4, 20, -51, -69, 107, -82, -39, -87, 79 45, 25, 41, 28, -76, -68, -11, -110, 80 -94, -97, 62, 47, 58, -125, 0, 51, 81 48, 49, 48, 47, 6, 9, 43, 6, 82 1, 5, 5, 7, 48, 1, 2, 4, 83 34, 4, 32, -26, -81, -120, -61, -127, 84 -79, 0, -39, -54, 49, 3, -51, -57, 85 -85, 19, -126, 94, -2, 21, 26, 98, 86 6, 105, -35, -37, -29, -73, 101, 53, 87 44, 15, -19 88 }; 89 90 public static void main(String[] args) throws Exception { 91 Map<String, TestCase> testList = 92 new LinkedHashMap<String, TestCase>() {{ 93 put("CTOR (default)", testCtorDefault); 94 put("CTOR (int, StatusRequest)", testCtorStatReqs); 95 put("CTOR (HandshakeInStream, length, getReqType, getRequest)", 96 testCtorInStream); 97 }}; 98 99 TestUtils.runTests(testList); 100 } 101 102 public static final TestCase testCtorDefault = new TestCase() { 103 @Override 104 public Map.Entry<Boolean, String> runTest() { 105 Boolean pass = Boolean.FALSE; 106 String message = null; 107 try { 108 CertStatusReqExtension csreDef = new CertStatusReqExtension(); 109 HandshakeOutStream hsout = 110 new HandshakeOutStream(null); 111 csreDef.send(hsout); 112 TestUtils.valueCheck(wrapExtData(null), hsout.toByteArray()); 113 114 // The length should be 4 (2 bytes for the type, 2 for the 115 // encoding of zero-length 116 if (csreDef.length() != 4) { 117 throw new RuntimeException("Incorrect length from " + 118 "default object. Expected 4, got " + 119 csreDef.length()); 120 } 121 122 // Since there's no data, there are no status_type or request 123 // data fields defined. Both should return null in this case 124 if (csreDef.getType() != null) { 125 throw new RuntimeException("Default CSRE returned " + 126 "non-null status_type"); 127 } else if (csreDef.getRequest() != null) { 128 throw new RuntimeException("Default CSRE returned " + 129 "non-null request object"); 130 } 131 132 pass = Boolean.TRUE; 133 } catch (Exception e) { 134 e.printStackTrace(System.out); 135 message = e.getClass().getName(); 136 } 137 138 return new AbstractMap.SimpleEntry<>(pass, message); 139 } 140 }; 141 142 public static final TestCase testCtorStatReqs = new TestCase() { 143 @Override 144 public Map.Entry<Boolean, String> runTest() { 145 Boolean pass = Boolean.FALSE; 146 String message = null; 147 try { 148 HandshakeOutStream hsout = 149 new HandshakeOutStream(null); 150 StatusRequest basicStatReq = new OCSPStatusRequest(); 151 152 // Create an extension using a default-style OCSPStatusRequest 153 // (no responder IDs, no extensions). 154 CertStatusReqExtension csre1 = new CertStatusReqExtension( 155 StatusRequestType.OCSP, basicStatReq); 156 csre1.send(hsout); 157 TestUtils.valueCheck(wrapExtData(CSRE_DEF_OSR), 158 hsout.toByteArray()); 159 hsout.reset(); 160 161 // Create the extension using a StatusRequestType not already 162 // instantiated as a static StatusRequestType 163 // (e.g. OCSP/OCSP_MULTI) 164 CertStatusReqExtension csre2 = 165 new CertStatusReqExtension(StatusRequestType.get(-1), 166 basicStatReq); 167 csre2.send(hsout); 168 TestUtils.valueCheck(wrapExtData(CSRE_TYPE_FF), 169 hsout.toByteArray()); 170 171 // Create the extension using a StatusRequest that 172 // does not match the status_type field 173 // This should throw an IllegalArgumentException 174 try { 175 CertStatusReqExtension csreBadRequest = 176 new CertStatusReqExtension(StatusRequestType.OCSP, 177 new BogusStatusRequest()); 178 throw new RuntimeException("Constructor accepted a " + 179 "StatusRequest that is inconsistent with " + 180 "the status_type"); 181 } catch (IllegalArgumentException iae) { } 182 183 // We don't allow a null value for the StatusRequestType 184 // parameter in this constructor. 185 try { 186 CertStatusReqExtension csreBadRequest = 187 new CertStatusReqExtension(null, basicStatReq); 188 throw new RuntimeException("Constructor accepted a " + 189 "null StatusRequestType"); 190 } catch (NullPointerException npe) { } 191 192 // We also don't allow a null value for the StatusRequest 193 // parameter in this constructor. 194 try { 195 CertStatusReqExtension csreBadRequest = 196 new CertStatusReqExtension(StatusRequestType.OCSP, 197 null); 198 throw new RuntimeException("Constructor accepted a " + 199 "null StatusRequest"); 200 } catch (NullPointerException npe) { } 201 202 pass = Boolean.TRUE; 203 } catch (Exception e) { 204 e.printStackTrace(System.out); 205 message = e.getClass().getName(); 206 } 207 208 return new AbstractMap.SimpleEntry<>(pass, message); 209 } 210 }; 211 212 // Test the constructor that builds the ob ject using data from 213 // a HandshakeInStream 214 // This also tests the length, getReqType and getRequest methods 215 public static final TestCase testCtorInStream = new TestCase() { 216 @Override 217 public Map.Entry<Boolean, String> runTest() { 218 Boolean pass = Boolean.FALSE; 219 String message = null; 220 OCSPStatusRequest osr; 221 222 try { 223 // To simulate the extension coming in a ServerHello, the 224 // type and length would already be read by HelloExtensions 225 // and there is no extension data 226 HandshakeInStream hsis = new HandshakeInStream(); 227 hsis.incomingRecord(ByteBuffer.wrap(new byte[0])); 228 CertStatusReqExtension csre = 229 new CertStatusReqExtension(hsis, hsis.available()); 230 // Verify length/type/request 231 if (csre.length() != 4) { 232 throw new RuntimeException("Invalid length: received " + 233 csre.length() + ", expected 4"); 234 } else if (csre.getType() != null) { 235 throw new RuntimeException("Non-null type from default " + 236 "extension"); 237 } else if (csre.getRequest() != null) { 238 throw new RuntimeException("Non-null request from default " + 239 "extension"); 240 } 241 242 // Try the an extension with a default OCSPStatusRequest 243 hsis = new HandshakeInStream(); 244 hsis.incomingRecord(ByteBuffer.wrap(CSRE_DEF_OSR)); 245 csre = new CertStatusReqExtension(hsis, hsis.available()); 246 if (csre.length() != (CSRE_DEF_OSR.length + 4)) { 247 throw new RuntimeException("Invalid length: received " + 248 csre.length() + ", expected " + 249 CSRE_DEF_OSR.length + 4); 250 } else if (!csre.getType().equals(StatusRequestType.OCSP)) { 251 throw new RuntimeException("Unknown status_type: " + 252 String.format("0x%02X", csre.getType().id)); 253 } else { 254 osr = (OCSPStatusRequest)csre.getRequest(); 255 if (!osr.getResponderIds().isEmpty() || 256 !osr.getExtensions().isEmpty()) { 257 throw new RuntimeException("Non-default " + 258 "OCSPStatusRequest found in extension"); 259 } 260 } 261 262 // Try with a non-default extension 263 hsis = new HandshakeInStream(); 264 hsis.incomingRecord(ByteBuffer.wrap(CSRE_REQ_RID_EXTS)); 265 csre = new CertStatusReqExtension(hsis, hsis.available()); 266 if (csre.length() != (CSRE_REQ_RID_EXTS.length + 4)) { 267 throw new RuntimeException("Invalid length: received " + 268 csre.length() + ", expected " + 269 CSRE_REQ_RID_EXTS.length + 4); 270 } else if (!(csre.getType().equals(StatusRequestType.OCSP))) { 271 throw new RuntimeException("Unknown status_type: " + 272 String.format("0x%02X", csre.getType().id)); 273 } else { 274 osr = (OCSPStatusRequest)csre.getRequest(); 275 if (osr.getResponderIds().size() != 5 || 276 osr.getExtensions().size() != 1) { 277 throw new RuntimeException("Incorrect number of " + 278 "ResponderIds or Extensions found in " + 279 "OCSPStatusRequest"); 280 } 281 } 282 283 // Create a CSRE that asserts status_request and has the 284 // proper length, but really is a bunch of random junk inside 285 // In this case, it will create an UnknownStatusRequest to 286 // handle the unparseable data. 287 byte[] junkData = new byte[48]; 288 Random r = new Random(System.currentTimeMillis()); 289 r.nextBytes(junkData); 290 junkData[0] = 7; // Ensure it isn't a valid status_type 291 hsis = new HandshakeInStream(); 292 hsis.incomingRecord(ByteBuffer.wrap(junkData)); 293 csre = new CertStatusReqExtension(hsis, hsis.available()); 294 StatusRequest sr = csre.getRequest(); 295 if (!(sr instanceof UnknownStatusRequest)) { 296 throw new RuntimeException("Expected returned status " + 297 "request to be of type UnknownStatusRequest but " + 298 "received " + sr.getClass().getName()); 299 } else if (csre.length() != (junkData.length + 4)) { 300 throw new RuntimeException("Invalid length: received " + 301 csre.length() + ", expected " + 302 junkData.length + 4); 303 } 304 305 // Set the leading byte to 1 (OCSP type) and run again 306 // It should pass the argument check and fail trying to parse 307 // the underlying StatusRequest. 308 junkData[0] = (byte)StatusRequestType.OCSP.id; 309 hsis = new HandshakeInStream(); 310 hsis.incomingRecord(ByteBuffer.wrap(junkData)); 311 try { 312 csre = new CertStatusReqExtension(hsis, hsis.available()); 313 throw new RuntimeException("Expected CTOR exception did " + 314 "not occur"); 315 } catch (IOException ioe) { } 316 317 pass = Boolean.TRUE; 318 } catch (Exception e) { 319 e.printStackTrace(System.out); 320 message = e.getClass().getName(); 321 } 322 323 return new AbstractMap.SimpleEntry<>(pass, message); 324 } 325 }; 326 327 // Take CSRE extension data and add extension type and length decorations 328 private static byte[] wrapExtData(byte[] extData) { 329 int bufferLen = (extData != null ? extData.length : 0) + 4; 330 ByteBuffer bb = ByteBuffer.allocate(bufferLen); 331 bb.putShort((short)ExtensionType.EXT_STATUS_REQUEST.id); 332 bb.putShort((short)(extData != null ? extData.length: 0)); 333 if (extData != null) { 334 bb.put(extData); 335 } 336 return bb.array(); 337 } 338} 339