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 |
|
24 package sun.security.ssl; |
|
25 |
|
26 import java.io.IOException; |
|
27 import java.util.*; |
|
28 import 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 */ |
|
36 public 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 } |
|