| /* |
| * 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. |
| */ |
| |
| /* |
| * @test |
| * @bug 8046321 |
| * @summary Unit tests for OCSPNonceExtension objects |
| * @modules java.base/sun.security.provider.certpath |
| * java.base/sun.security.util |
| * java.base/sun.security.x509 |
| */ |
| |
| import java.security.cert.Extension; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.util.*; |
| |
| import sun.security.util.DerValue; |
| import sun.security.util.DerInputStream; |
| import sun.security.util.ObjectIdentifier; |
| import sun.security.provider.certpath.OCSPNonceExtension; |
| import sun.security.x509.PKIXExtensions; |
| |
| public class OCSPNonceExtensionTests { |
| public static final boolean DEBUG = true; |
| public static final String OCSP_NONCE_OID = "1.3.6.1.5.5.7.48.1.2"; |
| public static final String ELEMENT_NONCE = "nonce"; |
| public static final String EXT_NAME = "OCSPNonce"; |
| |
| // DER encoding for OCSP nonce extension: |
| // OID = 1.3.6.1.5.5.7.48.1.2 |
| // Critical = true |
| // 48 bytes of 0xDEADBEEF |
| public static final byte[] OCSP_NONCE_DER = { |
| 48, 66, 6, 9, 43, 6, 1, 5, |
| 5, 7, 48, 1, 2, 1, 1, -1, |
| 4, 50, 4, 48, -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, -34, -83, -66, -17, |
| -34, -83, -66, -17, -34, -83, -66, -17, |
| -34, -83, -66, -17, |
| }; |
| |
| // 16 bytes of 0xDEADBEEF |
| public static final byte[] DEADBEEF_16 = { |
| -34, -83, -66, -17, -34, -83, -66, -17, |
| -34, -83, -66, -17, -34, -83, -66, -17, |
| }; |
| |
| // DER encoded extension using 16 bytes of DEADBEEF |
| public static final byte[] OCSP_NONCE_DB16 = { |
| 48, 31, 6, 9, 43, 6, 1, 5, |
| 5, 7, 48, 1, 2, 4, 18, 4, |
| 16, -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 Test (provide length)", testCtorByLength); |
| put("CTOR Test (provide nonce bytes)", testCtorByValue); |
| put("CTOR Test (set criticality forms)", testCtorCritForms); |
| put("CTOR Test (provide extension DER encoding)", |
| testCtorSuperByDerValue); |
| put("Test getName() method", testGetName); |
| }}; |
| |
| System.out.println("============ Tests ============"); |
| int testNo = 0; |
| int numberFailed = 0; |
| Map.Entry<Boolean, String> result; |
| for (String testName : testList.keySet()) { |
| System.out.println("Test " + ++testNo + ": " + testName); |
| result = testList.get(testName).runTest(); |
| System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL")); |
| System.out.println(" " + |
| (result.getValue() != null ? result.getValue() : "")); |
| System.out.println("-------------------------------------------"); |
| if (!result.getKey()) { |
| numberFailed++; |
| } |
| } |
| System.out.println("End Results: " + (testList.size() - numberFailed) + |
| " Passed" + ", " + numberFailed + " Failed."); |
| if (numberFailed > 0) { |
| throw new RuntimeException( |
| "One or more tests failed, see test output for details"); |
| } |
| } |
| |
| private static void dumpHexBytes(byte[] data) { |
| if (data != null) { |
| for (int i = 0; i < data.length; i++) { |
| if (i % 16 == 0 && i != 0) { |
| System.out.print("\n"); |
| } |
| System.out.print(String.format("%02X ", data[i])); |
| } |
| System.out.print("\n"); |
| } |
| } |
| |
| private static void debuglog(String message) { |
| if (DEBUG) { |
| System.out.println(message); |
| } |
| } |
| |
| public static void verifyExtStructure(byte[] derData) throws IOException { |
| debuglog("verifyASN1Extension() received " + derData.length + " bytes"); |
| DerInputStream dis = new DerInputStream(derData); |
| |
| // The sequenceItems array should be either two or three elements |
| // long. If three, then the criticality bit setting has been asserted. |
| DerValue[] sequenceItems = dis.getSequence(3); |
| debuglog("Found sequence containing " + sequenceItems.length + |
| " elements"); |
| if (sequenceItems.length != 2 && sequenceItems.length != 3) { |
| throw new RuntimeException("Incorrect number of items found in " + |
| "the SEQUENCE (Got " + sequenceItems.length + |
| ", expected 2 or 3 items)"); |
| } |
| |
| int seqIndex = 0; |
| ObjectIdentifier extOid = sequenceItems[seqIndex++].getOID(); |
| debuglog("Found OID: " + extOid.toString()); |
| if (!extOid.equals((Object)PKIXExtensions.OCSPNonce_Id)) { |
| throw new RuntimeException("Incorrect OID (Got " + |
| extOid.toString() + ", expected " + |
| PKIXExtensions.OCSPNonce_Id.toString() + ")"); |
| } |
| |
| if (sequenceItems.length == 3) { |
| // Non-default criticality bit setting should be at index 1 |
| boolean isCrit = sequenceItems[seqIndex++].getBoolean(); |
| debuglog("Found BOOLEAN (critical): " + isCrit); |
| } |
| |
| // The extnValue is an encapsulating OCTET STRING that contains the |
| // extension's value. For the OCSP Nonce, that value itself is also |
| // an OCTET STRING consisting of the random bytes. |
| DerValue extnValue = |
| new DerValue(sequenceItems[seqIndex++].getOctetString()); |
| byte[] nonceData = extnValue.getOctetString(); |
| debuglog("Found " + nonceData.length + " bytes of nonce data"); |
| } |
| |
| public interface TestCase { |
| Map.Entry<Boolean, String> runTest(); |
| } |
| |
| public static final TestCase testCtorByLength = new TestCase() { |
| @Override |
| public Map.Entry<Boolean, String> runTest() { |
| Boolean pass = Boolean.FALSE; |
| String message = null; |
| try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { |
| // Try sending in a negative length |
| try { |
| Extension negLenNonce = new OCSPNonceExtension(-8); |
| throw new RuntimeException( |
| "Accepted a negative length nonce"); |
| } catch (IllegalArgumentException iae) { } |
| |
| // How about a zero length? |
| try { |
| Extension zeroLenNonce = new OCSPNonceExtension(0); |
| throw new RuntimeException("Accepted a zero length nonce"); |
| } catch (IllegalArgumentException iae) { } |
| |
| // Valid input to constructor |
| Extension nonceByLen = new OCSPNonceExtension(32); |
| |
| // Verify overall encoded extension structure |
| nonceByLen.encode(baos); |
| verifyExtStructure(baos.toByteArray()); |
| |
| // Verify the name, elements, and data conform to |
| // expected values for this specific object. |
| boolean crit = nonceByLen.isCritical(); |
| String oid = nonceByLen.getId(); |
| DerValue nonceData = new DerValue(nonceByLen.getValue()); |
| |
| if (crit) { |
| message = "Extension incorrectly marked critical"; |
| } else if (!oid.equals(OCSP_NONCE_OID)) { |
| message = "Incorrect OID (Got " + oid + ", Expected " + |
| OCSP_NONCE_OID + ")"; |
| } else if (nonceData.getTag() != DerValue.tag_OctetString) { |
| message = "Incorrect nonce data tag type (Got " + |
| String.format("0x%02X", nonceData.getTag()) + |
| ", Expected 0x04)"; |
| } else if (nonceData.getOctetString().length != 32) { |
| message = "Incorrect nonce byte length (Got " + |
| nonceData.getOctetString().length + |
| ", Expected 32)"; |
| } else { |
| pass = Boolean.TRUE; |
| } |
| } catch (Exception e) { |
| e.printStackTrace(System.out); |
| message = e.getClass().getName(); |
| } |
| |
| return new AbstractMap.SimpleEntry<>(pass, message); |
| } |
| }; |
| |
| public static final TestCase testCtorByValue = new TestCase() { |
| @Override |
| public Map.Entry<Boolean, String> runTest() { |
| Boolean pass = Boolean.FALSE; |
| String message = null; |
| try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { |
| |
| // Try giving a null value for the nonce |
| try { |
| Extension nullNonce = new OCSPNonceExtension(null); |
| throw new RuntimeException("Accepted a null nonce"); |
| } catch (NullPointerException npe) { } |
| |
| // How about a zero-length byte array? |
| try { |
| Extension zeroLenNonce = |
| new OCSPNonceExtension(new byte[0]); |
| throw new RuntimeException("Accepted a zero length nonce"); |
| } catch (IllegalArgumentException iae) { } |
| |
| OCSPNonceExtension nonceByValue = |
| new OCSPNonceExtension(DEADBEEF_16); |
| |
| // Verify overall encoded extension structure |
| nonceByValue.encode(baos); |
| verifyExtStructure(baos.toByteArray()); |
| |
| // Verify the name, elements, and data conform to |
| // expected values for this specific object. |
| boolean crit = nonceByValue.isCritical(); |
| String oid = nonceByValue.getId(); |
| byte[] nonceData = nonceByValue.getNonceValue(); |
| |
| if (crit) { |
| message = "Extension incorrectly marked critical"; |
| } else if (!oid.equals(OCSP_NONCE_OID)) { |
| message = "Incorrect OID (Got " + oid + ", Expected " + |
| OCSP_NONCE_OID + ")"; |
| } else if (!Arrays.equals(nonceData, DEADBEEF_16)) { |
| message = "Returned nonce value did not match input"; |
| } else { |
| pass = Boolean.TRUE; |
| } |
| } catch (Exception e) { |
| e.printStackTrace(System.out); |
| message = e.getClass().getName(); |
| } |
| |
| return new AbstractMap.SimpleEntry<>(pass, message); |
| } |
| }; |
| |
| public static final TestCase testCtorCritForms = new TestCase() { |
| @Override |
| public Map.Entry<Boolean, String> runTest() { |
| Boolean pass = Boolean.FALSE; |
| String message = null; |
| try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { |
| Extension nonceByLength = new OCSPNonceExtension(true, 32); |
| Extension nonceByValue = |
| new OCSPNonceExtension(true, DEADBEEF_16); |
| pass = nonceByLength.isCritical() && nonceByValue.isCritical(); |
| if (!pass) { |
| message = "nonceByLength or nonceByValue was not marked " + |
| "critical as expected"; |
| } |
| } catch (Exception e) { |
| e.printStackTrace(System.out); |
| message = e.getClass().getName(); |
| } |
| |
| return new AbstractMap.SimpleEntry<>(pass, message); |
| } |
| }; |
| |
| |
| public static final TestCase testCtorSuperByDerValue = new TestCase() { |
| @Override |
| public Map.Entry<Boolean, String> runTest() { |
| Boolean pass = Boolean.FALSE; |
| String message = null; |
| try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { |
| Extension nonceByDer = new sun.security.x509.Extension( |
| new DerValue(OCSP_NONCE_DER)); |
| |
| // Verify overall encoded extension structure |
| nonceByDer.encode(baos); |
| verifyExtStructure(baos.toByteArray()); |
| |
| // Verify the name, elements, and data conform to |
| // expected values for this specific object. |
| boolean crit = nonceByDer.isCritical(); |
| String oid = nonceByDer.getId(); |
| DerValue nonceData = new DerValue(nonceByDer.getValue()); |
| |
| if (!crit) { |
| message = "Extension lacks expected criticality setting"; |
| } else if (!oid.equals(OCSP_NONCE_OID)) { |
| message = "Incorrect OID (Got " + oid + ", Expected " + |
| OCSP_NONCE_OID + ")"; |
| } else if (nonceData.getTag() != DerValue.tag_OctetString) { |
| message = "Incorrect nonce data tag type (Got " + |
| String.format("0x%02X", nonceData.getTag()) + |
| ", Expected 0x04)"; |
| } else if (nonceData.getOctetString().length != 48) { |
| message = "Incorrect nonce byte length (Got " + |
| nonceData.getOctetString().length + |
| ", Expected 48)"; |
| } else { |
| pass = Boolean.TRUE; |
| } |
| } catch (Exception e) { |
| e.printStackTrace(System.out); |
| message = e.getClass().getName(); |
| } |
| |
| return new AbstractMap.SimpleEntry<>(pass, message); |
| } |
| }; |
| |
| public static final TestCase testGetName = new TestCase() { |
| @Override |
| public Map.Entry<Boolean, String> runTest() { |
| Boolean pass = Boolean.FALSE; |
| String message = null; |
| try { |
| OCSPNonceExtension nonceByLen = new OCSPNonceExtension(32); |
| pass = new Boolean(nonceByLen.getName().equals(EXT_NAME)); |
| } catch (Exception e) { |
| e.printStackTrace(System.out); |
| message = e.getClass().getName(); |
| } |
| |
| return new AbstractMap.SimpleEntry<>(pass, message); |
| } |
| }; |
| } |