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
26package sun.security.x509;
27
28import java.io.IOException;
29import java.io.OutputStream;
30import java.util.Arrays;
31import 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 */
61public 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}
277