/* $Id: Cipher.java,v 1.1 2004/01/21 15:26:53 rgrimm Exp $
*
* Copyright (C) 1995-2000 The Cryptix Foundation Limited.
* All rights reserved.
*
* Use, modification, copying and distribution of this software is subject
* the terms and conditions of the Cryptix General Licence. You should have
* received a copy of the Cryptix General Licence along with this library;
* if not, you can download a copy from http://www.cryptix.org/ .
*/
package javax.crypto;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Iterator;
// XXX TODO: implement state
/**
* @version $Revision: 1.1 $
* @author Jeroen C. van Gelderen (gelderen@cryptix.org)
* @author Paul Waserbrot (pw@cryptix.org)
*/
public class Cipher
{
// Static variables and constants
//...........................................................................
/** Constants used for the init() methods */
public static final int
ENCRYPT_MODE = 1,
DECRYPT_MODE = 2,
PRIVATE_KEY = 3,
PUBLIC_KEY = 4,
SECRET_KEY = 5,
UNWRAP_MODE = 6,
WRAP_MODE = 7;
// Instance variables
//...........................................................................
/** The Cipher implementation we wrap */
private final CipherSpi spi;
/** Our provider */
private final Provider provider;
/** Our name */
private final String transformation;
/** Our mechanism */
// FIXME: I need to check something, to see if mechanism= null or not.
// Until then, make it null... (pw)
private final ExemptionMechanism mechanism = null;
private boolean isInitialized = false;
// Constructor
//...........................................................................
/**
* Constructs a Cipher wrapping the given cipherSpi.
* <p>
* This method should be private, but is not for backward compatibility.
*/
protected
Cipher(CipherSpi cipherSpi, Provider provider, String transformation)
{
this.spi = cipherSpi;
this.provider = provider;
this.transformation = transformation;
}
public String toString()
{
return "Cipher object: " + this.transformation;
// SHA1withRSA<initialized for verifying>
}
// FIXME:
// 1. Clean up and
// 2. check for compatibility.
// 3. Fix ignored exceptions
// 4. Possibly merge with Support.java
private static Object[]
getCipherImplementation(String transformation, Provider p)
throws NoSuchPaddingException
{
//
// Extract name components from 'transformation'
//
String part_alg;
String part_mode = "//"; // Default is invalid name
String part_pad = "//";
int index_1 = transformation.indexOf('/');
int index_2 = transformation.indexOf('/', index_1+1);
if(index_1==-1)
part_alg = transformation;
else if(index_2==-1)
return null;
else
{
part_alg = transformation.substring(0, index_1);
part_mode = transformation.substring(index_1+1, index_2);
part_pad = transformation.substring(index_2+1);
}
//
// Try and get a class instance
//
try
{
CipherSpi spi;
String class_name;
Object[] res = new Object[2];
res[1] = p;
class_name = Support.getClassName("Cipher", transformation, p);
if(class_name!=null)
{
res[0] = Class.forName(class_name).newInstance();
return res;
}
class_name = Support.getClassName("Cipher",
part_alg + "/" + part_mode, p);
if(class_name!=null)
{
spi = (CipherSpi)Class.forName(class_name).newInstance();
spi.engineSetPadding(part_pad);
res[0] = spi;
return res;
}
class_name = Support.getClassName("Cipher",
part_alg + "//" + part_alg, p);
if(class_name!=null)
{
spi = (CipherSpi)Class.forName(class_name).newInstance();
spi.engineSetMode(part_mode);
res[0] = spi;
return res;
}
class_name = Support.getClassName("Cipher", part_alg, p);
if(class_name!=null)
{
spi = (CipherSpi)Class.forName(class_name).newInstance();
spi.engineSetMode(part_mode);
spi.engineSetPadding(part_pad);
res[0] = spi;
return res;
}
}
catch(NoSuchAlgorithmException e)
{
}
catch(LinkageError e)
{
// FIXME: Throw a RuntimeException(?) with a sensible message????
}
catch(ClassNotFoundException e)
{
// FIXME: Throw a RuntimeException(?) with a sensible message????
}
catch(InstantiationException e)
{
// FIXME: Throw a RuntimeException(?) with a sensible message????
}
catch(IllegalAccessException e)
{
// FIXME: Throw a RuntimeException(?) with a sensible message????
}
return null;
}
/**
* Creates a Cipher that implements the given transformation.
* <p>
* This call will search all installed Providers in preference order and
* returns the first matching transformation.
* <p>
* Transformation is specified as the triple "Algorithm/Mode/Padding".
* We currently do not support transformation of type "Algorithm" with
* defaults for mode and padding.
*
* @param transformation
* The requested transformation.
* @returns A Cipher implementing the requested transformation.
* @throws NoSuchAlgorithmException
* If the given Algorithm/Mode/<any> combination cannot be found.
* @throws NoSuchPaddingException
* If the given <any>/<any>/Padding cannot be found.
*/
public static Cipher getInstance(String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException
{
Provider[] providers = Security.getProviders();
if( (providers==null) || (providers.length==0) )
throw new NoSuchAlgorithmException("No providers installed");
for(int i=0; i<providers.length; i++)
{
Object[] res = getCipherImplementation(transformation,
providers[i]);
if(res != null)
return new Cipher((CipherSpi)res[0],
(Provider)res[1],
transformation);
}
throw new NoSuchAlgorithmException(
"Algorithm not found. [Cipher." + transformation + "]");
}
/**
* Creates a Cipher that implements the given transformation.
* <p>
* This call searches the given provider only.
* <p>
* Transformation is specified as the triple "Algorithm/Mode/Padding".
* We currently do not support transformation of type "Algorithm" with
* defaults for mode and padding.
*
* @param transformation
* The requested transformation.
* @returns A Cipher implementing the requested transformation.
* @throws NoSuchAlgorithmException
* If the given Algorithm/Mode/<any> combination cannot be found.
* @throws NoSuchPaddingException
* If the given <any>/<any>/Padding cannot be found.
* @throws NoSuchProviderException
* If the given provider is not installed.
*/
public static Cipher getInstance(String transformation, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException
{
Provider p = Security.getProvider(provider);
if(p==null)
throw new NoSuchProviderException(
"Provider not found. [" + provider + "]");
Object[] res = getCipherImplementation(transformation, p);
if(res != null)
return new Cipher((CipherSpi)res[0],
(Provider)res[1],
transformation);
throw new NoSuchAlgorithmException(
"Algorithm not found. [Cipher." + transformation + "]");
}
/**
* Returns this Cipher's Provider.
*/
public final Provider getProvider()
{
return this.provider;
}
/**
* Returns the name of the transformation implemented by this Cipher.
*/
public final String getAlgorithm()
{
return this.transformation;
}
/**
* Returns the block size if this Cipher, or 0 if the underlying
* Cipher doesn't (yet) have a block size.
*/
public final int getBlockSize()
{
return this.spi.engineGetBlockSize();
}
/**
* Returns the maximum number of bytes that the next update() or
* doFinal() operation can return, given the length of the input.
* <p>
* This basically returns the sum of buffered data, padding, and input.
*/
public final int getOutputSize(int inputLen)
throws IllegalStateException
{
if(inputLen < 0)
throw new IllegalArgumentException("Input size must be >= 0");
return this.spi.engineGetOutputSize(inputLen);
}
/**
* Returns the IV associated with this Cipher, or null if this Cipher
* doesn't (yet) have one.
* <p>
* This is a rather brain damaged convenience method, because there is
* no corresponding setIV method (you need to use the ParameterSpec crap
* instead).
*/
public final byte[] getIV()
{
return this.spi.engineGetIV();
}
public final ExemptionMechanism getExemptionMechanism()
{
return this.mechanism;
}
public final AlgorithmParameters getParameters()
{
return this.spi.engineGetParameters();
}
public final void init(int opmode, Key key)
throws InvalidKeyException
{
this.spi.engineInit(opmode, key, new SecureRandom());
isInitialized = true;
}
public final void init(int opmode, Key key, SecureRandom random)
throws InvalidKeyException
{
this.spi.engineInit(opmode, key, random);
isInitialized = true;
}
public final void init(int opmode, Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
this.spi.engineInit(opmode, key, params, new SecureRandom());
isInitialized = true;
}
public final void
init(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
this.spi.engineInit(opmode, key, params, random);
isInitialized = true;
}
public final void init(int opmode, Key key, AlgorithmParameters params)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
this.spi.engineInit(opmode, key, params, new SecureRandom());
isInitialized = true;
}
public final void init(int opmode, Key key, AlgorithmParameters params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
this.spi.engineInit(opmode, key, params, random);
isInitialized = true;
}
public final byte[] update(byte[] input)
throws IllegalStateException
{
if(input == null)
throw new IllegalArgumentException();
return this.spi.engineUpdate(input, 0, input.length);
}
public final byte[] update(byte[] input, int inputOffset, int inputLen)
throws IllegalStateException
{
return this.spi.engineUpdate(input, inputOffset, inputLen);
}
public final int
update(byte[] input, int inputOffset, int inputLen, byte[] output)
throws IllegalStateException, ShortBufferException
{
return this.spi.engineUpdate(input, inputOffset, inputLen, output, 0);
}
public final int
update(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws IllegalStateException, ShortBufferException
{
return this.spi.engineUpdate(input, inputOffset, inputLen,
output, outputOffset);
}
public final byte[] doFinal()
throws IllegalStateException, IllegalBlockSizeException, BadPaddingException
{
return this.spi.engineDoFinal(null, 0, 0);
}
public final int
doFinal(byte[] output, int outputOffset)
throws IllegalStateException, IllegalBlockSizeException,
ShortBufferException, BadPaddingException
{
return this.spi.engineDoFinal(null, 0, 0, output, outputOffset);
}
public final byte[]
doFinal(byte[] input)
throws IllegalStateException, IllegalBlockSizeException, BadPaddingException
{
return this.spi.engineDoFinal(input, 0, input.length);
}
public final byte[]
doFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalStateException, IllegalBlockSizeException, BadPaddingException
{
return this.spi.engineDoFinal(input, inputOffset, inputLen);
}
public final int
doFinal(byte[] input, int inputOffset, int inputLen, byte[] output)
throws IllegalStateException, ShortBufferException,
IllegalBlockSizeException, BadPaddingException
{
return this.spi.engineDoFinal(input, inputOffset, inputLen, output, 0);
}
public final int
doFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws IllegalStateException, ShortBufferException,
IllegalBlockSizeException, BadPaddingException
{
return this.spi.engineDoFinal(input, inputOffset, inputLen, output, outputOffset);
}
public final void
init(int opmode, Certificate certificate)
throws InvalidKeyException
{
this.init(opmode, certificate, new SecureRandom());
isInitialized = true;
}
public final void
init(int opmode, Certificate certificate, SecureRandom random)
throws InvalidKeyException
{
/*
* Snipped from the JCE API, will be removed when finished.
*
* Initializes this cipher with the public key from the given
* certificate and a source of randomness.
*
* The cipher is initialized for one of the following four
* operations: encryption, decryption, key wrapping or key
* unwrapping, depending on the value of opmode.
*
* If the certificate is of type X.509 and has a key usage
* extension field marked as critical, and the value of the key
* usage extension field implies that the public key in the
* certificate and its corresponding private key are not supposed
* to be used for the operation represented by the value of opmode,
* an InvalidKeyException is thrown.
*
* If this cipher requires any algorithm parameters that cannot be
* derived from the public key in the given certificate, the
* underlying cipher implementation is supposed to generate the
* required parameters itself (using provider-specific default or
* random values) if it is being initialized for encryption or key
* wrapping, and raise an InvalidKeyException if it is being
* initialized for decryption or key unwrapping. The generated
* parameters can be retrieved using engineGetParameters or
* engineGetIV (if the parameter is an IV).
*
* If this cipher (including its underlying feedback or padding
* scheme) requires any random bytes (e.g., for parameter
* generation), it will get them from random.
*
* Note that when a Cipher object is initialized, it loses all
* previously-acquired state. In other words, initializing a Cipher
* is equivalent to creating a new instance of that Cipher and
* initializing it.
* Parameters:
* opmode - the operation mode of this cipher (this is one of the
* following: ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE or UNWRAP_MODE)
* certificate - the certificate
* random - the source of randomness
* Throws:
* java.security.InvalidKeyException - if the public key in the
* given certificate is inappropriate for initializing this cipher,
* or this cipher is being initialized for decryption or unwrapping
* keys and requires algorithm parameters that cannot be determined
* from the public key in the given certificate, or the keysize of
* the public key in the given certificate has a keysize that exceeds
* the maximum allowable keysize (as determined by the configured
* jurisdiction policy files).
*/
if (certificate instanceof X509Certificate) {
// Get the extensions
Iterator it = ((X509Certificate)certificate).getCriticalExtensionOIDs().iterator();
// Check to see if key usage is marked critical
// go through all oids and check for OID = 2.5.29.15
// Check whether the opmode corresponds to the key usage
boolean [] boa = ((X509Certificate)certificate).getKeyUsage();
/* The ASN.1 of KeyUsage is
* KeyUsage ::= BIT STRING {
* digitalSignature (0),
* nonRepudiation (1),
* keyEncipherment (2),
* dataEncipherment (3),
* keyAgreement (4),
* keyCertSign (5),
* cRLSign (6),
* encipherOnly (7),
* decipherOnly (8) }
*/
}
PublicKey pk = certificate.getPublicKey();
this.spi.engineInit(opmode, pk, random);
isInitialized = true;
}
public final byte[]
wrap(java.security.Key key)
throws IllegalStateException, IllegalBlockSizeException,
InvalidKeyException
{
if (!isInitialized)
throw new IllegalStateException("Cipher not initialized!");
return this.spi.engineWrap(key);
}
public final Key
unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)
throws IllegalStateException, InvalidKeyException,
NoSuchAlgorithmException
{
if (!isInitialized)
throw new IllegalStateException("Cipher not initialized!");
return this.spi.engineUnwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
}