1/*
2 * Copyright (c) 1998, 2006, 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
26package sun.security.x509;
27
28import java.io.IOException;
29import java.lang.reflect.Constructor;
30import java.util.Arrays;
31
32import sun.security.util.*;
33
34/**
35 * This class represents the OtherName as required by the GeneralNames
36 * ASN.1 object. It supplies the generic framework to allow specific
37 * Other Name types, and also provides minimal support for unrecognized
38 * Other Name types.
39 *
40 * The ASN.1 definition for OtherName is:
41 * <pre>
42 * OtherName ::= SEQUENCE {
43 *     type-id    OBJECT IDENTIFIER,
44 *     value      [0] EXPLICIT ANY DEFINED BY type-id
45 * }
46 * </pre>
47 * @author Hemma Prafullchandra
48 */
49public class OtherName implements GeneralNameInterface {
50
51    private String name;
52    private ObjectIdentifier oid;
53    private byte[] nameValue = null;
54    private GeneralNameInterface gni = null;
55
56    private static final byte TAG_VALUE = 0;
57
58    private int myhash = -1;
59
60    /**
61     * Create the OtherName object from a passed ObjectIdentfier and
62     * byte array name value
63     *
64     * @param oid ObjectIdentifier of this OtherName object
65     * @param value the DER-encoded value of the OtherName
66     * @throws IOException on error
67     */
68    public OtherName(ObjectIdentifier oid, byte[] value) throws IOException {
69        if (oid == null || value == null) {
70            throw new NullPointerException("parameters may not be null");
71        }
72        this.oid = oid;
73        this.nameValue = value;
74        gni = getGNI(oid, value);
75        if (gni != null) {
76            name = gni.toString();
77        } else {
78            name = "Unrecognized ObjectIdentifier: " + oid.toString();
79        }
80    }
81
82    /**
83     * Create the OtherName object from the passed encoded Der value.
84     *
85     * @param derValue the encoded DER OtherName.
86     * @exception IOException on error.
87     */
88    public OtherName(DerValue derValue) throws IOException {
89        DerInputStream in = derValue.toDerInputStream();
90
91        oid = in.getOID();
92        DerValue val = in.getDerValue();
93        nameValue = val.toByteArray();
94        gni = getGNI(oid, nameValue);
95        if (gni != null) {
96            name = gni.toString();
97        } else {
98            name = "Unrecognized ObjectIdentifier: " + oid.toString();
99        }
100    }
101
102    /**
103     * Get ObjectIdentifier
104     */
105    public ObjectIdentifier getOID() {
106        //XXXX May want to consider cloning this
107        return oid;
108    }
109
110    /**
111     * Get name value
112     */
113    public byte[] getNameValue() {
114        return nameValue.clone();
115    }
116
117    /**
118     * Get GeneralNameInterface
119     */
120    private GeneralNameInterface getGNI(ObjectIdentifier oid, byte[] nameValue)
121            throws IOException {
122        try {
123            Class extClass = OIDMap.getClass(oid);
124            if (extClass == null) {   // Unsupported OtherName
125                return null;
126            }
127            Class[] params = { Object.class };
128            Constructor cons = ((Class<?>)extClass).getConstructor(params);
129
130            Object[] passed = new Object[] { nameValue };
131            GeneralNameInterface gni =
132                       (GeneralNameInterface)cons.newInstance(passed);
133            return gni;
134        } catch (Exception e) {
135            throw (IOException)new IOException("Instantiation error: " + e).initCause(e);
136        }
137    }
138
139    /**
140     * Return the type of the GeneralName.
141     */
142    public int getType() {
143        return GeneralNameInterface.NAME_ANY;
144    }
145
146    /**
147     * Encode the Other name into the DerOutputStream.
148     *
149     * @param out the DER stream to encode the Other-Name to.
150     * @exception IOException on encoding errors.
151     */
152    public void encode(DerOutputStream out) throws IOException {
153        if (gni != null) {
154            // This OtherName has a supported class
155            gni.encode(out);
156            return;
157        } else {
158            // This OtherName has no supporting class
159            DerOutputStream tmp = new DerOutputStream();
160            tmp.putOID(oid);
161            tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_VALUE), nameValue);
162            out.write(DerValue.tag_Sequence, tmp);
163        }
164    }
165
166    /**
167     * Compares this name with another, for equality.
168     *
169     * @return true iff the names are identical.
170     */
171    public boolean equals(Object other) {
172        if (this == other) {
173            return true;
174        }
175        if (!(other instanceof OtherName)) {
176            return false;
177        }
178        OtherName otherOther = (OtherName)other;
179        if (!(otherOther.oid.equals(oid))) {
180            return false;
181        }
182        GeneralNameInterface otherGNI = null;
183        try {
184            otherGNI = getGNI(otherOther.oid, otherOther.nameValue);
185        } catch (IOException ioe) {
186            return false;
187        }
188
189        boolean result;
190        if (otherGNI != null) {
191            try {
192                result = (otherGNI.constrains(this) == NAME_MATCH);
193            } catch (UnsupportedOperationException ioe) {
194                result = false;
195            }
196        } else {
197            result = Arrays.equals(nameValue, otherOther.nameValue);
198        }
199
200        return result;
201    }
202
203    /**
204     * Returns the hash code for this OtherName.
205     *
206     * @return a hash code value.
207     */
208    public int hashCode() {
209        if (myhash == -1) {
210            myhash = 37 + oid.hashCode();
211            for (int i = 0; i < nameValue.length; i++) {
212                myhash = 37 * myhash + nameValue[i];
213            }
214        }
215        return myhash;
216    }
217
218    /**
219     * Convert the name into user readable string.
220     */
221    public String toString() {
222        return "Other-Name: " + name;
223    }
224
225    /**
226     * Return type of constraint inputName places on this name:<ul>
227     *   <li>NAME_DIFF_TYPE = -1: input name is different type from name
228     *       (i.e. does not constrain).
229     *   <li>NAME_MATCH = 0: input name matches name.
230     *   <li>NAME_NARROWS = 1: input name narrows name (is lower in the
231     *       naming subtree)
232     *   <li>NAME_WIDENS = 2: input name widens name (is higher in the
233     *       naming subtree)
234     *   <li>NAME_SAME_TYPE = 3: input name does not match or narrow name,
235     *       but is same type.
236     * </ul>.  These results are used in checking NameConstraints during
237     * certification path verification.
238     *
239     * @param inputName to be checked for being constrained
240     * @returns constraint type above
241     * @throws UnsupportedOperationException if name is same type, but
242     *         comparison operations are not supported for this name type.
243     */
244    public int constrains(GeneralNameInterface inputName) {
245        int constraintType;
246        if (inputName == null) {
247            constraintType = NAME_DIFF_TYPE;
248        } else if (inputName.getType() != NAME_ANY) {
249            constraintType = NAME_DIFF_TYPE;
250        } else {
251            throw new UnsupportedOperationException("Narrowing, widening, "
252                + "and matching are not supported for OtherName.");
253        }
254        return constraintType;
255    }
256
257    /**
258     * Return subtree depth of this name for purposes of determining
259     * NameConstraints minimum and maximum bounds.
260     *
261     * @returns distance of name from root
262     * @throws UnsupportedOperationException if not supported for this name type
263     */
264    public int subtreeDepth() {
265        throw new UnsupportedOperationException
266            ("subtreeDepth() not supported for generic OtherName");
267    }
268
269}
270