1 /*
   2  * Copyright (c) 2015, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.provider.certpath;
  27 
  28 import java.util.Arrays;
  29 import java.io.IOException;
  30 import java.security.PublicKey;
  31 import javax.security.auth.x500.X500Principal;
  32 import sun.security.x509.KeyIdentifier;
  33 import sun.security.util.DerValue;
  34 
  35 /**
  36  * Class for ResponderId entities as described in RFC6960.  ResponderId objects
  37  * are used to uniquely identify OCSP responders.
  38  * <p>
  39  * The RFC 6960 defines a ResponderID structure as:
  40  * <pre>
  41  * ResponderID ::= CHOICE {
  42  *      byName              [1] Name,
  43  *      byKey               [2] KeyHash }
  44  *
  45  * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
  46  * (excluding the tag and length fields)
  47  *
  48  * Name is defined in RFC 5280.
  49  * </pre>
  50  *
  51  * @see ResponderId.Type
  52  * @since 9
  53  */
  54 public final class ResponderId {
  55 
  56     /**
  57      * A {@code ResponderId} enumeration describing the accepted forms for a
  58      * {@code ResponderId}.
  59      *
  60      * @see ResponderId
  61      * @since 9
  62      */
  63     public static enum Type {
  64         /**
  65          * A BY_NAME {@code ResponderId} will be built from a subject name,
  66          * either as an {@code X500Principal} or a DER-encoded byte array.
  67          */
  68         BY_NAME(1, "byName"),
  69 
  70         /**
  71          * A BY_KEY {@code ResponderId} will be built from a public key
  72          * identifier, either derived from a {@code PublicKey} or directly
  73          * from a DER-encoded byte array containing the key identifier.
  74          */
  75         BY_KEY(2, "byKey");
  76 
  77         private final int tagNumber;
  78         private final String ridTypeName;
  79 
  80         private Type(int value, String name) {
  81             this.tagNumber = value;
  82             this.ridTypeName = name;
  83         }
  84 
  85         public int value() {
  86             return tagNumber;
  87         }
  88 
  89         @Override
  90         public String toString() {
  91             return ridTypeName;
  92         }
  93     }
  94 
  95     private Type type;
  96     private X500Principal responderName;
  97     private KeyIdentifier responderKeyId;
  98     private byte[] encodedRid;
  99 
 100     /**
 101      * Constructs a {@code ResponderId} object using an {@code X500Principal}.
 102      * When encoded in DER this object will use the BY_NAME option.
 103      *
 104      * @param subjectName the subject name of the certificate used
 105      * to sign OCSP responses.
 106      *
 107      * @throws IOException if the internal DER-encoding of the
 108      *      {@code X500Principal} fails.
 109      */
 110     public ResponderId(X500Principal subjectName) throws IOException {
 111         responderName = subjectName;
 112         responderKeyId = null;
 113         encodedRid = principalToBytes();
 114         type = Type.BY_NAME;
 115     }
 116 
 117     /**
 118      * Constructs a {@code ResponderId} object using a {@code PublicKey}.
 119      * When encoded in DER this object will use the byKey option, a
 120      * SHA-1 hash of the responder's public key.
 121      *
 122      * @param pubKey the the OCSP responder's public key
 123      *
 124      * @throws IOException if the internal DER-encoding of the
 125      *      {@code KeyIdentifier} fails.
 126      */
 127     public ResponderId(PublicKey pubKey) throws IOException {
 128         responderKeyId = new KeyIdentifier(pubKey);
 129         responderName = null;
 130         encodedRid = keyIdToBytes();
 131         type = Type.BY_KEY;
 132     }
 133 
 134     /**
 135      * Constructs a {@code ResponderId} object from its DER-encoding.
 136      *
 137      * @param encodedData the DER-encoded bytes
 138      *
 139      * @throws IOException if the encodedData is not properly DER encoded
 140      */
 141     public ResponderId(byte[] encodedData) throws IOException {
 142         DerValue outer = new DerValue(encodedData);
 143 
 144         if (outer.isContextSpecific((byte)Type.BY_NAME.value())
 145                 && outer.isConstructed()) {
 146             // Use the X500Principal constructor as a way to sanity
 147             // check the incoming data.
 148             responderName = new X500Principal(outer.getDataBytes());
 149             encodedRid = principalToBytes();
 150             type = Type.BY_NAME;
 151         } else if (outer.isContextSpecific((byte)Type.BY_KEY.value())
 152                 && outer.isConstructed()) {
 153             // Use the KeyIdentifier constructor as a way to sanity
 154             // check the incoming data.
 155             responderKeyId =
 156                 new KeyIdentifier(new DerValue(outer.getDataBytes()));
 157             encodedRid = keyIdToBytes();
 158             type = Type.BY_KEY;
 159         } else {
 160             throw new IOException("Invalid ResponderId content");
 161         }
 162     }
 163 
 164     /**
 165      * Encode a {@code ResponderId} in DER form
 166      *
 167      * @return a byte array containing the DER-encoded representation for this
 168      *      {@code ResponderId}
 169      */
 170     public byte[] getEncoded() {
 171         return encodedRid.clone();
 172     }
 173 
 174     /**
 175      * Return the type of {@ResponderId}
 176      *
 177      * @return a number corresponding to the context-specific tag number
 178      *      used in the DER-encoding for a {@code ResponderId}
 179      */
 180     public ResponderId.Type getType() {
 181         return type;
 182     }
 183 
 184     /**
 185      * Get the length of the encoded {@code ResponderId} (including the tag and
 186      * length of the explicit tagging from the outer ASN.1 CHOICE).
 187      *
 188      * @return the length of the encoded {@code ResponderId}
 189      */
 190     public int length() {
 191         return encodedRid.length;
 192     }
 193 
 194     /**
 195      * Obtain the underlying {@code X500Principal} from a {@code ResponderId}
 196      *
 197      * @return the {@code X500Principal} for this {@code ResponderId} if it
 198      *      is a BY_NAME variant.  If the {@code ResponderId} is a BY_KEY
 199      *      variant, this routine will return {@code null}.
 200      */
 201     public X500Principal getResponderName() {
 202         return responderName;
 203     }
 204 
 205     /**
 206      * Obtain the underlying key identifier from a {@code ResponderId}
 207      *
 208      * @return the {@code KeyIdentifier} for this {@code ResponderId} if it
 209      *      is a BY_KEY variant.  If the {@code ResponderId} is a BY_NAME
 210      *      variant, this routine will return {@code null}.
 211      */
 212     public KeyIdentifier getKeyIdentifier() {
 213         return responderKeyId;
 214     }
 215 
 216     /**
 217      * Compares the specified object with this {@code ResponderId} for equality.
 218      * A ResponderId will only be considered equivalent if both the type and
 219      * data value are equal.  Two ResponderIds initialized by name and
 220      * key ID, respectively, will not be equal even if the
 221      * ResponderId objects are created from the same source certificate.
 222      *
 223      * @param obj the object to be compared against
 224      *
 225      * @return true if the specified object is equal to this {@code Responderid}
 226      */
 227     @Override
 228     public boolean equals(Object obj) {
 229         if (obj == null) {
 230             return false;
 231         }
 232 
 233         if (this == obj) {
 234             return true;
 235         }
 236 
 237         if (obj instanceof ResponderId) {
 238             ResponderId respObj = (ResponderId)obj;
 239                 return Arrays.equals(encodedRid, respObj.getEncoded());
 240         }
 241 
 242         return false;
 243     }
 244 
 245     /**
 246      * Returns the hash code value for this {@code ResponderId}
 247      *
 248      * @return the hash code value for this {@code ResponderId}
 249      */
 250     @Override
 251     public int hashCode() {
 252         return Arrays.hashCode(encodedRid);
 253     }
 254 
 255     /**
 256      * Create a String representation of this {@code ResponderId}
 257      *
 258      * @return a String representation of this {@code ResponderId}
 259      */
 260     @Override
 261     public String toString() {
 262         StringBuilder sb = new StringBuilder();
 263         switch (type) {
 264             case BY_NAME:
 265                 sb.append(type).append(": ").append(responderName);
 266                 break;
 267             case BY_KEY:
 268                 sb.append(type).append(": ");
 269                 for (byte keyIdByte : responderKeyId.getIdentifier()) {
 270                     sb.append(String.format("%02X", keyIdByte));
 271                 }
 272                 break;
 273             default:
 274                 sb.append("Unknown ResponderId Type: ").append(type);
 275         }
 276         return sb.toString();
 277     }
 278 
 279     /**
 280      * Convert the responderName data member into its DER-encoded form
 281      *
 282      * @return the DER encoding for a responder ID byName option, including
 283      *      explicit context-specific tagging.
 284      *
 285      * @throws IOException if any encoding error occurs
 286      */
 287     private byte[] principalToBytes() throws IOException {
 288         DerValue dv = new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
 289                 true, (byte)Type.BY_NAME.value()),
 290                 responderName.getEncoded());
 291         return dv.toByteArray();
 292     }
 293 
 294     /**
 295      * Convert the responderKeyId data member into its DER-encoded form
 296      *
 297      * @return the DER encoding for a responder ID byKey option, including
 298      *      explicit context-specific tagging.
 299      *
 300      * @throws IOException if any encoding error occurs
 301      */
 302     private byte[] keyIdToBytes() throws IOException {
 303         // Place the KeyIdentifier bytes into an OCTET STRING
 304         DerValue inner = new DerValue(DerValue.tag_OctetString,
 305                 responderKeyId.getIdentifier());
 306 
 307         // Mark the OCTET STRING-wrapped KeyIdentifier bytes
 308         // as EXPLICIT CONTEXT 2
 309         DerValue outer = new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,
 310                 true, (byte)Type.BY_KEY.value()), inner.toByteArray());
 311 
 312         return outer.toByteArray();
 313     }
 314 
 315 }