1 /*
2  * Copyright (c) 1997, 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.io.OutputStream;
30 import java.util.Arrays;
31 import sun.security.util.*;
32 
33 /**
34  * Represent a X509 Extension Attribute.
35  *
36  * <p>Extensions are additional attributes which can be inserted in a X509
37  * v3 certificate. For example a "Driving License Certificate" could have
38  * the driving license number as a extension.
39  *
40  * <p>Extensions are represented as a sequence of the extension identifier
41  * (Object Identifier), a boolean flag stating whether the extension is to
42  * be treated as being critical and the extension value itself (this is again
43  * a DER encoding of the extension value).
44  * <pre>
45  * ASN.1 definition of Extension:
46  * Extension ::= SEQUENCE {
47  *      ExtensionId     OBJECT IDENTIFIER,
48  *      critical        BOOLEAN DEFAULT FALSE,
49  *      extensionValue  OCTET STRING
50  * }
51  * </pre>
52  * All subclasses need to implement a constructor of the form
53  * <pre>
54  *     <subclass> (Boolean, Object)
55  * </pre>
56  * where the Object is typically an array of DER encoded bytes.
57  * <p>
58  * @author Amit Kapoor
59  * @author Hemma Prafullchandra
60  */
61 public class Extension implements java.security.cert.Extension {
62 
63     protected ObjectIdentifier  extensionId = null;
64     protected boolean           critical = false;
65     protected byte[]            extensionValue = null;
66 
67     /**
68      * Default constructor.  Used only by sub-classes.
69      */
Extension()70     public Extension() { }
71 
72     /**
73      * Constructs an extension from a DER encoded array of bytes.
74      */
Extension(DerValue derVal)75     public Extension(DerValue derVal) throws IOException {
76 
77         DerInputStream in = derVal.toDerInputStream();
78 
79         // Object identifier
80         extensionId = in.getOID();
81 
82         // If the criticality flag was false, it will not have been encoded.
83         DerValue val = in.getDerValue();
84         if (val.tag == DerValue.tag_Boolean) {
85             critical = val.getBoolean();
86 
87             // Extension value (DER encoded)
88             val = in.getDerValue();
89             extensionValue = val.getOctetString();
90         } else {
91             critical = false;
92             extensionValue = val.getOctetString();
93         }
94     }
95 
96     /**
97      * Constructs an Extension from individual components of ObjectIdentifier,
98      * criticality and the DER encoded OctetString.
99      *
100      * @param extensionId the ObjectIdentifier of the extension
101      * @param critical the boolean indicating if the extension is critical
102      * @param extensionValue the DER encoded octet string of the value.
103      */
Extension(ObjectIdentifier extensionId, boolean critical, byte[] extensionValue)104     public Extension(ObjectIdentifier extensionId, boolean critical,
105                      byte[] extensionValue) throws IOException {
106         this.extensionId = extensionId;
107         this.critical = critical;
108         // passed in a DER encoded octet string, strip off the tag
109         // and length
110         DerValue inDerVal = new DerValue(extensionValue);
111         this.extensionValue = inDerVal.getOctetString();
112     }
113 
114     /**
115      * Constructs an Extension from another extension. To be used for
116      * creating decoded subclasses.
117      *
118      * @param ext the extension to create from.
119      */
Extension(Extension ext)120     public Extension(Extension ext) {
121         this.extensionId = ext.extensionId;
122         this.critical = ext.critical;
123         this.extensionValue = ext.extensionValue;
124     }
125 
126     /**
127      * Constructs an Extension from individual components of ObjectIdentifier,
128      * criticality and the raw encoded extension value.
129      *
130      * @param extensionId the ObjectIdentifier of the extension
131      * @param critical the boolean indicating if the extension is critical
132      * @param rawExtensionValue the raw DER-encoded extension value (this
133      * is not the encoded OctetString).
134      */
newExtension(ObjectIdentifier extensionId, boolean critical, byte[] rawExtensionValue)135     public static Extension newExtension(ObjectIdentifier extensionId,
136         boolean critical, byte[] rawExtensionValue) throws IOException {
137         Extension ext = new Extension();
138         ext.extensionId = extensionId;
139         ext.critical = critical;
140         ext.extensionValue = rawExtensionValue;
141         return ext;
142     }
143 
encode(OutputStream out)144     public void encode(OutputStream out) throws IOException {
145         if (out == null) {
146             throw new NullPointerException();
147         }
148 
149         DerOutputStream dos1 = new DerOutputStream();
150         DerOutputStream dos2 = new DerOutputStream();
151 
152         dos1.putOID(extensionId);
153         if (critical) {
154             dos1.putBoolean(critical);
155         }
156         dos1.putOctetString(extensionValue);
157 
158         dos2.write(DerValue.tag_Sequence, dos1);
159         out.write(dos2.toByteArray());
160     }
161 
162     /**
163      * Write the extension to the DerOutputStream.
164      *
165      * @param out the DerOutputStream to write the extension to.
166      * @exception IOException on encoding errors
167      */
encode(DerOutputStream out)168     public void encode(DerOutputStream out) throws IOException {
169 
170         if (extensionId == null)
171             throw new IOException("Null OID to encode for the extension!");
172         if (extensionValue == null)
173             throw new IOException("No value to encode for the extension!");
174 
175         DerOutputStream dos = new DerOutputStream();
176 
177         dos.putOID(extensionId);
178         if (critical)
179             dos.putBoolean(critical);
180         dos.putOctetString(extensionValue);
181 
182         out.write(DerValue.tag_Sequence, dos);
183     }
184 
185     /**
186      * Returns true if extension is critical.
187      */
isCritical()188     public boolean isCritical() {
189         return critical;
190     }
191 
192     /**
193      * Returns the ObjectIdentifier of the extension.
194      */
getExtensionId()195     public ObjectIdentifier getExtensionId() {
196         return extensionId;
197     }
198 
getValue()199     public byte[] getValue() {
200         return extensionValue.clone();
201     }
202 
203     /**
204      * Returns the extension value as an byte array for further processing.
205      * Note, this is the raw DER value of the extension, not the DER
206      * encoded octet string which is in the certificate.
207      * This method does not return a clone; it is the responsibility of the
208      * caller to clone the array if necessary.
209      */
getExtensionValue()210     public byte[] getExtensionValue() {
211         return extensionValue;
212     }
213 
getId()214     public String getId() {
215         return extensionId.toString();
216     }
217 
218     /**
219      * Returns the Extension in user readable form.
220      */
toString()221     public String toString() {
222         String s = "ObjectId: " + extensionId.toString();
223         if (critical) {
224             s += " Criticality=true\n";
225         } else {
226             s += " Criticality=false\n";
227         }
228         return (s);
229     }
230 
231     // Value to mix up the hash
232     private static final int hashMagic = 31;
233 
234     /**
235      * Returns a hashcode value for this Extension.
236      *
237      * @return the hashcode value.
238      */
hashCode()239     public int hashCode() {
240         int h = 0;
241         if (extensionValue != null) {
242             byte[] val = extensionValue;
243             int len = val.length;
244             while (len > 0)
245                 h += len * val[--len];
246         }
247         h = h * hashMagic + extensionId.hashCode();
248         h = h * hashMagic + (critical?1231:1237);
249         return h;
250     }
251 
252     /**
253      * Compares this Extension for equality with the specified
254      * object. If the <code>other</code> object is an
255      * <code>instanceof</code> <code>Extension</code>, then
256      * its encoded form is retrieved and compared with the
257      * encoded form of this Extension.
258      *
259      * @param other the object to test for equality with this Extension.
260      * @return true iff the other object is of type Extension, and the
261      * criticality flag, object identifier and encoded extension value of
262      * the two Extensions match, false otherwise.
263      */
equals(Object other)264     public boolean equals(Object other) {
265         if (this == other)
266             return true;
267         if (!(other instanceof Extension))
268             return false;
269         Extension otherExt = (Extension) other;
270         if (critical != otherExt.critical)
271             return false;
272         if (!extensionId.equals((Object)otherExt.extensionId))
273             return false;
274         return Arrays.equals(extensionValue, otherExt.extensionValue);
275     }
276 }
277