001: /* $Id: Cipher.java,v 1.1 2004/01/21 15:26:53 rgrimm Exp $
002: *
003: * Copyright (C) 1995-2000 The Cryptix Foundation Limited.
004: * All rights reserved.
005: *
006: * Use, modification, copying and distribution of this software is subject
007: * the terms and conditions of the Cryptix General Licence. You should have
008: * received a copy of the Cryptix General Licence along with this library;
009: * if not, you can download a copy from http://www.cryptix.org/ .
010: */
011: package javax.crypto;
012:
013: import java.security.AlgorithmParameters;
014: import java.security.InvalidAlgorithmParameterException;
015: import java.security.InvalidKeyException;
016: import java.security.Key;
017: import java.security.NoSuchAlgorithmException;
018: import java.security.NoSuchProviderException;
019: import java.security.Provider;
020: import java.security.SecureRandom;
021: import java.security.Security;
022: import java.security.spec.AlgorithmParameterSpec;
023: import java.security.PublicKey;
024: import java.security.cert.Certificate;
025: import java.security.cert.X509Certificate;
026: import java.util.Iterator;
027:
028: // XXX TODO: implement state
029:
030: /**
031: * @version $Revision: 1.1 $
032: * @author Jeroen C. van Gelderen (gelderen@cryptix.org)
033: * @author Paul Waserbrot (pw@cryptix.org)
034: */
035: public class Cipher {
036:
037: // Static variables and constants
038: //...........................................................................
039:
040: /** Constants used for the init() methods */
041: public static final int ENCRYPT_MODE = 1, DECRYPT_MODE = 2,
042: PRIVATE_KEY = 3, PUBLIC_KEY = 4, SECRET_KEY = 5,
043: UNWRAP_MODE = 6, WRAP_MODE = 7;
044:
045: // Instance variables
046: //...........................................................................
047:
048: /** The Cipher implementation we wrap */
049: private final CipherSpi spi;
050:
051: /** Our provider */
052: private final Provider provider;
053:
054: /** Our name */
055: private final String transformation;
056:
057: /** Our mechanism */
058: // FIXME: I need to check something, to see if mechanism= null or not.
059: // Until then, make it null... (pw)
060: private final ExemptionMechanism mechanism = null;
061:
062: private boolean isInitialized = false;
063:
064: // Constructor
065: //...........................................................................
066:
067: /**
068: * Constructs a Cipher wrapping the given cipherSpi.
069: * <p>
070: * This method should be private, but is not for backward compatibility.
071: */
072: protected Cipher(CipherSpi cipherSpi, Provider provider,
073: String transformation) {
074: this .spi = cipherSpi;
075: this .provider = provider;
076: this .transformation = transformation;
077: }
078:
079: public String toString() {
080: return "Cipher object: " + this .transformation;
081: // SHA1withRSA<initialized for verifying>
082: }
083:
084: // FIXME:
085: // 1. Clean up and
086: // 2. check for compatibility.
087: // 3. Fix ignored exceptions
088: // 4. Possibly merge with Support.java
089: private static Object[] getCipherImplementation(
090: String transformation, Provider p)
091: throws NoSuchPaddingException {
092: //
093: // Extract name components from 'transformation'
094: //
095: String part_alg;
096: String part_mode = "//"; // Default is invalid name
097: String part_pad = "//";
098:
099: int index_1 = transformation.indexOf('/');
100: int index_2 = transformation.indexOf('/', index_1 + 1);
101:
102: if (index_1 == -1)
103: part_alg = transformation;
104: else if (index_2 == -1)
105: return null;
106: else {
107: part_alg = transformation.substring(0, index_1);
108: part_mode = transformation.substring(index_1 + 1, index_2);
109: part_pad = transformation.substring(index_2 + 1);
110: }
111:
112: //
113: // Try and get a class instance
114: //
115: try {
116: CipherSpi spi;
117: String class_name;
118: Object[] res = new Object[2];
119:
120: res[1] = p;
121:
122: class_name = Support.getClassName("Cipher", transformation,
123: p);
124: if (class_name != null) {
125: res[0] = Class.forName(class_name).newInstance();
126: return res;
127: }
128: class_name = Support.getClassName("Cipher", part_alg + "/"
129: + part_mode, p);
130: if (class_name != null) {
131: spi = (CipherSpi) Class.forName(class_name)
132: .newInstance();
133: spi.engineSetPadding(part_pad);
134: res[0] = spi;
135: return res;
136: }
137: class_name = Support.getClassName("Cipher", part_alg + "//"
138: + part_alg, p);
139: if (class_name != null) {
140: spi = (CipherSpi) Class.forName(class_name)
141: .newInstance();
142: spi.engineSetMode(part_mode);
143: res[0] = spi;
144: return res;
145: }
146: class_name = Support.getClassName("Cipher", part_alg, p);
147: if (class_name != null) {
148: spi = (CipherSpi) Class.forName(class_name)
149: .newInstance();
150: spi.engineSetMode(part_mode);
151: spi.engineSetPadding(part_pad);
152: res[0] = spi;
153: return res;
154: }
155: } catch (NoSuchAlgorithmException e) {
156: } catch (LinkageError e) {
157: // FIXME: Throw a RuntimeException(?) with a sensible message????
158: } catch (ClassNotFoundException e) {
159: // FIXME: Throw a RuntimeException(?) with a sensible message????
160: } catch (InstantiationException e) {
161: // FIXME: Throw a RuntimeException(?) with a sensible message????
162: } catch (IllegalAccessException e) {
163: // FIXME: Throw a RuntimeException(?) with a sensible message????
164: }
165: return null;
166: }
167:
168: /**
169: * Creates a Cipher that implements the given transformation.
170: * <p>
171: * This call will search all installed Providers in preference order and
172: * returns the first matching transformation.
173: * <p>
174: * Transformation is specified as the triple "Algorithm/Mode/Padding".
175: * We currently do not support transformation of type "Algorithm" with
176: * defaults for mode and padding.
177: *
178: * @param transformation
179: * The requested transformation.
180: * @returns A Cipher implementing the requested transformation.
181: * @throws NoSuchAlgorithmException
182: * If the given Algorithm/Mode/<any> combination cannot be found.
183: * @throws NoSuchPaddingException
184: * If the given <any>/<any>/Padding cannot be found.
185: */
186: public static Cipher getInstance(String transformation)
187: throws NoSuchAlgorithmException, NoSuchPaddingException {
188: Provider[] providers = Security.getProviders();
189: if ((providers == null) || (providers.length == 0))
190: throw new NoSuchAlgorithmException("No providers installed");
191:
192: for (int i = 0; i < providers.length; i++) {
193: Object[] res = getCipherImplementation(transformation,
194: providers[i]);
195: if (res != null)
196: return new Cipher((CipherSpi) res[0],
197: (Provider) res[1], transformation);
198: }
199: throw new NoSuchAlgorithmException(
200: "Algorithm not found. [Cipher." + transformation + "]");
201: }
202:
203: /**
204: * Creates a Cipher that implements the given transformation.
205: * <p>
206: * This call searches the given provider only.
207: * <p>
208: * Transformation is specified as the triple "Algorithm/Mode/Padding".
209: * We currently do not support transformation of type "Algorithm" with
210: * defaults for mode and padding.
211: *
212: * @param transformation
213: * The requested transformation.
214: * @returns A Cipher implementing the requested transformation.
215: * @throws NoSuchAlgorithmException
216: * If the given Algorithm/Mode/<any> combination cannot be found.
217: * @throws NoSuchPaddingException
218: * If the given <any>/<any>/Padding cannot be found.
219: * @throws NoSuchProviderException
220: * If the given provider is not installed.
221: */
222: public static Cipher getInstance(String transformation,
223: String provider) throws NoSuchAlgorithmException,
224: NoSuchProviderException, NoSuchPaddingException {
225: Provider p = Security.getProvider(provider);
226: if (p == null)
227: throw new NoSuchProviderException("Provider not found. ["
228: + provider + "]");
229:
230: Object[] res = getCipherImplementation(transformation, p);
231: if (res != null)
232: return new Cipher((CipherSpi) res[0], (Provider) res[1],
233: transformation);
234:
235: throw new NoSuchAlgorithmException(
236: "Algorithm not found. [Cipher." + transformation + "]");
237: }
238:
239: /**
240: * Returns this Cipher's Provider.
241: */
242: public final Provider getProvider() {
243: return this .provider;
244: }
245:
246: /**
247: * Returns the name of the transformation implemented by this Cipher.
248: */
249: public final String getAlgorithm() {
250: return this .transformation;
251: }
252:
253: /**
254: * Returns the block size if this Cipher, or 0 if the underlying
255: * Cipher doesn't (yet) have a block size.
256: */
257: public final int getBlockSize() {
258: return this .spi.engineGetBlockSize();
259: }
260:
261: /**
262: * Returns the maximum number of bytes that the next update() or
263: * doFinal() operation can return, given the length of the input.
264: * <p>
265: * This basically returns the sum of buffered data, padding, and input.
266: */
267: public final int getOutputSize(int inputLen)
268: throws IllegalStateException {
269: if (inputLen < 0)
270: throw new IllegalArgumentException(
271: "Input size must be >= 0");
272:
273: return this .spi.engineGetOutputSize(inputLen);
274: }
275:
276: /**
277: * Returns the IV associated with this Cipher, or null if this Cipher
278: * doesn't (yet) have one.
279: * <p>
280: * This is a rather brain damaged convenience method, because there is
281: * no corresponding setIV method (you need to use the ParameterSpec crap
282: * instead).
283: */
284: public final byte[] getIV() {
285: return this .spi.engineGetIV();
286: }
287:
288: public final ExemptionMechanism getExemptionMechanism() {
289: return this .mechanism;
290: }
291:
292: public final AlgorithmParameters getParameters() {
293: return this .spi.engineGetParameters();
294: }
295:
296: public final void init(int opmode, Key key)
297: throws InvalidKeyException {
298: this .spi.engineInit(opmode, key, new SecureRandom());
299: isInitialized = true;
300: }
301:
302: public final void init(int opmode, Key key, SecureRandom random)
303: throws InvalidKeyException {
304: this .spi.engineInit(opmode, key, random);
305: isInitialized = true;
306: }
307:
308: public final void init(int opmode, Key key,
309: AlgorithmParameterSpec params) throws InvalidKeyException,
310: InvalidAlgorithmParameterException {
311: this .spi.engineInit(opmode, key, params, new SecureRandom());
312: isInitialized = true;
313: }
314:
315: public final void init(int opmode, Key key,
316: AlgorithmParameterSpec params, SecureRandom random)
317: throws InvalidKeyException,
318: InvalidAlgorithmParameterException {
319: this .spi.engineInit(opmode, key, params, random);
320: isInitialized = true;
321: }
322:
323: public final void init(int opmode, Key key,
324: AlgorithmParameters params) throws InvalidKeyException,
325: InvalidAlgorithmParameterException {
326: this .spi.engineInit(opmode, key, params, new SecureRandom());
327: isInitialized = true;
328: }
329:
330: public final void init(int opmode, Key key,
331: AlgorithmParameters params, SecureRandom random)
332: throws InvalidKeyException,
333: InvalidAlgorithmParameterException {
334: this .spi.engineInit(opmode, key, params, random);
335: isInitialized = true;
336: }
337:
338: public final byte[] update(byte[] input)
339: throws IllegalStateException {
340: if (input == null)
341: throw new IllegalArgumentException();
342:
343: return this .spi.engineUpdate(input, 0, input.length);
344: }
345:
346: public final byte[] update(byte[] input, int inputOffset,
347: int inputLen) throws IllegalStateException {
348: return this .spi.engineUpdate(input, inputOffset, inputLen);
349: }
350:
351: public final int update(byte[] input, int inputOffset,
352: int inputLen, byte[] output) throws IllegalStateException,
353: ShortBufferException {
354: return this .spi.engineUpdate(input, inputOffset, inputLen,
355: output, 0);
356: }
357:
358: public final int update(byte[] input, int inputOffset,
359: int inputLen, byte[] output, int outputOffset)
360: throws IllegalStateException, ShortBufferException {
361: return this .spi.engineUpdate(input, inputOffset, inputLen,
362: output, outputOffset);
363: }
364:
365: public final byte[] doFinal() throws IllegalStateException,
366: IllegalBlockSizeException, BadPaddingException {
367: return this .spi.engineDoFinal(null, 0, 0);
368: }
369:
370: public final int doFinal(byte[] output, int outputOffset)
371: throws IllegalStateException, IllegalBlockSizeException,
372: ShortBufferException, BadPaddingException {
373: return this .spi.engineDoFinal(null, 0, 0, output, outputOffset);
374: }
375:
376: public final byte[] doFinal(byte[] input)
377: throws IllegalStateException, IllegalBlockSizeException,
378: BadPaddingException {
379: return this .spi.engineDoFinal(input, 0, input.length);
380: }
381:
382: public final byte[] doFinal(byte[] input, int inputOffset,
383: int inputLen) throws IllegalStateException,
384: IllegalBlockSizeException, BadPaddingException {
385: return this .spi.engineDoFinal(input, inputOffset, inputLen);
386: }
387:
388: public final int doFinal(byte[] input, int inputOffset,
389: int inputLen, byte[] output) throws IllegalStateException,
390: ShortBufferException, IllegalBlockSizeException,
391: BadPaddingException {
392: return this .spi.engineDoFinal(input, inputOffset, inputLen,
393: output, 0);
394: }
395:
396: public final int doFinal(byte[] input, int inputOffset,
397: int inputLen, byte[] output, int outputOffset)
398: throws IllegalStateException, ShortBufferException,
399: IllegalBlockSizeException, BadPaddingException {
400: return this .spi.engineDoFinal(input, inputOffset, inputLen,
401: output, outputOffset);
402: }
403:
404: public final void init(int opmode, Certificate certificate)
405: throws InvalidKeyException {
406: this .init(opmode, certificate, new SecureRandom());
407: isInitialized = true;
408: }
409:
410: public final void init(int opmode, Certificate certificate,
411: SecureRandom random) throws InvalidKeyException {
412: /*
413: * Snipped from the JCE API, will be removed when finished.
414: *
415: * Initializes this cipher with the public key from the given
416: * certificate and a source of randomness.
417: *
418: * The cipher is initialized for one of the following four
419: * operations: encryption, decryption, key wrapping or key
420: * unwrapping, depending on the value of opmode.
421: *
422: * If the certificate is of type X.509 and has a key usage
423: * extension field marked as critical, and the value of the key
424: * usage extension field implies that the public key in the
425: * certificate and its corresponding private key are not supposed
426: * to be used for the operation represented by the value of opmode,
427: * an InvalidKeyException is thrown.
428: *
429: * If this cipher requires any algorithm parameters that cannot be
430: * derived from the public key in the given certificate, the
431: * underlying cipher implementation is supposed to generate the
432: * required parameters itself (using provider-specific default or
433: * random values) if it is being initialized for encryption or key
434: * wrapping, and raise an InvalidKeyException if it is being
435: * initialized for decryption or key unwrapping. The generated
436: * parameters can be retrieved using engineGetParameters or
437: * engineGetIV (if the parameter is an IV).
438: *
439: * If this cipher (including its underlying feedback or padding
440: * scheme) requires any random bytes (e.g., for parameter
441: * generation), it will get them from random.
442: *
443: * Note that when a Cipher object is initialized, it loses all
444: * previously-acquired state. In other words, initializing a Cipher
445: * is equivalent to creating a new instance of that Cipher and
446: * initializing it.
447: * Parameters:
448: * opmode - the operation mode of this cipher (this is one of the
449: * following: ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE or UNWRAP_MODE)
450: * certificate - the certificate
451: * random - the source of randomness
452: * Throws:
453: * java.security.InvalidKeyException - if the public key in the
454: * given certificate is inappropriate for initializing this cipher,
455: * or this cipher is being initialized for decryption or unwrapping
456: * keys and requires algorithm parameters that cannot be determined
457: * from the public key in the given certificate, or the keysize of
458: * the public key in the given certificate has a keysize that exceeds
459: * the maximum allowable keysize (as determined by the configured
460: * jurisdiction policy files).
461: */
462: if (certificate instanceof X509Certificate) {
463: // Get the extensions
464: Iterator it = ((X509Certificate) certificate)
465: .getCriticalExtensionOIDs().iterator();
466: // Check to see if key usage is marked critical
467: // go through all oids and check for OID = 2.5.29.15
468:
469: // Check whether the opmode corresponds to the key usage
470: boolean[] boa = ((X509Certificate) certificate)
471: .getKeyUsage();
472: /* The ASN.1 of KeyUsage is
473: * KeyUsage ::= BIT STRING {
474: * digitalSignature (0),
475: * nonRepudiation (1),
476: * keyEncipherment (2),
477: * dataEncipherment (3),
478: * keyAgreement (4),
479: * keyCertSign (5),
480: * cRLSign (6),
481: * encipherOnly (7),
482: * decipherOnly (8) }
483: */
484: }
485: PublicKey pk = certificate.getPublicKey();
486: this .spi.engineInit(opmode, pk, random);
487: isInitialized = true;
488: }
489:
490: public final byte[] wrap(java.security.Key key)
491: throws IllegalStateException, IllegalBlockSizeException,
492: InvalidKeyException {
493: if (!isInitialized)
494: throw new IllegalStateException("Cipher not initialized!");
495:
496: return this .spi.engineWrap(key);
497: }
498:
499: public final Key unwrap(byte[] wrappedKey,
500: String wrappedKeyAlgorithm, int wrappedKeyType)
501: throws IllegalStateException, InvalidKeyException,
502: NoSuchAlgorithmException {
503: if (!isInitialized)
504: throw new IllegalStateException("Cipher not initialized!");
505:
506: return this.spi.engineUnwrap(wrappedKey, wrappedKeyAlgorithm,
507: wrappedKeyType);
508: }
509: }
|