/* | 
|
 * Copyright (c) 2015, 2017, 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.provider.certpath;  | 
|
import java.util.Arrays;  | 
|
import java.io.IOException;  | 
|
import java.security.PublicKey;  | 
|
import javax.security.auth.x500.X500Principal;  | 
|
import sun.security.x509.KeyIdentifier;  | 
|
import sun.security.util.DerValue;  | 
|
/** | 
|
 * Class for ResponderId entities as described in RFC6960.  ResponderId objects | 
|
 * are used to uniquely identify OCSP responders. | 
|
 * <p> | 
|
 * The RFC 6960 defines a ResponderID structure as: | 
|
 * <pre> | 
|
 * ResponderID ::= CHOICE { | 
|
 *      byName              [1] Name, | 
|
 *      byKey               [2] KeyHash } | 
|
 * | 
|
 * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key | 
|
 * (excluding the tag and length fields) | 
|
 * | 
|
 * Name is defined in RFC 5280. | 
|
 * </pre> | 
|
 * | 
|
 * @see ResponderId.Type | 
|
 * @since 9 | 
|
*/  | 
|
public final class ResponderId { | 
|
    /** | 
|
     * A {@code ResponderId} enumeration describing the accepted forms for a | 
|
     * {@code ResponderId}. | 
|
     * | 
|
     * @see ResponderId | 
|
     * @since 9 | 
|
*/  | 
|
    public static enum Type { | 
|
        /** | 
|
         * A BY_NAME {@code ResponderId} will be built from a subject name, | 
|
         * either as an {@code X500Principal} or a DER-encoded byte array. | 
|
*/  | 
|
        BY_NAME(1, "byName"), | 
|
        /** | 
|
         * A BY_KEY {@code ResponderId} will be built from a public key | 
|
         * identifier, either derived from a {@code PublicKey} or directly | 
|
         * from a DER-encoded byte array containing the key identifier. | 
|
*/  | 
|
        BY_KEY(2, "byKey"); | 
|
private final int tagNumber;  | 
|
private final String ridTypeName;  | 
|
private Type(int value, String name) {  | 
|
this.tagNumber = value;  | 
|
this.ridTypeName = name;  | 
|
}  | 
|
        public int value() { | 
|
return tagNumber;  | 
|
}  | 
|
@Override  | 
|
public String toString() {  | 
|
return ridTypeName;  | 
|
}  | 
|
}  | 
|
private Type type;  | 
|
private X500Principal responderName;  | 
|
private KeyIdentifier responderKeyId;  | 
|
private byte[] encodedRid;  | 
|
    /** | 
|
     * Constructs a {@code ResponderId} object using an {@code X500Principal}. | 
|
     * When encoded in DER this object will use the BY_NAME option. | 
|
     * | 
|
     * @param subjectName the subject name of the certificate used | 
|
     * to sign OCSP responses. | 
|
     * | 
|
     * @throws IOException if the internal DER-encoding of the | 
|
     *      {@code X500Principal} fails. | 
|
*/  | 
|
public ResponderId(X500Principal subjectName) throws IOException {  | 
|
responderName = subjectName;  | 
|
responderKeyId = null;  | 
|
encodedRid = principalToBytes();  | 
|
type = Type.BY_NAME;  | 
|
}  | 
|
    /** | 
|
     * Constructs a {@code ResponderId} object using a {@code PublicKey}. | 
|
     * When encoded in DER this object will use the byKey option, a | 
|
     * SHA-1 hash of the responder's public key. | 
|
     * | 
|
     * @param pubKey the the OCSP responder's public key | 
|
     * | 
|
     * @throws IOException if the internal DER-encoding of the | 
|
     *      {@code KeyIdentifier} fails. | 
|
*/  | 
|
public ResponderId(PublicKey pubKey) throws IOException {  | 
|
responderKeyId = new KeyIdentifier(pubKey);  | 
|
responderName = null;  | 
|
encodedRid = keyIdToBytes();  | 
|
type = Type.BY_KEY;  | 
|
}  | 
|
    /** | 
|
     * Constructs a {@code ResponderId} object from its DER-encoding. | 
|
     * | 
|
     * @param encodedData the DER-encoded bytes | 
|
     * | 
|
     * @throws IOException if the encodedData is not properly DER encoded | 
|
*/  | 
|
public ResponderId(byte[] encodedData) throws IOException {  | 
|
DerValue outer = new DerValue(encodedData);  | 
|
if (outer.isContextSpecific((byte)Type.BY_NAME.value())  | 
|
&& outer.isConstructed()) {  | 
|
// Use the X500Principal constructor as a way to sanity  | 
|
            // check the incoming data. | 
|
responderName = new X500Principal(outer.getDataBytes());  | 
|
encodedRid = principalToBytes();  | 
|
type = Type.BY_NAME;  | 
|
} else if (outer.isContextSpecific((byte)Type.BY_KEY.value())  | 
|
&& outer.isConstructed()) {  | 
|
// Use the KeyIdentifier constructor as a way to sanity  | 
|
            // check the incoming data. | 
|
responderKeyId =  | 
|
new KeyIdentifier(new DerValue(outer.getDataBytes()));  | 
|
encodedRid = keyIdToBytes();  | 
|
type = Type.BY_KEY;  | 
|
        } else { | 
|
throw new IOException("Invalid ResponderId content");  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Encode a {@code ResponderId} in DER form | 
|
     * | 
|
     * @return a byte array containing the DER-encoded representation for this | 
|
     *      {@code ResponderId} | 
|
*/  | 
|
    public byte[] getEncoded() { | 
|
return encodedRid.clone();  | 
|
}  | 
|
    /** | 
|
     * Return the type of {@ResponderId} | 
|
     * | 
|
     * @return a number corresponding to the context-specific tag number | 
|
     *      used in the DER-encoding for a {@code ResponderId} | 
|
*/  | 
|
public ResponderId.Type getType() {  | 
|
return type;  | 
|
}  | 
|
    /** | 
|
     * Get the length of the encoded {@code ResponderId} (including the tag and | 
|
     * length of the explicit tagging from the outer ASN.1 CHOICE). | 
|
     * | 
|
     * @return the length of the encoded {@code ResponderId} | 
|
*/  | 
|
    public int length() { | 
|
return encodedRid.length;  | 
|
}  | 
|
    /** | 
|
     * Obtain the underlying {@code X500Principal} from a {@code ResponderId} | 
|
     * | 
|
     * @return the {@code X500Principal} for this {@code ResponderId} if it | 
|
     *      is a BY_NAME variant.  If the {@code ResponderId} is a BY_KEY | 
|
     *      variant, this routine will return {@code null}. | 
|
*/  | 
|
public X500Principal getResponderName() {  | 
|
return responderName;  | 
|
}  | 
|
    /** | 
|
     * Obtain the underlying key identifier from a {@code ResponderId} | 
|
     * | 
|
     * @return the {@code KeyIdentifier} for this {@code ResponderId} if it | 
|
     *      is a BY_KEY variant.  If the {@code ResponderId} is a BY_NAME | 
|
     *      variant, this routine will return {@code null}. | 
|
*/  | 
|
public KeyIdentifier getKeyIdentifier() {  | 
|
return responderKeyId;  | 
|
}  | 
|
    /** | 
|
     * Compares the specified object with this {@code ResponderId} for equality. | 
|
     * A ResponderId will only be considered equivalent if both the type and | 
|
     * data value are equal.  Two ResponderIds initialized by name and | 
|
     * key ID, respectively, will not be equal even if the | 
|
     * ResponderId objects are created from the same source certificate. | 
|
     * | 
|
     * @param obj the object to be compared against | 
|
     * | 
|
     * @return true if the specified object is equal to this {@code Responderid} | 
|
*/  | 
|
@Override  | 
|
public boolean equals(Object obj) {  | 
|
if (obj == null) {  | 
|
return false;  | 
|
}  | 
|
if (this == obj) {  | 
|
return true;  | 
|
}  | 
|
if (obj instanceof ResponderId) {  | 
|
ResponderId respObj = (ResponderId)obj;  | 
|
return Arrays.equals(encodedRid, respObj.getEncoded());  | 
|
}  | 
|
return false;  | 
|
}  | 
|
    /** | 
|
     * Returns the hash code value for this {@code ResponderId} | 
|
     * | 
|
     * @return the hash code value for this {@code ResponderId} | 
|
*/  | 
|
@Override  | 
|
    public int hashCode() { | 
|
return Arrays.hashCode(encodedRid);  | 
|
}  | 
|
    /** | 
|
     * Create a String representation of this {@code ResponderId} | 
|
     * | 
|
     * @return a String representation of this {@code ResponderId} | 
|
*/  | 
|
@Override  | 
|
public String toString() {  | 
|
StringBuilder sb = new StringBuilder();  | 
|
switch (type) {  | 
|
case BY_NAME:  | 
|
sb.append(type).append(": ").append(responderName);  | 
|
break;  | 
|
case BY_KEY:  | 
|
sb.append(type).append(": ");  | 
|
for (byte keyIdByte : responderKeyId.getIdentifier()) {  | 
|
sb.append(String.format("%02X", keyIdByte));  | 
|
}  | 
|
break;  | 
|
default:  | 
|
sb.append("Unknown ResponderId Type: ").append(type);  | 
|
}  | 
|
return sb.toString();  | 
|
}  | 
|
    /** | 
|
     * Convert the responderName data member into its DER-encoded form | 
|
     * | 
|
     * @return the DER encoding for a responder ID byName option, including | 
|
     *      explicit context-specific tagging. | 
|
     * | 
|
     * @throws IOException if any encoding error occurs | 
|
*/  | 
|
private byte[] principalToBytes() throws IOException {  | 
|
DerValue dv = new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,  | 
|
true, (byte)Type.BY_NAME.value()),  | 
|
responderName.getEncoded());  | 
|
return dv.toByteArray();  | 
|
}  | 
|
    /** | 
|
     * Convert the responderKeyId data member into its DER-encoded form | 
|
     * | 
|
     * @return the DER encoding for a responder ID byKey option, including | 
|
     *      explicit context-specific tagging. | 
|
     * | 
|
     * @throws IOException if any encoding error occurs | 
|
*/  | 
|
private byte[] keyIdToBytes() throws IOException {  | 
|
        // Place the KeyIdentifier bytes into an OCTET STRING | 
|
DerValue inner = new DerValue(DerValue.tag_OctetString,  | 
|
responderKeyId.getIdentifier());  | 
|
// Mark the OCTET STRING-wrapped KeyIdentifier bytes  | 
|
        // as EXPLICIT CONTEXT 2 | 
|
DerValue outer = new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT,  | 
|
true, (byte)Type.BY_KEY.value()), inner.toByteArray());  | 
|
return outer.toByteArray();  | 
|
}  | 
|
}  |