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 }