/*
 * 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.cert.Extension;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import javax.net.ssl.SSLException;
import sun.security.util.DerValue;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.provider.certpath.ResponderId;

/*
 * RFC6066 defines the TLS extension,"status_request" (type 0x5),
 * which allows the client to request that the server perform OCSP
 * on the client's behalf.
 *
 * The RFC defines an OCSPStatusRequest structure:
 *
 *      struct {
 *          ResponderID responder_id_list<0..2^16-1>;
 *          Extensions  request_extensions;
 *      } OCSPStatusRequest;
 */
final class OCSPStatusRequest implements StatusRequest {

    private final List<ResponderId> responderIds;
    private final List<Extension> extensions;
    private int encodedLen;
    private int ridListLen;
    private int extListLen;

    
Construct a default OCSPStatusRequest object with empty responder ID and code extension list fields.
/** * Construct a default {@code OCSPStatusRequest} object with empty * responder ID and code extension list fields. */
OCSPStatusRequest() { responderIds = new ArrayList<>(); extensions = new ArrayList<>(); encodedLen = this.length(); }
Construct an OCSPStatusRequest object using the provided ResponderId and Extension lists.
Params:
  • respIds – the list of ResponderId objects to be placed into the OCSPStatusRequest. If the user wishes to place no ResponderId objects in the request, either an empty List or null is acceptable.
  • exts – the list of Extension objects to be placed into the OCSPStatusRequest If the user wishes to place no Extension objects in the request, either an empty List or null is acceptable.
/** * Construct an {@code OCSPStatusRequest} object using the provided * {@code ResponderId} and {@code Extension} lists. * * @param respIds the list of {@code ResponderId} objects to be placed * into the {@code OCSPStatusRequest}. If the user wishes to place * no {@code ResponderId} objects in the request, either an empty * {@code List} or {@code null} is acceptable. * @param exts the list of {@code Extension} objects to be placed into * the {@code OCSPStatusRequest} If the user wishes to place * no {@code Extension} objects in the request, either an empty * {@code List} or {@code null} is acceptable. */
OCSPStatusRequest(List<ResponderId> respIds, List<Extension> exts) { responderIds = new ArrayList<>(respIds != null ? respIds : Collections.emptyList()); extensions = new ArrayList<>(exts != null ? exts : Collections.emptyList()); encodedLen = this.length(); }
Construct an OCSPStatusRequest object from data read from a HandshakeInputStream
Params:
  • s – the HandshakeInputStream providing the encoded data
Throws:
  • IOException – if any decoding errors happen during object construction.
/** * Construct an {@code OCSPStatusRequest} object from data read from * a {@code HandshakeInputStream} * * @param s the {@code HandshakeInputStream} providing the encoded data * * @throws IOException if any decoding errors happen during object * construction. */
OCSPStatusRequest(HandshakeInStream in) throws IOException { responderIds = new ArrayList<>(); extensions = new ArrayList<>(); int ridListBytesRemaining = in.getInt16(); while (ridListBytesRemaining != 0) { byte[] ridBytes = in.getBytes16(); responderIds.add(new ResponderId(ridBytes)); ridListBytesRemaining -= (ridBytes.length + 2); // Make sure that no individual responder ID's length caused an // overrun relative to the outer responder ID list length if (ridListBytesRemaining < 0) { throw new SSLException("Responder ID length overflow: " + "current rid = " + ridBytes.length + ", remaining = " + ridListBytesRemaining); } } int extensionLength = in.getInt16(); if (extensionLength > 0) { byte[] extensionData = new byte[extensionLength]; in.read(extensionData); DerInputStream dis = new DerInputStream(extensionData); DerValue[] extSeqContents = dis.getSequence(extensionData.length); for (DerValue extDerVal : extSeqContents) { extensions.add(new sun.security.x509.Extension(extDerVal)); } } }
Construct an OCSPStatusRequest from its encoded form
Params:
  • requestBytes – the status request extension bytes
Throws:
/** * Construct an {@code OCSPStatusRequest} from its encoded form * * @param requestBytes the status request extension bytes * * @throws IOException if any error occurs during decoding */
OCSPStatusRequest(byte[] requestBytes) throws IOException { responderIds = new ArrayList<>(); extensions = new ArrayList<>(); ByteBuffer reqBuf = ByteBuffer.wrap(requestBytes); // Get the ResponderId list length encodedLen = requestBytes.length; ridListLen = Short.toUnsignedInt(reqBuf.getShort()); int endOfRidList = reqBuf.position() + ridListLen; // The end position of the ResponderId list in the ByteBuffer // should be at least 2 less than the end of the buffer. This // 2 byte defecit is the minimum length required to encode a // zero-length extensions segment. if (reqBuf.limit() - endOfRidList < 2) { throw new SSLException ("ResponderId List length exceeds provided buffer - Len: " + ridListLen + ", Buffer: " + reqBuf.remaining()); } while (reqBuf.position() < endOfRidList) { int ridLength = Short.toUnsignedInt(reqBuf.getShort()); // Make sure an individual ResponderId length doesn't // run past the end of the ResponderId list portion of the // provided buffer. if (reqBuf.position() + ridLength > endOfRidList) { throw new SSLException ("ResponderId length exceeds list length - Off: " + reqBuf.position() + ", Length: " + ridLength + ", End offset: " + endOfRidList); } // Consume/add the ResponderId if (ridLength > 0) { byte[] ridData = new byte[ridLength]; reqBuf.get(ridData); responderIds.add(new ResponderId(ridData)); } } // Get the Extensions length int extensionsLen = Short.toUnsignedInt(reqBuf.getShort()); // The end of the extensions should also be the end of the // encoded OCSPStatusRequest if (extensionsLen != reqBuf.remaining()) { throw new SSLException("Incorrect extensions length: Read " + extensionsLen + ", Data length: " + reqBuf.remaining()); } // Extensions are a SEQUENCE of Extension if (extensionsLen > 0) { byte[] extensionData = new byte[extensionsLen]; reqBuf.get(extensionData); DerInputStream dis = new DerInputStream(extensionData); DerValue[] extSeqContents = dis.getSequence(extensionData.length); for (DerValue extDerVal : extSeqContents) { extensions.add(new sun.security.x509.Extension(extDerVal)); } } }
Obtain the length of the OCSPStatusRequest object in its encoded form
Returns:the length of the OCSPStatusRequest object in its encoded form
/** * Obtain the length of the {@code OCSPStatusRequest} object in its * encoded form * * @return the length of the {@code OCSPStatusRequest} object in its * encoded form */
@Override public int length() { // If we've previously calculated encodedLen simply return it if (encodedLen != 0) { return encodedLen; } ridListLen = 0; for (ResponderId rid : responderIds) { ridListLen += rid.length() + 2; } extListLen = 0; if (!extensions.isEmpty()) { try { DerOutputStream extSequence = new DerOutputStream(); DerOutputStream extEncoding = new DerOutputStream(); for (Extension ext : extensions) { ext.encode(extEncoding); } extSequence.write(DerValue.tag_Sequence, extEncoding); extListLen = extSequence.size(); } catch (IOException ioe) { // Not sure what to do here } } // Total length is the responder ID list length and extensions length // plus each lists' 2-byte length fields. encodedLen = ridListLen + extListLen + 4; return encodedLen; }
Send the encoded OCSPStatusRequest out through the provided HandshakeOutputStream
Params:
  • s – the HandshakeOutputStream on which to send the encoded data
Throws:
/** * Send the encoded {@code OCSPStatusRequest} out through the provided * {@code HandshakeOutputStream} * * @param s the {@code HandshakeOutputStream} on which to send the encoded * data * * @throws IOException if any encoding errors occur */
@Override public void send(HandshakeOutStream s) throws IOException { s.putInt16(ridListLen); for (ResponderId rid : responderIds) { s.putBytes16(rid.getEncoded()); } DerOutputStream seqOut = new DerOutputStream(); DerOutputStream extBytes = new DerOutputStream(); if (extensions.size() > 0) { for (Extension ext : extensions) { ext.encode(extBytes); } seqOut.write(DerValue.tag_Sequence, extBytes); } s.putBytes16(seqOut.toByteArray()); }
Determine if a provided OCSPStatusRequest objects is equal to this one.
Params:
  • obj – an OCSPStatusRequest object to be compared against
Returns:true if the objects are equal, false otherwise. Equivalence is established if the lists of responder IDs and extensions between the two objects are also equal.
/** * Determine if a provided {@code OCSPStatusRequest} objects is equal to * this one. * * @param obj an {@code OCSPStatusRequest} object to be compared against * * @return {@code true} if the objects are equal, {@code false} otherwise. * Equivalence is established if the lists of responder IDs and * extensions between the two objects are also equal. */
@Override public boolean equals(Object obj) { if (obj == null) { return false; } else if (this == obj) { return true; } else if (obj instanceof OCSPStatusRequest) { OCSPStatusRequest respObj = (OCSPStatusRequest)obj; return responderIds.equals(respObj.getResponderIds()) && extensions.equals(respObj.getExtensions()); } return false; }
Returns the hash code value for this OCSPStatusRequest
Returns:the hash code value for this OCSPStatusRequest
/** * Returns the hash code value for this {@code OCSPStatusRequest} * * @return the hash code value for this {@code OCSPStatusRequest} */
@Override public int hashCode() { int result = 17; result = 31 * result + responderIds.hashCode(); result = 31 * result + extensions.hashCode(); return result; }
Create a string representation of this OCSPStatusRequest
Returns:a string representation of this OCSPStatusRequest
/** * Create a string representation of this {@code OCSPStatusRequest} * * @return a string representation of this {@code OCSPStatusRequest} */
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("OCSPStatusRequest\n"); sb.append(" ResponderIds:"); if (responderIds.isEmpty()) { sb.append(" <EMPTY>"); } else { for (ResponderId rid : responderIds) { sb.append("\n ").append(rid.toString()); } } sb.append("\n").append(" Extensions:"); if (extensions.isEmpty()) { sb.append(" <EMPTY>"); } else { for (Extension ext : extensions) { sb.append("\n ").append(ext.toString()); } } return sb.toString(); }
Get the list of ResponderId objects for this OCSPStatusRequest
Returns:an unmodifiable List of ResponderId objects
/** * Get the list of {@code ResponderId} objects for this * {@code OCSPStatusRequest} * * @return an unmodifiable {@code List} of {@code ResponderId} objects */
List<ResponderId> getResponderIds() { return Collections.unmodifiableList(responderIds); }
Get the list of Extension objects for this OCSPStatusRequest
Returns:an unmodifiable List of Extension objects
/** * Get the list of {@code Extension} objects for this * {@code OCSPStatusRequest} * * @return an unmodifiable {@code List} of {@code Extension} objects */
List<Extension> getExtensions() { return Collections.unmodifiableList(extensions); } }