View Javadoc
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       */
70      public Extension() { }
71  
72      /**
73       * Constructs an extension from a DER encoded array of bytes.
74       */
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      */
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      */
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      */
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 
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      */
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      */
188     public boolean isCritical() {
189         return critical;
190     }
191 
192     /**
193      * Returns the ObjectIdentifier of the extension.
194      */
195     public ObjectIdentifier getExtensionId() {
196         return extensionId;
197     }
198 
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      */
210     public byte[] getExtensionValue() {
211         return extensionValue;
212     }
213 
214     public String getId() {
215         return extensionId.toString();
216     }
217 
218     /**
219      * Returns the Extension in user readable form.
220      */
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      */
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      */
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 }