J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any 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 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 | */ |
| 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 | } |