1 /*
2  * Copyright (c) 1998, 2011, 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.x509;
27 
28 import java.io.IOException;
29 import java.lang.reflect.Constructor;
30 import java.util.Arrays;
31 
32 import 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  */
49 public 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      */
OtherName(ObjectIdentifier oid, byte[] value)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      */
OtherName(DerValue derValue)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      */
getOID()105     public ObjectIdentifier getOID() {
106         //XXXX May want to consider cloning this
107         return oid;
108     }
109 
110     /**
111      * Get name value
112      */
getNameValue()113     public byte[] getNameValue() {
114         return nameValue.clone();
115     }
116 
117     /**
118      * Get GeneralNameInterface
119      */
getGNI(ObjectIdentifier oid, byte[] nameValue)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 = 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 new IOException("Instantiation error: " + e, e);
136         }
137     }
138 
139     /**
140      * Return the type of the GeneralName.
141      */
getType()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      */
encode(DerOutputStream out)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      */
equals(Object other)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((Object)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      */
hashCode()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      */
toString()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      */
constrains(GeneralNameInterface inputName)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      */
subtreeDepth()264     public int subtreeDepth() {
265         throw new UnsupportedOperationException
266             ("subtreeDepth() not supported for generic OtherName");
267     }
268 
269 }
270