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.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
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 bitString[position];
87      }
88  
89      /**
90       * Set the bit at the specified position.
91       */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
220     public Boolean 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      */
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      */
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      */
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 }