e7f31340a0
Initial feature commit for OCSP stapling in JSSE Reviewed-by: xuelei, mullan
341 lines
14 KiB
Java
341 lines
14 KiB
Java
/*
|
|
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.security.ssl;
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8046321
|
|
* @summary OCSP Stapling for TLS (OCSPStatusRequestTests tests)
|
|
* @build OCSPStatusRequestTests TestCase TestUtils
|
|
* @run main/othervm sun.security.ssl.OCSPStatusRequestTests
|
|
*/
|
|
|
|
import java.security.cert.*;
|
|
import java.util.*;
|
|
import java.nio.ByteBuffer;
|
|
import javax.security.auth.x500.X500Principal;
|
|
import sun.security.provider.certpath.ResponderId;
|
|
import sun.security.provider.certpath.OCSPNonceExtension;
|
|
|
|
/*
|
|
* Checks that the hash value for a certificate's issuer name is generated
|
|
* correctly. Requires any certificate that is not self-signed.
|
|
*
|
|
* NOTE: this test uses Sun private classes which are subject to change.
|
|
*/
|
|
public class OCSPStatusRequestTests {
|
|
|
|
private static final boolean debug = false;
|
|
|
|
// The default (no Responder IDs or Extensions)
|
|
private static final byte[] DEF_OCSPREQ_BYTES = { 0, 0, 0, 0 };
|
|
|
|
// OCSP Extension with one Responder ID (byName: CN=OCSP Signer) and
|
|
// a nonce extension (32 bytes).
|
|
private static final byte[] OCSPREQ_1RID_1EXT = {
|
|
0, 28, 0, 26, -95, 24, 48, 22,
|
|
49, 20, 48, 18, 6, 3, 85, 4,
|
|
3, 19, 11, 79, 67, 83, 80, 32,
|
|
83, 105, 103, 110, 101, 114, 0, 51,
|
|
48, 49, 48, 47, 6, 9, 43, 6,
|
|
1, 5, 5, 7, 48, 1, 2, 4,
|
|
34, 4, 32, -34, -83, -66, -17, -34,
|
|
-83, -66, -17, -34, -83, -66, -17, -34,
|
|
-83, -66, -17, -34, -83, -66, -17, -34,
|
|
-83, -66, -17, -34, -83, -66, -17, -34,
|
|
-83, -66, -17
|
|
};
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
Map<String, TestCase> testList =
|
|
new LinkedHashMap<String, TestCase>() {{
|
|
put("CTOR (default)", testCtorDefault);
|
|
put("CTOR (Responder Id and Extension)", testCtorRidsExts);
|
|
put("CTOR (HandshakeInStream)", testCtorInStream);
|
|
put("CTOR (byte array)", testCtorByteArray);
|
|
put("Length tests", testLength);
|
|
put("Equals tests", testEquals);
|
|
}};
|
|
|
|
TestUtils.runTests(testList);
|
|
}
|
|
|
|
// Test the default constructor and its encoding
|
|
public static final TestCase testCtorDefault = new TestCase() {
|
|
@Override
|
|
public Map.Entry<Boolean, String> runTest() {
|
|
Boolean pass = Boolean.FALSE;
|
|
String message = null;
|
|
try {
|
|
// Create a OCSPStatusRequest with a single ResponderId
|
|
// and Extension
|
|
OCSPStatusRequest osrDefault = new OCSPStatusRequest();
|
|
HandshakeOutStream hsout = new HandshakeOutStream(null);
|
|
osrDefault.send(hsout);
|
|
System.out.println("Encoded Result:");
|
|
TestUtils.dumpBytes(hsout.toByteArray());
|
|
|
|
TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
|
|
pass = Boolean.TRUE;
|
|
} catch (Exception e) {
|
|
e.printStackTrace(System.out);
|
|
message = e.getClass().getName();
|
|
}
|
|
|
|
return new AbstractMap.SimpleEntry<>(pass, message);
|
|
}
|
|
};
|
|
|
|
// Test the constructor form that allows the user to specify zero
|
|
// or more ResponderId objects and/or Extensions
|
|
public static final TestCase testCtorRidsExts = new TestCase() {
|
|
@Override
|
|
public Map.Entry<Boolean, String> runTest() {
|
|
Boolean pass = Boolean.FALSE;
|
|
String message = null;
|
|
try {
|
|
List<ResponderId> ridList = new LinkedList<ResponderId>() {{
|
|
add(new ResponderId(new X500Principal("CN=OCSP Signer")));
|
|
}};
|
|
List<Extension> extList = new LinkedList<Extension>() {{
|
|
add(new OCSPNonceExtension(32));
|
|
}};
|
|
|
|
// Default-style OCSPStatusRequest using both empty Lists and
|
|
// null inputs
|
|
OCSPStatusRequest osrDef1 =
|
|
new OCSPStatusRequest(new LinkedList<ResponderId>(),
|
|
null);
|
|
OCSPStatusRequest osrDef2 =
|
|
new OCSPStatusRequest(null,
|
|
new LinkedList<Extension>());
|
|
HandshakeOutStream hsout = new HandshakeOutStream(null);
|
|
osrDef1.send(hsout);
|
|
System.out.println("Encoded Result:");
|
|
TestUtils.dumpBytes(hsout.toByteArray());
|
|
TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
|
|
|
|
hsout.reset();
|
|
osrDef2.send(hsout);
|
|
System.out.println("Encoded Result:");
|
|
TestUtils.dumpBytes(hsout.toByteArray());
|
|
TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray());
|
|
|
|
hsout.reset();
|
|
OCSPStatusRequest osrWithItems =
|
|
new OCSPStatusRequest(ridList, extList);
|
|
osrWithItems.send(hsout);
|
|
System.out.println("Encoded Result:");
|
|
byte[] encodedData = hsout.toByteArray();
|
|
TestUtils.dumpBytes(encodedData);
|
|
// Check everything except the last 32 bytes (nonce data)
|
|
TestUtils.valueCheck(OCSPREQ_1RID_1EXT, encodedData, 0, 0,
|
|
encodedData.length - 32);
|
|
|
|
pass = Boolean.TRUE;
|
|
} catch (Exception e) {
|
|
e.printStackTrace(System.out);
|
|
message = e.getClass().getName();
|
|
}
|
|
|
|
return new AbstractMap.SimpleEntry<>(pass, message);
|
|
}
|
|
};
|
|
|
|
// Test the constructor that builds the ob ject using data from
|
|
// a HandshakeInStream
|
|
public static final TestCase testCtorInStream = new TestCase() {
|
|
@Override
|
|
public Map.Entry<Boolean, String> runTest() {
|
|
Boolean pass = Boolean.FALSE;
|
|
String message = null;
|
|
try {
|
|
ResponderId checkRid =
|
|
new ResponderId(new X500Principal("CN=OCSP Signer"));
|
|
Extension checkExt = new OCSPNonceExtension(32);
|
|
|
|
HandshakeInStream hsis = new HandshakeInStream();
|
|
hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
|
|
OCSPStatusRequest osr = new OCSPStatusRequest(hsis);
|
|
|
|
List<ResponderId> ridList = osr.getResponderIds();
|
|
List<Extension> extList = osr.getExtensions();
|
|
|
|
if (ridList.size() != 1 || !ridList.contains(checkRid)) {
|
|
throw new RuntimeException("Responder list mismatch");
|
|
} else if (extList.size() != 1 ||
|
|
!extList.get(0).getId().equals(checkExt.getId())) {
|
|
throw new RuntimeException("Extension list mismatch");
|
|
}
|
|
|
|
pass = Boolean.TRUE;
|
|
} catch (Exception e) {
|
|
e.printStackTrace(System.out);
|
|
message = e.getClass().getName();
|
|
}
|
|
|
|
return new AbstractMap.SimpleEntry<>(pass, message);
|
|
}
|
|
};
|
|
|
|
// Test the constructor form that takes the data from a byte array
|
|
public static final TestCase testCtorByteArray = new TestCase() {
|
|
@Override
|
|
public Map.Entry<Boolean, String> runTest() {
|
|
Boolean pass = Boolean.FALSE;
|
|
String message = null;
|
|
try {
|
|
ResponderId checkRid =
|
|
new ResponderId(new X500Principal("CN=OCSP Signer"));
|
|
Extension checkExt = new OCSPNonceExtension(32);
|
|
|
|
OCSPStatusRequest osr =
|
|
new OCSPStatusRequest(OCSPREQ_1RID_1EXT);
|
|
|
|
List<ResponderId> ridList = osr.getResponderIds();
|
|
List<Extension> extList = osr.getExtensions();
|
|
|
|
if (ridList.size() != 1 || !ridList.contains(checkRid)) {
|
|
throw new RuntimeException("Responder list mismatch");
|
|
} else if (extList.size() != 1 ||
|
|
!extList.get(0).getId().equals(checkExt.getId())) {
|
|
throw new RuntimeException("Extension list mismatch");
|
|
}
|
|
pass = Boolean.TRUE;
|
|
} catch (Exception e) {
|
|
e.printStackTrace(System.out);
|
|
message = e.getClass().getName();
|
|
}
|
|
|
|
return new AbstractMap.SimpleEntry<>(pass, message);
|
|
}
|
|
};
|
|
|
|
// Test the length functions for both default and non-default
|
|
// OCSPStatusRequest objects
|
|
public static final TestCase testLength = new TestCase() {
|
|
@Override
|
|
public Map.Entry<Boolean, String> runTest() {
|
|
Boolean pass = Boolean.FALSE;
|
|
String message = null;
|
|
try {
|
|
HandshakeInStream hsis = new HandshakeInStream();
|
|
hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
|
|
OCSPStatusRequest osr = new OCSPStatusRequest(hsis);
|
|
OCSPStatusRequest osrDefault = new OCSPStatusRequest();
|
|
|
|
if (osrDefault.length() != DEF_OCSPREQ_BYTES.length) {
|
|
throw new RuntimeException("Invalid length for default: " +
|
|
"Expected" + DEF_OCSPREQ_BYTES.length +
|
|
", received " + osrDefault.length());
|
|
} else if (osr.length() != OCSPREQ_1RID_1EXT.length) {
|
|
throw new RuntimeException("Invalid length for default: " +
|
|
"Expected" + OCSPREQ_1RID_1EXT.length +
|
|
", received " + osr.length());
|
|
}
|
|
|
|
pass = Boolean.TRUE;
|
|
} catch (Exception e) {
|
|
e.printStackTrace(System.out);
|
|
message = e.getClass().getName();
|
|
}
|
|
|
|
return new AbstractMap.SimpleEntry<>(pass, message);
|
|
}
|
|
};
|
|
|
|
// Test the equals method with default and non-default objects
|
|
public static final TestCase testEquals = new TestCase() {
|
|
@Override
|
|
public Map.Entry<Boolean, String> runTest() {
|
|
Boolean pass = Boolean.FALSE;
|
|
String message = null;
|
|
try {
|
|
// Make two different lists with the same ResponderId values
|
|
// and also make a extension list
|
|
List<ResponderId> ridList1 = new LinkedList<ResponderId>() {{
|
|
add(new ResponderId(new X500Principal("CN=OCSP Signer")));
|
|
}};
|
|
List<ResponderId> ridList2 = new LinkedList<ResponderId>() {{
|
|
add(new ResponderId(new X500Principal("CN=OCSP Signer")));
|
|
}};
|
|
List<Extension> extList = new LinkedList<Extension>() {{
|
|
add(new OCSPNonceExtension(32));
|
|
}};
|
|
|
|
// We expect two default OCSP objects to be equal
|
|
OCSPStatusRequest osrDefault = new OCSPStatusRequest();
|
|
if (!osrDefault.equals(new OCSPStatusRequest())) {
|
|
throw new RuntimeException("Default OCSPStatusRequest" +
|
|
" equality test failed");
|
|
}
|
|
|
|
// null test (expect false return)
|
|
if (osrDefault.equals(null)) {
|
|
throw new RuntimeException("OCSPStatusRequest matched" +
|
|
" unexpectedly");
|
|
}
|
|
|
|
// Self-reference test
|
|
OCSPStatusRequest osrSelfRef = osrDefault;
|
|
if (!osrDefault.equals(osrSelfRef)) {
|
|
throw new RuntimeException("Default OCSPStatusRequest" +
|
|
" equality test failed");
|
|
}
|
|
|
|
// Two OCSPStatusRequests with matching ResponderIds should
|
|
// be considered equal
|
|
OCSPStatusRequest osrByList1 =
|
|
new OCSPStatusRequest(ridList1, null);
|
|
OCSPStatusRequest osrByList2 = new OCSPStatusRequest(ridList2,
|
|
Collections.emptyList());
|
|
if (!osrByList1.equals(osrByList2)) {
|
|
throw new RuntimeException("Single Responder ID " +
|
|
"OCSPStatusRequest equality test failed");
|
|
}
|
|
|
|
// We expect OCSPStatusRequests with different nonces to be
|
|
// considered unequal.
|
|
HandshakeInStream hsis = new HandshakeInStream();
|
|
hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT));
|
|
OCSPStatusRequest osrStream = new OCSPStatusRequest(hsis);
|
|
OCSPStatusRequest osrRidExt = new OCSPStatusRequest(ridList1,
|
|
extList);
|
|
if (osrStream.equals(osrRidExt)) {
|
|
throw new RuntimeException("OCSPStatusRequest matched" +
|
|
" unexpectedly");
|
|
}
|
|
|
|
pass = Boolean.TRUE;
|
|
} catch (Exception e) {
|
|
e.printStackTrace(System.out);
|
|
message = e.getClass().getName();
|
|
}
|
|
|
|
return new AbstractMap.SimpleEntry<>(pass, message);
|
|
}
|
|
};
|
|
|
|
}
|