1 /*
2  * Copyright (c) 1997, 2006, 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.Enumeration;
31 
32 import 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  */
48 public class KeyUsageExtension extends Extension
49 implements 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
encodeThis()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      */
isSet(int position)85     private boolean isSet(int position) {
86         return bitString[position];
87     }
88 
89     /**
90      * Set the bit at the specified position.
91      */
set(int position, boolean val)92     private void set(int position, boolean val) {
93         // enlarge bitString if necessary
94         if (position >= bitString.length) {
95             boolean[] tmp = new boolean[position+1];
96             System.arraycopy(bitString, 0, tmp, 0, bitString.length);
97             bitString = tmp;
98         }
99         bitString[position] = val;
100     }
101 
102     /**
103      * Create a KeyUsageExtension with the passed bit settings. The criticality
104      * is set to true.
105      *
106      * @param bitString the bits to be set for the extension.
107      */
KeyUsageExtension(byte[] bitString)108     public KeyUsageExtension(byte[] bitString) throws IOException {
109         this.bitString =
110             new BitArray(bitString.length*8,bitString).toBooleanArray();
111         this.extensionId = PKIXExtensions.KeyUsage_Id;
112         this.critical = true;
113         encodeThis();
114     }
115 
116     /**
117      * Create a KeyUsageExtension with the passed bit settings. The criticality
118      * is set to true.
119      *
120      * @param bitString the bits to be set for the extension.
121      */
KeyUsageExtension(boolean[] bitString)122     public KeyUsageExtension(boolean[] bitString) throws IOException {
123         this.bitString = bitString;
124         this.extensionId = PKIXExtensions.KeyUsage_Id;
125         this.critical = true;
126         encodeThis();
127     }
128 
129     /**
130      * Create a KeyUsageExtension with the passed bit settings. The criticality
131      * is set to true.
132      *
133      * @param bitString the bits to be set for the extension.
134      */
KeyUsageExtension(BitArray bitString)135     public KeyUsageExtension(BitArray bitString) throws IOException {
136         this.bitString = bitString.toBooleanArray();
137         this.extensionId = PKIXExtensions.KeyUsage_Id;
138         this.critical = true;
139         encodeThis();
140     }
141 
142     /**
143      * Create the extension from the passed DER encoded value of the same.
144      * The DER encoded value may be wrapped in an OCTET STRING.
145      *
146      * @param critical true if the extension is to be treated as critical.
147      * @param value an array of DER encoded bytes of the actual value (possibly
148      * wrapped in an OCTET STRING).
149      * @exception ClassCastException if value is not an array of bytes
150      * @exception IOException on error.
151      */
KeyUsageExtension(Boolean critical, Object value)152     public KeyUsageExtension(Boolean critical, Object value)
153     throws IOException {
154         this.extensionId = PKIXExtensions.KeyUsage_Id;
155         this.critical = critical.booleanValue();
156         /*
157          * The following check should be activated again after
158          * the PKIX profiling work becomes standard and the check
159          * is not a barrier to interoperability !
160          * if (!this.critical) {
161          *   throw new IOException("KeyUsageExtension not marked critical,"
162          *                         + " invalid profile.");
163          * }
164          */
165         byte[] extValue = (byte[]) value;
166         if (extValue[0] == DerValue.tag_OctetString) {
167             this.extensionValue = new DerValue(extValue).getOctetString();
168         } else {
169             this.extensionValue = extValue;
170         }
171         DerValue val = new DerValue(this.extensionValue);
172         this.bitString = val.getUnalignedBitString().toBooleanArray();
173     }
174 
175     /**
176      * Create a default key usage.
177      */
KeyUsageExtension()178     public KeyUsageExtension() {
179         extensionId = PKIXExtensions.KeyUsage_Id;
180         critical = true;
181         bitString = new boolean[0];
182     }
183 
184     /**
185      * Set the attribute value.
186      */
set(String name, Object obj)187     public void set(String name, Object obj) throws IOException {
188         if (!(obj instanceof Boolean)) {
189             throw new IOException("Attribute must be of type Boolean.");
190         }
191         boolean val = ((Boolean)obj).booleanValue();
192         if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) {
193             set(0,val);
194         } else if (name.equalsIgnoreCase(NON_REPUDIATION)) {
195             set(1,val);
196         } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) {
197             set(2,val);
198         } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) {
199             set(3,val);
200         } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) {
201             set(4,val);
202         } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) {
203             set(5,val);
204         } else if (name.equalsIgnoreCase(CRL_SIGN)) {
205             set(6,val);
206         } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) {
207             set(7,val);
208         } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) {
209             set(8,val);
210         } else {
211           throw new IOException("Attribute name not recognized by"
212                                 + " CertAttrSet:KeyUsage.");
213         }
214         encodeThis();
215     }
216 
217     /**
218      * Get the attribute value.
219      */
get(String name)220     public Object get(String name) throws IOException {
221         if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) {
222             return Boolean.valueOf(isSet(0));
223         } else if (name.equalsIgnoreCase(NON_REPUDIATION)) {
224             return Boolean.valueOf(isSet(1));
225         } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) {
226             return Boolean.valueOf(isSet(2));
227         } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) {
228             return Boolean.valueOf(isSet(3));
229         } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) {
230             return Boolean.valueOf(isSet(4));
231         } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) {
232             return Boolean.valueOf(isSet(5));
233         } else if (name.equalsIgnoreCase(CRL_SIGN)) {
234             return Boolean.valueOf(isSet(6));
235         } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) {
236             return Boolean.valueOf(isSet(7));
237         } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) {
238             return Boolean.valueOf(isSet(8));
239         } else {
240           throw new IOException("Attribute name not recognized by"
241                                 + " CertAttrSet:KeyUsage.");
242         }
243     }
244 
245     /**
246      * Delete the attribute value.
247      */
delete(String name)248     public void delete(String name) throws IOException {
249         if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) {
250             set(0,false);
251         } else if (name.equalsIgnoreCase(NON_REPUDIATION)) {
252             set(1,false);
253         } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) {
254             set(2,false);
255         } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) {
256             set(3,false);
257         } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) {
258             set(4,false);
259         } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) {
260             set(5,false);
261         } else if (name.equalsIgnoreCase(CRL_SIGN)) {
262             set(6,false);
263         } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) {
264             set(7,false);
265         } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) {
266             set(8,false);
267         } else {
268           throw new IOException("Attribute name not recognized by"
269                                 + " CertAttrSet:KeyUsage.");
270         }
271         encodeThis();
272     }
273 
274     /**
275      * Returns a printable representation of the KeyUsage.
276      */
toString()277     public String toString() {
278         String s = super.toString() + "KeyUsage [\n";
279 
280         try {
281             if (isSet(0)) {
282                 s += "  DigitalSignature\n";
283             }
284             if (isSet(1)) {
285                 s += "  Non_repudiation\n";
286             }
287             if (isSet(2)) {
288                 s += "  Key_Encipherment\n";
289             }
290             if (isSet(3)) {
291                 s += "  Data_Encipherment\n";
292             }
293             if (isSet(4)) {
294                 s += "  Key_Agreement\n";
295             }
296             if (isSet(5)) {
297                 s += "  Key_CertSign\n";
298             }
299             if (isSet(6)) {
300                 s += "  Crl_Sign\n";
301             }
302             if (isSet(7)) {
303                 s += "  Encipher_Only\n";
304             }
305             if (isSet(8)) {
306                 s += "  Decipher_Only\n";
307             }
308         } catch (ArrayIndexOutOfBoundsException ex) {}
309 
310         s += "]\n";
311 
312         return (s);
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      */
encode(OutputStream out)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      */
getElements()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 
getBits()353     public boolean[] getBits() {
354         return bitString.clone();
355     }
356 
357     /**
358      * Return the name of this attribute.
359      */
getName()360     public String getName() {
361         return (NAME);
362     }
363 }
364