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 
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 (position < bitString.length) &&
87                 bitString[position];
88     }
89 
90     /**
91      * Set the bit at the specified position.
92      */
set(int position, boolean val)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      */
KeyUsageExtension(byte[] bitString)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      */
KeyUsageExtension(boolean[] bitString)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      */
KeyUsageExtension(BitArray bitString)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      */
KeyUsageExtension(Boolean critical, Object value)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      */
KeyUsageExtension()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      */
set(String name, Object obj)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      */
get(String name)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      */
delete(String name)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      */
toString()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      */
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