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.security.cert.*;
  27 import java.util.*;
  28 import java.nio.ByteBuffer;
  29 import javax.net.ssl.SSLException;
  30 import javax.security.auth.x500.X500Principal;
  31 import sun.security.provider.certpath.ResponderId;
  32 import sun.security.provider.certpath.OCSPNonceExtension;
  33 
  34 /*
  35  * Checks that the hash value for a certificate's issuer name is generated
  36  * correctly. Requires any certificate that is not self-signed.
  37  *
  38  * NOTE: this test uses Sun private classes which are subject to change.
  39  */
  40 public class CertStatusReqItemV2Tests {
  41 
  42     private static final boolean debug = false;
  43 
  44     private static final byte[] DEF_CSRIV2_OCSP_MULTI_BYTES = {
  45            2,    0,    4,    0,    0,    0,    0
  46     };
  47 
  48     private static final byte[] DEF_CSRIV2_OCSP_BYTES = {
  49            1,    0,    4,    0,    0,    0,    0
  50     };
  51 
  52     // This is a CSRIV2 (ocsp_multi) that has a single
  53     // responder ID and no extensions.
  54     private static final byte[] CSRIV2_1RID = {
  55             2,    0,   32,     0,   28,    0,   26,  -95,
  56            24,   48,   22,    49,   20,   48,   18,    6,
  57             3,   85,    4,     3,   19,   11,   79,   67,
  58            83,   80,   32,    83,  105,  103,  110,  101,
  59           114,    0 ,   0
  60     };
  61 
  62     // This is a CSRIV2 (ocsp_multi) that has a single
  63     // responder ID and no extensions.  The request_length
  64     // field is too short in this case.
  65     private static final byte[] CSRIV2_LENGTH_TOO_SHORT = {
  66             2,    0,   27,     0,   28,    0,   26,  -95,
  67            24,   48,   22,    49,   20,   48,   18,    6,
  68             3,   85,    4,     3,   19,   11,   79,   67,
  69            83,   80,   32,    83,  105,  103,  110,  101,
  70           114,    0 ,   0
  71     };
  72 
  73     // This is a CSRIV2 (ocsp_multi) that has a single
  74     // responder ID and no extensions.  The request_length
  75     // field is too long in this case.
  76     private static final byte[] CSRIV2_LENGTH_TOO_LONG = {
  77             2,    0,   54,     0,   28,    0,   26,  -95,
  78            24,   48,   22,    49,   20,   48,   18,    6,
  79             3,   85,    4,     3,   19,   11,   79,   67,
  80            83,   80,   32,    83,  105,  103,  110,  101,
  81           114,    0 ,   0
  82     };
  83 
  84     // A CSRIV2 (ocsp) with one Responder ID (byName: CN=OCSP Signer)
  85     // and a nonce extension (32 bytes).
  86     private static final byte[] CSRIV2_OCSP_1RID_1EXT = {
  87             1,    0,   83,    0,   28,    0,   26,  -95,
  88            24,   48,   22,   49,   20,   48,   18,    6,
  89             3,   85,    4,    3,   19,   11,   79,   67,
  90            83,   80,   32,   83,  105,  103,  110,  101,
  91           114,    0,   51,   48,   49,   48,   47,    6,
  92             9,   43,    6,    1,    5,    5,    7,   48,
  93             1,    2,    4,   34,    4,   32,  -34,  -83,
  94           -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
  95           -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
  96           -66,  -17,  -34,  -83,  -66,  -17,  -34,  -83,
  97           -66,  -17,  -34,  -83,  -66,  -17
  98     };
  99 
 100     public static void main(String[] args) throws Exception {
 101         Map<String, TestCase> testList =
 102                 new LinkedHashMap<String, TestCase>() {{
 103             put("CTOR (Default)", testCtorTypeStatReq);
 104             put("CTOR (Byte array)", testCtorByteArray);
 105             put("CTOR (invalid lengths)", testCtorInvalidLengths);
 106         }};
 107 
 108         TestUtils.runTests(testList);
 109     }
 110 
 111     public static final TestCase testCtorTypeStatReq = new TestCase() {
 112         @Override
 113         public Map.Entry<Boolean, String> runTest() {
 114             Boolean pass = Boolean.FALSE;
 115             String message = null;
 116             try {
 117                 // Attempt to create CSRIv2 objects using null pointers
 118                 // for either parameter.  In either case NPE should be thrown
 119                 CertStatusReqItemV2 csriNull;
 120                 try {
 121                     csriNull = new CertStatusReqItemV2(null,
 122                             new OCSPStatusRequest());
 123                     throw new RuntimeException("Did not catch expected NPE " +
 124                             "for null status_type parameter");
 125                 } catch (NullPointerException npe) { }
 126 
 127                 try {
 128                     csriNull = new CertStatusReqItemV2(StatusRequestType.OCSP,
 129                             null);
 130                     throw new RuntimeException("Did not catch expected NPE " +
 131                             "for null StatusRequest parameter");
 132                 } catch (NullPointerException npe) { }
 133 
 134                 // Create an "ocsp_multi" type request using a default
 135                 // (no Responder IDs, no Extensions) OCSPStatusRequest
 136                 CertStatusReqItemV2 csriMulti =
 137                         new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI,
 138                                 new OCSPStatusRequest());
 139                 HandshakeOutStream hsout = new HandshakeOutStream(null);
 140                 csriMulti.send(hsout);
 141                 TestUtils.valueCheck(DEF_CSRIV2_OCSP_MULTI_BYTES,
 142                         hsout.toByteArray());
 143                 hsout.reset();
 144 
 145                 // Create an "ocsp" type request using a default
 146                 // (no Responder IDs, no Extensions) OCSPStatusRequest
 147                 CertStatusReqItemV2 csriSingle =
 148                         new CertStatusReqItemV2(StatusRequestType.OCSP,
 149                                 new OCSPStatusRequest(new LinkedList<>(),
 150                                         new LinkedList<>()));
 151                 csriSingle.send(hsout);
 152                 TestUtils.valueCheck(DEF_CSRIV2_OCSP_BYTES,
 153                         hsout.toByteArray());
 154 
 155                 // Create the CertStatusRequestItemV2 with a user-defined
 156                 // StatusRequestType value
 157                 CertStatusReqItemV2 csriNine =
 158                         new CertStatusReqItemV2(StatusRequestType.get(9),
 159                                 new OCSPStatusRequest(null, null));
 160                 if (csriNine.getType().id != 9) {
 161                     throw new RuntimeException("Expected status_type = 9, " +
 162                             "got " + csriNine.getType().id);
 163                 } else {
 164                     StatusRequest sr = csriNine.getRequest();
 165                     if (!(sr instanceof OCSPStatusRequest)) {
 166                         throw new RuntimeException("Expected " +
 167                                 "OCSPStatusRequest, got " +
 168                                 sr.getClass().getName());
 169                     }
 170                 }
 171 
 172                 // Create the CertStatusRequestItemV2 with a StatusRequest
 173                 // that does not match the status_type argument.
 174                 // We expect IllegalArgumentException in this case.
 175                 try {
 176                     CertStatusReqItemV2 csriBadSR = new CertStatusReqItemV2(
 177                             StatusRequestType.OCSP_MULTI,
 178                             new BogusStatusRequest());
 179                     throw new RuntimeException("Constructor accepted a " +
 180                             "StatusRequest that is inconsistent with " +
 181                             "the status_type");
 182                 } catch (IllegalArgumentException iae) {
 183                     // The expected result...nothing to do here
 184                 }
 185 
 186                 pass = Boolean.TRUE;
 187             } catch (Exception e) {
 188                 e.printStackTrace(System.out);
 189                 message = e.getClass().getName();
 190             }
 191 
 192             return new AbstractMap.SimpleEntry<>(pass, message);
 193         }
 194     };
 195 
 196     // Test the constructor form that takes the data from a byte array
 197     public static final TestCase testCtorByteArray = new TestCase() {
 198         @Override
 199         public Map.Entry<Boolean, String> runTest() {
 200             Boolean pass = Boolean.FALSE;
 201             String message = null;
 202             try {
 203                 StatusRequestType sType;
 204                 StatusRequest sReq;
 205                 ResponderId checkRid =
 206                         new ResponderId(new X500Principal("CN=OCSP Signer"));
 207                 Extension checkExt = new OCSPNonceExtension(32);
 208 
 209                 CertStatusReqItemV2 csriv =
 210                         new CertStatusReqItemV2(CSRIV2_OCSP_1RID_1EXT);
 211                 sType = csriv.getType();
 212                 if (sType != StatusRequestType.OCSP) {
 213                     throw new RuntimeException("Unexpected StatusRequestType " +
 214                             sType.getClass().getName());
 215                 }
 216 
 217                 sReq = csriv.getRequest();
 218                 if (sReq instanceof OCSPStatusRequest) {
 219                     OCSPStatusRequest osr = (OCSPStatusRequest)sReq;
 220                     List<ResponderId> ridList = osr.getResponderIds();
 221                     List<Extension> extList = osr.getExtensions();
 222 
 223                     if (ridList.size() != 1 || !ridList.contains(checkRid)) {
 224                         throw new RuntimeException("Responder list mismatch");
 225                     } else if (extList.size() !=  1 ||
 226                             !extList.get(0).getId().equals(checkExt.getId())) {
 227                         throw new RuntimeException("Extension list mismatch");
 228                     }
 229                 } else {
 230                     throw new RuntimeException("Expected OCSPStatusRequest " +
 231                             "from decoded bytes, got " +
 232                             sReq.getClass().getName());
 233                 }
 234 
 235                 // Create a CSRIV2 out of random data.  A non-OCSP/OCSP_MULTI
 236                 // type will be forcibly set and the outer length field will
 237                 // be correct.
 238                 // The constructor should create a StatusRequestType object
 239                 // and an UnknownStatusRequest object consisting of the
 240                 // data segment.
 241                 byte[] junkData = new byte[48];
 242                 Random r = new Random(System.currentTimeMillis());
 243                 r.nextBytes(junkData);
 244                 junkData[0] = 7;        // status_type = 7
 245                 junkData[1] = 0;
 246                 junkData[2] = 45;       // request_length = 45
 247                 csriv = new CertStatusReqItemV2(junkData);
 248 
 249                 sType = csriv.getType();
 250                 sReq = csriv.getRequest();
 251                 if (sType.id != junkData[0]) {
 252                     throw new RuntimeException("StatusRequestType mismatch: " +
 253                             "expected 7, got " + sType.id);
 254                 }
 255                 if (sReq instanceof UnknownStatusRequest) {
 256                     // Verify the underlying StatusRequest bytes have been
 257                     // preserved correctly.
 258                     HandshakeOutStream hsout = new HandshakeOutStream(null);
 259                     sReq.send(hsout);
 260                     byte[] srDataOut = hsout.toByteArray();
 261                     TestUtils.valueCheck(srDataOut, junkData, 0, 3,
 262                             srDataOut.length);
 263                 } else {
 264                     throw new RuntimeException("StatusRequest mismatch: " +
 265                             "expected UnknownStatusRequest, got " +
 266                             sReq.getClass().getName());
 267                 }
 268 
 269                 // Test the parsing of the default OCSP/OCSP_MULTI extensions
 270                 // and make sure the underlying StatusRequestType and
 271                 // StatusRequest objects are correct.
 272                 csriv = new CertStatusReqItemV2(DEF_CSRIV2_OCSP_MULTI_BYTES);
 273                 sType = csriv.getType();
 274                 sReq = csriv.getRequest();
 275                 if (sType != StatusRequestType.OCSP_MULTI) {
 276                     throw new RuntimeException("StatusRequestType mismatch: " +
 277                             "expected OCSP_MULTI (2), got " + sType.id);
 278                 }
 279                 if (!(sReq instanceof OCSPStatusRequest)) {
 280                     throw new RuntimeException("StatusRequest mismatch: " +
 281                             "expected OCSPStatusRequest, got " +
 282                             sReq.getClass().getName());
 283                 }
 284 
 285                 csriv = new CertStatusReqItemV2(DEF_CSRIV2_OCSP_BYTES);
 286                 sType = csriv.getType();
 287                 sReq = csriv.getRequest();
 288                 if (sType != StatusRequestType.OCSP) {
 289                     throw new RuntimeException("StatusRequestType mismatch: " +
 290                             "expected OCSP (1), got " + sType.id);
 291                 }
 292                 if (!(sReq instanceof OCSPStatusRequest)) {
 293                     throw new RuntimeException("StatusRequest mismatch: " +
 294                             "expected OCSPStatusRequest, got " +
 295                             sReq.getClass().getName());
 296                 }
 297 
 298                 pass = Boolean.TRUE;
 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     public static final TestCase testCtorInvalidLengths = new TestCase() {
 309         @Override
 310         public Map.Entry<Boolean, String> runTest() {
 311             Boolean pass = Boolean.FALSE;
 312             String message = null;
 313             try {
 314                 try {
 315                     CertStatusReqItemV2 csriTooShort =
 316                             new CertStatusReqItemV2(CSRIV2_LENGTH_TOO_SHORT);
 317                     throw new RuntimeException("Expected exception not thrown");
 318                 } catch (SSLException ssle) { }
 319 
 320                 try {
 321                     CertStatusReqItemV2 csriTooLong =
 322                             new CertStatusReqItemV2(CSRIV2_LENGTH_TOO_LONG);
 323                     throw new RuntimeException("Expected exception not thrown");
 324                 } catch (SSLException ssle) { }
 325 
 326                 pass = Boolean.TRUE;
 327             } catch (Exception e) {
 328                 e.printStackTrace(System.out);
 329                 message = e.getClass().getName();
 330             }
 331 
 332             return new AbstractMap.SimpleEntry<>(pass, message);
 333         }
 334     };
 335 
 336     // Test the constructor form that takes the data from HandshakeInputStream
 337     public static final TestCase testCtorInputStream = new TestCase() {
 338         @Override
 339         public Map.Entry<Boolean, String> runTest() {
 340             Boolean pass = Boolean.FALSE;
 341             String message = null;
 342             try {
 343                 StatusRequestType sType;
 344                 StatusRequest sReq;
 345                 ResponderId checkRid =
 346                         new ResponderId(new X500Principal("CN=OCSP Signer"));
 347                 Extension checkExt = new OCSPNonceExtension(32);
 348 
 349                 HandshakeInStream hsis = new HandshakeInStream();
 350                 hsis.incomingRecord(ByteBuffer.wrap(CSRIV2_OCSP_1RID_1EXT));
 351                 CertStatusReqItemV2 csriv = new CertStatusReqItemV2(hsis);
 352                 sType = csriv.getType();
 353                 if (sType != StatusRequestType.OCSP) {
 354                     throw new RuntimeException("Unexpected StatusRequestType " +
 355                             sType.getClass().getName());
 356                 }
 357 
 358                 sReq = csriv.getRequest();
 359                 if (sReq instanceof OCSPStatusRequest) {
 360                     OCSPStatusRequest osr = (OCSPStatusRequest)sReq;
 361                     List<ResponderId> ridList = osr.getResponderIds();
 362                     List<Extension> extList = osr.getExtensions();
 363 
 364                     if (ridList.size() != 1 || !ridList.contains(checkRid)) {
 365                         throw new RuntimeException("Responder list mismatch");
 366                     } else if (extList.size() !=  1 ||
 367                             !extList.get(0).getId().equals(checkExt.getId())) {
 368                         throw new RuntimeException("Extension list mismatch");
 369                     }
 370                 } else {
 371                     throw new RuntimeException("Expected OCSPStatusRequest " +
 372                             "from decoded bytes, got " +
 373                             sReq.getClass().getName());
 374                 }
 375 
 376                 // Create a CSRIV2 out of random data.  A non-OCSP/OCSP_MULTI
 377                 // type will be forcibly set and the outer length field will
 378                 // be correct.
 379                 // The constructor should create a StatusRequestType object
 380                 // and an UnknownStatusRequest object consisting of the
 381                 // data segment.
 382                 byte[] junkData = new byte[48];
 383                 Random r = new Random(System.currentTimeMillis());
 384                 r.nextBytes(junkData);
 385                 junkData[0] = 7;        // status_type = 7
 386                 junkData[1] = 0;
 387                 junkData[2] = 45;       // request_length = 45
 388                 hsis = new HandshakeInStream();
 389                 hsis.incomingRecord(ByteBuffer.wrap(junkData));
 390                 csriv = new CertStatusReqItemV2(hsis);
 391 
 392                 sType = csriv.getType();
 393                 sReq = csriv.getRequest();
 394                 if (sType.id != junkData[0]) {
 395                     throw new RuntimeException("StatusRequestType mismatch: " +
 396                             "expected 7, got " + sType.id);
 397                 }
 398                 if (sReq instanceof UnknownStatusRequest) {
 399                     // Verify the underlying StatusRequest bytes have been
 400                     // preserved correctly.
 401                     HandshakeOutStream hsout = new HandshakeOutStream(null);
 402                     sReq.send(hsout);
 403                     byte[] srDataOut = hsout.toByteArray();
 404                     TestUtils.valueCheck(srDataOut, junkData, 0, 3,
 405                             srDataOut.length);
 406                 } else {
 407                     throw new RuntimeException("StatusRequest mismatch: " +
 408                             "expected UnknownStatusRequest, got " +
 409                             sReq.getClass().getName());
 410                 }
 411 
 412                 // Test the parsing of the default OCSP/OCSP_MULTI extensions
 413                 // and make sure the underlying StatusRequestType and
 414                 // StatusRequest objects are correct.
 415                 hsis = new HandshakeInStream();
 416                 hsis.incomingRecord(
 417                         ByteBuffer.wrap(DEF_CSRIV2_OCSP_MULTI_BYTES));
 418                 csriv = new CertStatusReqItemV2(hsis);
 419                 sType = csriv.getType();
 420                 sReq = csriv.getRequest();
 421                 if (sType != StatusRequestType.OCSP_MULTI) {
 422                     throw new RuntimeException("StatusRequestType mismatch: " +
 423                             "expected OCSP_MULTI (2), got " + sType.id);
 424                 }
 425                 if (!(sReq instanceof OCSPStatusRequest)) {
 426                     throw new RuntimeException("StatusRequest mismatch: " +
 427                             "expected OCSPStatusRequest, got " +
 428                             sReq.getClass().getName());
 429                 }
 430 
 431                 hsis = new HandshakeInStream();
 432                 hsis.incomingRecord(ByteBuffer.wrap(DEF_CSRIV2_OCSP_BYTES));
 433                 csriv = new CertStatusReqItemV2(hsis);
 434                 sType = csriv.getType();
 435                 sReq = csriv.getRequest();
 436                 if (sType != StatusRequestType.OCSP) {
 437                     throw new RuntimeException("StatusRequestType mismatch: " +
 438                             "expected OCSP (1), got " + sType.id);
 439                 }
 440                 if (!(sReq instanceof OCSPStatusRequest)) {
 441                     throw new RuntimeException("StatusRequest mismatch: " +
 442                             "expected OCSPStatusRequest, got " +
 443                             sReq.getClass().getName());
 444                 }
 445 
 446                 pass = Boolean.TRUE;
 447             } catch (Exception e) {
 448                 e.printStackTrace(System.out);
 449                 message = e.getClass().getName();
 450             }
 451 
 452             return new AbstractMap.SimpleEntry<>(pass, message);
 453         }
 454     };
 455 }