1/*
2 * Copyright (c) 1997, 2015, 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.Enumeration;
31
32import sun.security.util.*;
33
34/**
35 * Represent the Key Usage Extension.
36 *
37 * <p>This extension, if present, defines the purpose (e.g., encipherment,
38 * signature, certificate signing) of the key contained in the certificate.
39 * The usage restriction might be employed when a multipurpose key is to be
40 * restricted (e.g., when an RSA key should be used only for signing or only
41 * for key encipherment).
42 *
43 * @author Amit Kapoor
44 * @author Hemma Prafullchandra
45 * @see Extension
46 * @see CertAttrSet
47 */
48public class KeyUsageExtension extends Extension
49implements CertAttrSet<String> {
50
51    /**
52     * Identifier for this attribute, to be used with the
53     * get, set, delete methods of Certificate, x509 type.
54     */
55    public static final String IDENT = "x509.info.extensions.KeyUsage";
56    /**
57     * Attribute names.
58     */
59    public static final String NAME = "KeyUsage";
60    public static final String DIGITAL_SIGNATURE = "digital_signature";
61    public static final String NON_REPUDIATION = "non_repudiation";
62    public static final String KEY_ENCIPHERMENT = "key_encipherment";
63    public static final String DATA_ENCIPHERMENT = "data_encipherment";
64    public static final String KEY_AGREEMENT = "key_agreement";
65    public static final String KEY_CERTSIGN = "key_certsign";
66    public static final String CRL_SIGN = "crl_sign";
67    public static final String ENCIPHER_ONLY = "encipher_only";
68    public static final String DECIPHER_ONLY = "decipher_only";
69
70    // Private data members
71    private boolean[] bitString;
72
73    // Encode this extension value
74    private void encodeThis() throws IOException {
75        DerOutputStream os = new DerOutputStream();
76        os.putTruncatedUnalignedBitString(new BitArray(this.bitString));
77        this.extensionValue = os.toByteArray();
78    }
79
80    /**
81     * Check if bit is set.
82     *
83     * @param position the position in the bit string to check.
84     */
85    private boolean isSet(int position) {
86        return (position < bitString.length) &&
87                bitString[position];
88    }
89
90    /**
91     * Set the bit at the specified position.
92     */
93    private void set(int position, boolean val) {
94        // enlarge bitString if necessary
95        if (position >= bitString.length) {
96            boolean[] tmp = new boolean[position+1];
97            System.arraycopy(bitString, 0, tmp, 0, bitString.length);
98            bitString = tmp;
99        }
100        bitString[position] = val;
101    }
102
103    /**
104     * Create a KeyUsageExtension with the passed bit settings. The criticality
105     * is set to true.
106     *
107     * @param bitString the bits to be set for the extension.
108     */
109    public KeyUsageExtension(byte[] bitString) throws IOException {
110        this.bitString =
111            new BitArray(bitString.length*8,bitString).toBooleanArray();
112        this.extensionId = PKIXExtensions.KeyUsage_Id;
113        this.critical = true;
114        encodeThis();
115    }
116
117    /**
118     * Create a KeyUsageExtension with the passed bit settings. The criticality
119     * is set to true.
120     *
121     * @param bitString the bits to be set for the extension.
122     */
123    public KeyUsageExtension(boolean[] bitString) throws IOException {
124        this.bitString = bitString;
125        this.extensionId = PKIXExtensions.KeyUsage_Id;
126        this.critical = true;
127        encodeThis();
128    }
129
130    /**
131     * Create a KeyUsageExtension with the passed bit settings. The criticality
132     * is set to true.
133     *
134     * @param bitString the bits to be set for the extension.
135     */
136    public KeyUsageExtension(BitArray bitString) throws IOException {
137        this.bitString = bitString.toBooleanArray();
138        this.extensionId = PKIXExtensions.KeyUsage_Id;
139        this.critical = true;
140        encodeThis();
141    }
142
143    /**
144     * Create the extension from the passed DER encoded value of the same.
145     * The DER encoded value may be wrapped in an OCTET STRING.
146     *
147     * @param critical true if the extension is to be treated as critical.
148     * @param value an array of DER encoded bytes of the actual value (possibly
149     * wrapped in an OCTET STRING).
150     * @exception ClassCastException if value is not an array of bytes
151     * @exception IOException on error.
152     */
153    public KeyUsageExtension(Boolean critical, Object value)
154    throws IOException {
155        this.extensionId = PKIXExtensions.KeyUsage_Id;
156        this.critical = critical.booleanValue();
157        /*
158         * The following check should be activated again after
159         * the PKIX profiling work becomes standard and the check
160         * is not a barrier to interoperability !
161         * if (!this.critical) {
162         *   throw new IOException("KeyUsageExtension not marked critical,"
163         *                         + " invalid profile.");
164         * }
165         */
166        byte[] extValue = (byte[]) value;
167        if (extValue[0] == DerValue.tag_OctetString) {
168            this.extensionValue = new DerValue(extValue).getOctetString();
169        } else {
170            this.extensionValue = extValue;
171        }
172        DerValue val = new DerValue(this.extensionValue);
173        this.bitString = val.getUnalignedBitString().toBooleanArray();
174    }
175
176    /**
177     * Create a default key usage.
178     */
179    public KeyUsageExtension() {
180        extensionId = PKIXExtensions.KeyUsage_Id;
181        critical = true;
182        bitString = new boolean[0];
183    }
184
185    /**
186     * Set the attribute value.
187     */
188    public void set(String name, Object obj) throws IOException {
189        if (!(obj instanceof Boolean)) {
190            throw new IOException("Attribute must be of type Boolean.");
191        }
192        boolean val = ((Boolean)obj).booleanValue();
193        if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) {
194            set(0,val);
195        } else if (name.equalsIgnoreCase(NON_REPUDIATION)) {
196            set(1,val);
197        } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) {
198            set(2,val);
199        } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) {
200            set(3,val);
201        } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) {
202            set(4,val);
203        } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) {
204            set(5,val);
205        } else if (name.equalsIgnoreCase(CRL_SIGN)) {
206            set(6,val);
207        } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) {
208            set(7,val);
209        } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) {
210            set(8,val);
211        } else {
212          throw new IOException("Attribute name not recognized by"
213                                + " CertAttrSet:KeyUsage.");
214        }
215        encodeThis();
216    }
217
218    /**
219     * Get the attribute value.
220     */
221    public Boolean get(String name) throws IOException {
222        if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) {
223            return Boolean.valueOf(isSet(0));
224        } else if (name.equalsIgnoreCase(NON_REPUDIATION)) {
225            return Boolean.valueOf(isSet(1));
226        } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) {
227            return Boolean.valueOf(isSet(2));
228        } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) {
229            return Boolean.valueOf(isSet(3));
230        } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) {
231            return Boolean.valueOf(isSet(4));
232        } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) {
233            return Boolean.valueOf(isSet(5));
234        } else if (name.equalsIgnoreCase(CRL_SIGN)) {
235            return Boolean.valueOf(isSet(6));
236        } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) {
237            return Boolean.valueOf(isSet(7));
238        } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) {
239            return Boolean.valueOf(isSet(8));
240        } else {
241          throw new IOException("Attribute name not recognized by"
242                                + " CertAttrSet:KeyUsage.");
243        }
244    }
245
246    /**
247     * Delete the attribute value.
248     */
249    public void delete(String name) throws IOException {
250        if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) {
251            set(0,false);
252        } else if (name.equalsIgnoreCase(NON_REPUDIATION)) {
253            set(1,false);
254        } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) {
255            set(2,false);
256        } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) {
257            set(3,false);
258        } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) {
259            set(4,false);
260        } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) {
261            set(5,false);
262        } else if (name.equalsIgnoreCase(CRL_SIGN)) {
263            set(6,false);
264        } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) {
265            set(7,false);
266        } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) {
267            set(8,false);
268        } else {
269          throw new IOException("Attribute name not recognized by"
270                                + " CertAttrSet:KeyUsage.");
271        }
272        encodeThis();
273    }
274
275    /**
276     * Returns a printable representation of the KeyUsage.
277     */
278    public String toString() {
279        StringBuilder sb = new StringBuilder();
280        sb.append(super.toString());
281        sb.append("KeyUsage [\n");
282
283        if (isSet(0)) {
284            sb.append("  DigitalSignature\n");
285        }
286        if (isSet(1)) {
287            sb.append("  Non_repudiation\n");
288        }
289        if (isSet(2)) {
290            sb.append("  Key_Encipherment\n");
291        }
292        if (isSet(3)) {
293            sb.append("  Data_Encipherment\n");
294        }
295        if (isSet(4)) {
296            sb.append("  Key_Agreement\n");
297        }
298        if (isSet(5)) {
299            sb.append("  Key_CertSign\n");
300        }
301        if (isSet(6)) {
302            sb.append("  Crl_Sign\n");
303        }
304        if (isSet(7)) {
305            sb.append("  Encipher_Only\n");
306        }
307        if (isSet(8)) {
308            sb.append("  Decipher_Only\n");
309        }
310        sb.append("]\n");
311
312        return sb.toString();
313    }
314
315    /**
316     * Write the extension to the DerOutputStream.
317     *
318     * @param out the DerOutputStream to write the extension to.
319     * @exception IOException on encoding errors.
320     */
321    public void encode(OutputStream out) throws IOException {
322       DerOutputStream  tmp = new DerOutputStream();
323
324       if (this.extensionValue == null) {
325           this.extensionId = PKIXExtensions.KeyUsage_Id;
326           this.critical = true;
327           encodeThis();
328       }
329       super.encode(tmp);
330       out.write(tmp.toByteArray());
331    }
332
333    /**
334     * Return an enumeration of names of attributes existing within this
335     * attribute.
336     */
337    public Enumeration<String> getElements() {
338        AttributeNameEnumeration elements = new AttributeNameEnumeration();
339        elements.addElement(DIGITAL_SIGNATURE);
340        elements.addElement(NON_REPUDIATION);
341        elements.addElement(KEY_ENCIPHERMENT);
342        elements.addElement(DATA_ENCIPHERMENT);
343        elements.addElement(KEY_AGREEMENT);
344        elements.addElement(KEY_CERTSIGN);
345        elements.addElement(CRL_SIGN);
346        elements.addElement(ENCIPHER_ONLY);
347        elements.addElement(DECIPHER_ONLY);
348
349        return (elements.elements());
350    }
351
352
353    public boolean[] getBits() {
354        return bitString.clone();
355    }
356
357    /**
358     * Return the name of this attribute.
359     */
360    public String getName() {
361        return (NAME);
362    }
363}
364