javax.crypto.Cipher Maven / Gradle / Ivy
Show all versions of android-all Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.crypto;
import java.nio.ByteBuffer;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Provider.Service;
import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Set;
import org.apache.harmony.crypto.internal.NullCipherSpi;
import org.apache.harmony.security.fortress.Engine;
/**
* This class provides access to implementations of cryptographic ciphers for
* encryption and decryption. Cipher classes can not be instantiated directly,
* one has to call the Cipher's {@code getInstance} method with the name of a
* requested transformation, optionally with a provider. A transformation
* specifies an operation (or a set of operations) as a string in the form:
*
* - "algorithm/mode/padding"
or
* - "algorithm"
*
* algorithm is the name of a cryptographic algorithm, mode is the
* name of a feedback mode and padding is the name of a padding scheme.
* If mode and/or padding values are omitted, provider specific
* default values will be used.
*
* A valid transformation would be:
*
* {@code Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");}
*
* When a block cipher is requested in stream cipher mode, the number of bits
* to be processed at a time can be optionally specified by appending it to the
* mode name. e.g. "AES/CFB8/NoPadding". If no number is specified, a
* provider specific default value is used.
*/
public class Cipher {
/**
* Constant for decryption operation mode.
*/
public static final int DECRYPT_MODE = 2;
/**
* Constant for encryption operation mode.
*/
public static final int ENCRYPT_MODE = 1;
/**
* Constant indicating that the key to be unwrapped is a private key.
*/
public static final int PRIVATE_KEY = 2;
/**
* Constant indicating that the key to be unwrapped is a public key.
*/
public static final int PUBLIC_KEY = 1;
/**
* Constant indicating that the key to be unwrapped is a secret key.
*/
public static final int SECRET_KEY = 3;
/**
* Constant for key unwrapping operation mode.
*/
public static final int UNWRAP_MODE = 4;
/**
* Constant for key wrapping operation mode.
*/
public static final int WRAP_MODE = 3;
private int mode;
/** Items that need to be set on the Cipher instance. */
private enum NeedToSet {
NONE, MODE, PADDING, BOTH,
};
/**
* The service name.
*/
private static final String SERVICE = "Cipher";
/**
* Used to access common engine functionality.
*/
private static final Engine ENGINE = new Engine(SERVICE);
/** The attribute used for supported paddings. */
private static final String ATTRIBUTE_PADDINGS = "SupportedPaddings";
/** The attribute used for supported modes. */
private static final String ATTRIBUTE_MODES = "SupportedModes";
/**
* The provider.
*/
private Provider provider;
/**
* The provider specified when instance created.
*/
private final Provider specifiedProvider;
/**
* The SPI implementation.
*/
private CipherSpi spiImpl;
/**
* The SPI implementation.
*/
private final CipherSpi specifiedSpi;
/**
* The transformation.
*/
private final String transformation;
/**
* The transformation split into parts.
*/
private final String[] transformParts;
/**
* Lock held while the SPI is initializing.
*/
private final Object initLock = new Object();
private static SecureRandom secureRandom;
/**
* Creates a new Cipher instance.
*
* @param cipherSpi
* the implementation delegate of the cipher.
* @param provider
* the provider of the implementation of this cipher.
* @param transformation
* the name of the transformation that this cipher performs.
* @throws NullPointerException
* if either cipherSpi is {@code null} or provider is {@code
* null} and {@code cipherSpi} is a {@code NullCipherSpi}.
*/
protected Cipher(CipherSpi cipherSpi, Provider provider, String transformation) {
if (cipherSpi == null) {
throw new NullPointerException("cipherSpi == null");
}
if (!(cipherSpi instanceof NullCipherSpi) && provider == null) {
throw new NullPointerException("provider == null");
}
this.specifiedProvider = provider;
this.specifiedSpi = cipherSpi;
this.transformation = transformation;
this.transformParts = null;
}
private Cipher(String transformation, String[] transformParts, Provider provider) {
this.transformation = transformation;
this.transformParts = transformParts;
this.specifiedProvider = provider;
this.specifiedSpi = null;
}
/**
* Creates a new Cipher for the specified transformation. The installed
* providers are searched in order for an implementation of the specified
* transformation. The first found provider providing the transformation is
* used to create the cipher. If no provider is found an exception is
* thrown.
*
* @param transformation
* the name of the transformation to create a cipher for.
* @return a cipher for the requested transformation.
* @throws NoSuchAlgorithmException
* if no installed provider can provide the
* transformation, or it is {@code null}, empty or in an
* invalid format.
* @throws NoSuchPaddingException
* if no installed provider can provide the padding scheme in
* the transformation.
*/
public static final Cipher getInstance(String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException {
return getCipher(transformation, null);
}
/**
* Creates a new cipher for the specified transformation provided by the
* specified provider.
*
* @param transformation
* the name of the transformation to create a cipher for.
* @param provider
* the name of the provider to ask for the transformation.
* @return a cipher for the requested transformation.
* @throws NoSuchAlgorithmException
* if the specified provider can not provide the
* transformation, or it is {@code null}, empty or in an
* invalid format.
* @throws NoSuchProviderException
* if no provider with the specified name can be found.
* @throws NoSuchPaddingException
* if the requested padding scheme in the transformation
* is not available.
* @throws IllegalArgumentException
* if the specified provider is {@code null}.
*/
public static final Cipher getInstance(String transformation,
String provider) throws NoSuchAlgorithmException,
NoSuchProviderException, NoSuchPaddingException {
if (provider == null) {
throw new IllegalArgumentException("provider == null");
}
Provider p = Security.getProvider(provider);
if (p == null) {
throw new NoSuchProviderException("Provider not available: " + provider);
}
return getInstance(transformation, p);
}
/**
* Creates a new cipher for the specified transformation. The
* {@code provider} supplied does not have to be registered.
*
* @param transformation
* the name of the transformation to create a cipher for.
* @param provider
* the provider to ask for the transformation.
* @return a cipher for the requested transformation.
* @throws NoSuchAlgorithmException
* if the specified provider can not provide the
* transformation, or it is {@code null}, empty or in an
* invalid format.
* @throws NoSuchPaddingException
* if the requested padding scheme in the transformation
* is not available.
* @throws IllegalArgumentException
* if the provider is {@code null}.
*/
public static final Cipher getInstance(String transformation,
Provider provider) throws NoSuchAlgorithmException,
NoSuchPaddingException {
if (provider == null) {
throw new IllegalArgumentException("provider == null");
}
return getCipher(transformation, provider);
}
private static NoSuchAlgorithmException invalidTransformation(String transformation)
throws NoSuchAlgorithmException {
throw new NoSuchAlgorithmException("Invalid transformation: " + transformation);
}
/**
* Create a Cipher instance but don't choose a CipherSpi until we have more
* information.
*/
private static Cipher getCipher(String transformation, Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException {
if (transformation == null || transformation.isEmpty()) {
throw invalidTransformation(transformation);
}
String[] transformParts = checkTransformation(transformation);
if (tryCombinations(null, provider, transformParts) == null) {
if (provider == null) {
throw new NoSuchAlgorithmException("No provider found for " + transformation);
} else {
throw new NoSuchAlgorithmException("Provider " + provider.getName()
+ " does not provide " + transformation);
}
}
return new Cipher(transformation, transformParts, provider);
}
private static String[] checkTransformation(String transformation)
throws NoSuchAlgorithmException {
// ignore an extra prefix / characters such as in
// "/DES/CBC/PKCS5Padding" http://b/3387688
if (transformation.startsWith("/")) {
transformation = transformation.substring(1);
}
// 'transformation' should be of the form "algorithm/mode/padding".
String[] pieces = transformation.split("/");
if (pieces.length > 3) {
throw invalidTransformation(transformation);
}
// Empty or missing pieces are represented by null.
String[] result = new String[3];
for (int i = 0; i < pieces.length; ++i) {
String piece = pieces[i].trim();
if (!piece.isEmpty()) {
result[i] = piece;
}
}
// You MUST specify an algorithm.
if (result[0] == null) {
throw invalidTransformation(transformation);
}
if (!(result[1] == null && result[2] == null) && (result[1] == null || result[2] == null)) {
throw invalidTransformation(transformation);
}
return result;
}
/**
* Makes sure a CipherSpi that matches this type is selected.
*/
private CipherSpi getSpi(Key key) {
if (specifiedSpi != null) {
return specifiedSpi;
}
synchronized (initLock) {
if (spiImpl != null && key == null) {
return spiImpl;
}
final Engine.SpiAndProvider sap = tryCombinations(key, specifiedProvider,
transformParts);
if (sap == null) {
throw new ProviderException("No provider for " + transformation);
}
spiImpl = (CipherSpi) sap.spi;
provider = sap.provider;
return spiImpl;
}
}
/**
* Convenience call when the Key is not available.
*/
private CipherSpi getSpi() {
return getSpi(null);
}
/**
* Try all combinations of mode strings:
*
*
* [cipher]/[mode]/[padding]
* [cipher]/[mode]
* [cipher]//[padding]
* [cipher]
*
*/
private static Engine.SpiAndProvider tryCombinations(Key key, Provider provider,
String[] transformParts) {
Engine.SpiAndProvider sap = null;
if (transformParts[1] != null && transformParts[2] != null) {
sap = tryTransform(key, provider, transformParts[0] + "/" + transformParts[1] + "/"
+ transformParts[2], transformParts, NeedToSet.NONE);
if (sap != null) {
return sap;
}
}
if (transformParts[1] != null) {
sap = tryTransform(key, provider, transformParts[0] + "/" + transformParts[1],
transformParts, NeedToSet.PADDING);
if (sap != null) {
return sap;
}
}
if (transformParts[2] != null) {
sap = tryTransform(key, provider, transformParts[0] + "//" + transformParts[2],
transformParts, NeedToSet.MODE);
if (sap != null) {
return sap;
}
}
return tryTransform(key, provider, transformParts[0], transformParts, NeedToSet.BOTH);
}
private static Engine.SpiAndProvider tryTransform(Key key, Provider provider, String transform,
String[] transformParts, NeedToSet type) {
if (provider != null) {
Provider.Service service = provider.getService(SERVICE, transform);
if (service == null) {
return null;
}
return tryTransformWithProvider(key, transformParts, type, service);
}
ArrayList services = ENGINE.getServices(transform);
if (services == null) {
return null;
}
for (Provider.Service service : services) {
Engine.SpiAndProvider sap = tryTransformWithProvider(key, transformParts, type, service);
if (sap != null) {
return sap;
}
}
return null;
}
private static Engine.SpiAndProvider tryTransformWithProvider(Key key, String[] transformParts,
NeedToSet type, Provider.Service service) {
try {
if (key != null && !service.supportsParameter(key)) {
return null;
}
/*
* Check to see if the Cipher even supports the attributes before
* trying to instantiate it.
*/
if (!matchAttribute(service, ATTRIBUTE_MODES, transformParts[1])
|| !matchAttribute(service, ATTRIBUTE_PADDINGS, transformParts[2])) {
return null;
}
Engine.SpiAndProvider sap = ENGINE.getInstance(service, null);
if (sap.spi == null || sap.provider == null) {
return null;
}
if (!(sap.spi instanceof CipherSpi)) {
return null;
}
CipherSpi spi = (CipherSpi) sap.spi;
if (((type == NeedToSet.MODE) || (type == NeedToSet.BOTH))
&& (transformParts[1] != null)) {
spi.engineSetMode(transformParts[1]);
}
if (((type == NeedToSet.PADDING) || (type == NeedToSet.BOTH))
&& (transformParts[2] != null)) {
spi.engineSetPadding(transformParts[2]);
}
return sap;
} catch (NoSuchAlgorithmException ignored) {
} catch (NoSuchPaddingException ignored) {
}
return null;
}
/**
* If the attribute listed exists, check that it matches the regular
* expression.
*/
private static boolean matchAttribute(Service service, String attr, String value) {
if (value == null) {
return true;
}
final String pattern = service.getAttribute(attr);
if (pattern == null) {
return true;
}
final String valueUc = value.toUpperCase(Locale.US);
return valueUc.matches(pattern.toUpperCase(Locale.US));
}
/**
* Returns the provider of this cipher instance.
*
* @return the provider of this cipher instance.
*/
public final Provider getProvider() {
getSpi();
return provider;
}
/**
* Returns the name of the algorithm of this cipher instance.
*
* This is the name of the transformation argument used in the
* {@code getInstance} call creating this object.
*
* @return the name of the algorithm of this cipher instance.
*/
public final String getAlgorithm() {
return transformation;
}
/**
* Returns this ciphers block size (in bytes).
*
* @return this ciphers block size.
*/
public final int getBlockSize() {
return getSpi().engineGetBlockSize();
}
/**
* Returns the length in bytes an output buffer needs to be when this cipher
* is updated with {@code inputLen} bytes.
*
* @param inputLen
* the number of bytes of the input.
* @return the output buffer length for the input length.
* @throws IllegalStateException
* if this cipher instance is in an invalid state.
*/
public final int getOutputSize(int inputLen) {
if (mode == 0) {
throw new IllegalStateException("Cipher has not yet been initialized");
}
return getSpi().engineGetOutputSize(inputLen);
}
/**
* Returns the initialization vector for this cipher instance.
*
* @return the initialization vector for this cipher instance.
*/
public final byte[] getIV() {
return getSpi().engineGetIV();
}
/**
* Returns the parameters that where used to create this cipher instance.
*
* These may be a the same parameters that were used to create this cipher
* instance, or may be a combination of default and random parameters,
* depending on the underlying cipher implementation.
*
* @return the parameters that where used to create this cipher instance, or
* {@code null} if this cipher instance does not have any
* parameters.
*/
public final AlgorithmParameters getParameters() {
return getSpi().engineGetParameters();
}
/**
* Returns the exemption mechanism associated with this cipher.
*
* @return currently {@code null}
*/
public final ExemptionMechanism getExemptionMechanism() {
//FIXME implement getExemptionMechanism
// try {
// return ExemptionMechanism.getInstance(transformation, provider);
// } catch (NoSuchAlgorithmException e) {
return null;
// }
}
private void checkMode(int mode) {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE && mode != UNWRAP_MODE
&& mode != WRAP_MODE) {
throw new InvalidParameterException("Invalid mode: " + mode);
}
}
/**
* Initializes this cipher instance with the specified key.
*
* The cipher is initialized for the specified operational mode (one of:
* encryption, decryption, key wrapping or key unwrapping) depending on
* {@code opmode}.
*
* If this cipher instance needs any algorithm parameters or random values
* that the specified key can not provide, the underlying implementation of
* this cipher is supposed to generate the required parameters (using its
* provider or random values).
*
* When a cipher instance is initialized by a call to any of the {@code
* init} methods, the state of the instance is overridden, meaning that it
* is equivalent to creating a new instance and calling its {@code init}
* method.
*
* @param opmode
* the operation this cipher instance should be initialized for
* (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
* WRAP_MODE} or {@code UNWRAP_MODE}).
* @param key
* the input key for the operation.
* @throws InvalidKeyException
* if the specified key can not be used to initialize this
* cipher instance.
*/
public final void init(int opmode, Key key) throws InvalidKeyException {
if (secureRandom == null) {
// In theory it might be thread-unsafe but in the given case it's OK
// since it does not matter which SecureRandom instance is passed
// to the init()
secureRandom = new SecureRandom();
}
init(opmode, key, secureRandom);
}
/**
* Initializes this cipher instance with the specified key and a source of
* randomness.
*
* The cipher is initialized for the specified operational mode (one of:
* encryption, decryption, key wrapping or key unwrapping) depending on
* {@code opmode}.
*
* If this cipher instance needs any algorithm parameters or random values
* that the specified key can not provide, the underlying implementation of
* this cipher is supposed to generate the required parameters (using its
* provider or random values). Random values are generated using {@code
* random};
*
* When a cipher instance is initialized by a call to any of the {@code
* init} methods, the state of the instance is overridden, means it is
* equivalent to creating a new instance and calling it {@code init} method.
*
* @param opmode
* the operation this cipher instance should be initialized for
* (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
* WRAP_MODE} or {@code UNWRAP_MODE}).
* @param key
* the input key for the operation.
* @param random
* the source of randomness to use.
* @throws InvalidKeyException
* if the specified key can not be used to initialize this
* cipher instance.
* @throws InvalidParameterException
* if the specified opmode is invalid.
*/
public final void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
checkMode(opmode);
// FIXME InvalidKeyException
// if keysize exceeds the maximum allowable keysize
// (jurisdiction policy files)
getSpi(key).engineInit(opmode, key, random);
mode = opmode;
}
/**
* Initializes this cipher instance with the specified key and algorithm
* parameters.
*
* The cipher is initialized for the specified operational mode (one of:
* encryption, decryption, key wrapping or key unwrapping).
*
* If this cipher instance needs any algorithm parameters and {@code params}
* is {@code null}, the underlying implementation of this cipher is supposed
* to generate the required parameters (using its provider or random
* values).
*
* When a cipher instance is initialized by a call to any of the {@code
* init} methods, the state of the instance is overridden, means it is
* equivalent to creating a new instance and calling it {@code init} method.
*
* @param opmode
* the operation this cipher instance should be initialized for
* (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
* WRAP_MODE} or {@code UNWRAP_MODE}).
* @param key
* the input key for the operation.
* @param params
* the algorithm parameters.
* @throws InvalidKeyException
* if the specified key can not be used to initialize this
* cipher instance.
* @throws InvalidAlgorithmParameterException
* it the specified parameters are inappropriate for this
* cipher.
*/
public final void init(int opmode, Key key, AlgorithmParameterSpec params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (secureRandom == null) {
secureRandom = new SecureRandom();
}
init(opmode, key, params, secureRandom);
}
/**
* Initializes this cipher instance with the specified key, algorithm
* parameters and a source of randomness.
*
* The cipher is initialized for the specified operational mode (one of:
* encryption, decryption, key wrapping or key unwrapping) depending on
* {@code opmode}.
*
* If this cipher instance needs any algorithm parameters and {@code params}
* is {@code null}, the underlying implementation of this cipher is supposed
* to generate the required parameters (using its provider or random
* values). Random values are generated using {@code random};
*
* When a cipher instance is initialized by a call to any of the {@code
* init} methods, the state of the instance is overridden, meaning that it
* is equivalent to creating a new instance and calling it {@code init}
* method.
*
* @param opmode
* the operation this cipher instance should be initialized for
* (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
* WRAP_MODE} or {@code UNWRAP_MODE}).
* @param key
* the input key for the operation.
* @param params
* the algorithm parameters.
* @param random
* the source of randomness to use.
* @throws InvalidKeyException
* if the specified key can not be used to initialize this
* cipher instance.
* @throws InvalidAlgorithmParameterException
* it the specified parameters are inappropriate for this
* cipher.
* @throws InvalidParameterException
* if the specified {@code opmode} is invalid.
*/
public final void init(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
checkMode(opmode);
// FIXME InvalidKeyException
// if keysize exceeds the maximum allowable keysize
// (jurisdiction policy files)
// FIXME InvalidAlgorithmParameterException
// cryptographic strength exceed the legal limits
// (jurisdiction policy files)
getSpi(key).engineInit(opmode, key, params, random);
mode = opmode;
}
/**
* Initializes this cipher instance with the specified key and algorithm
* parameters.
*
* The cipher is initialized for the specified operation (one of:
* encryption, decryption, key wrapping or key unwrapping) depending on
* {@code opmode}.
*
* If this cipher instance needs any algorithm parameters and {@code params}
* is {@code null}, the underlying implementation of this cipher is supposed
* to generate the required parameters (using its provider or random
* values).
*
* When a cipher instance is initialized by a call to any of the {@code
* init} methods, the state of the instance is overridden, meaning that it
* is equivalent to creating a new instance and calling it {@code init}
* method.
*
* @param opmode
* the operation this cipher instance should be initialized for
* (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
* WRAP_MODE} or {@code UNWRAP_MODE}).
* @param key
* the input key for the operation.
* @param params
* the algorithm parameters.
* @throws InvalidKeyException
* if the specified key can not be used to initialize this
* cipher instance.
* @throws InvalidAlgorithmParameterException
* it the specified parameters are inappropriate for this
* cipher.
*/
public final void init(int opmode, Key key, AlgorithmParameters params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (secureRandom == null) {
secureRandom = new SecureRandom();
}
init(opmode, key, params, secureRandom);
}
/**
* Initializes this cipher instance with the specified key, algorithm
* parameters and a source of randomness.
*
* The cipher will be initialized for the specified operation (one of:
* encryption, decryption, key wrapping or key unwrapping) depending on
* {@code opmode}.
*
* If this cipher instance needs any algorithm parameters and {@code params}
* is {@code null}, the underlying implementation of this cipher is supposed
* to generate the required parameters (using its provider or random
* values). Random values are generated using {@code random}.
*
* When a cipher instance is initialized by a call to any of the {@code
* init} methods, the state of the instance is overridden, means it is
* equivalent to creating a new instance and calling it {@code init} method.
*
* @param opmode
* the operation this cipher instance should be initialized for
* (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
* WRAP_MODE} or {@code UNWRAP_MODE}).
* @param key
* the input key for the operation.
* @param params
* the algorithm parameters.
* @param random
* the source of randomness to use.
* @throws InvalidKeyException
* if the specified key can not be used to initialize this
* cipher instance.
* @throws InvalidAlgorithmParameterException
* if the specified parameters are inappropriate for this
* cipher.
* @throws InvalidParameterException
* if the specified {@code opmode} is invalid.
*/
public final void init(int opmode, Key key, AlgorithmParameters params,
SecureRandom random) throws InvalidKeyException,
InvalidAlgorithmParameterException {
checkMode(opmode);
// FIXME InvalidKeyException
// if keysize exceeds the maximum allowable keysize
// (jurisdiction policy files)
// FIXME InvalidAlgorithmParameterException
// cryptographic strength exceed the legal limits
// (jurisdiction policy files)
getSpi(key).engineInit(opmode, key, params, random);
mode = opmode;
}
/**
* Initializes this cipher instance with the public key from the specified
* certificate.
*
* The cipher will be initialized for the specified operation (one of:
* encryption, decryption, key wrapping or key unwrapping) depending on
* {@code opmode}.
*
* It the type of the certificate is X.509 and the certificate has a key
* usage extension field marked as critical, the specified {@code
* opmode} has the be enabled for this key, otherwise an {@code
* InvalidKeyException} is thrown.
*
* If this cipher instance needs any algorithm parameters that the key in
* the certificate can not provide, the underlying implementation of this
* cipher is supposed to generate the required parameters (using its
* provider or random values).
*
* When a cipher instance is initialized by a call to any of the {@code
* init} methods, the state of the instance is overridden, means it is
* equivalent to creating a new instance and calling it {@code init} method.
*
* @param opmode
* the operation this cipher instance should be initialized for
* (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
* WRAP_MODE} or {@code UNWRAP_MODE}).
* @param certificate
* the certificate.
* @throws InvalidKeyException
* if the public key in the certificate can not be used to
* initialize this cipher instance.
*/
public final void init(int opmode, Certificate certificate)
throws InvalidKeyException {
if (secureRandom == null) {
secureRandom = new SecureRandom();
}
init(opmode, certificate, secureRandom);
}
/**
* Initializes this cipher instance with the public key from the specified
* certificate and a source of randomness.
*
* The cipher will be initialized for the specified operation (one of:
* encryption, decryption, key wrapping or key unwrapping) depending on
* {@code opmode}.
*
* It the type of the certificate is X.509 and the certificate has a key
* usage extension field marked as critical, the specified {@code
* opmode} has the be enabled for this key, otherwise an {@code
* InvalidKeyException} is thrown.
*
* If this cipher instance needs any algorithm parameters that the key in
* the certificate can not provide, the underlying implementation of this
* cipher is supposed to generate the required parameters (using its
* provider or random values). Random values are generated using {@code
* random}.
*
* When a cipher instance is initialized by a call to any of the {@code
* init} methods, the state of the instance is overridden, means it is
* equivalent to creating a new instance and calling it {@code init} method.
*
* @param opmode
* the operation this cipher instance should be initialized for
* (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code
* WRAP_MODE} or {@code UNWRAP_MODE}).
* @param certificate
* the certificate.
* @param random
* the source of randomness to be used.
* @throws InvalidKeyException
* if the public key in the certificate can not be used to
* initialize this cipher instance.
*/
public final void init(int opmode, Certificate certificate,
SecureRandom random) throws InvalidKeyException {
checkMode(opmode);
if (certificate instanceof X509Certificate) {
Set ce = ((X509Certificate) certificate).getCriticalExtensionOIDs();
boolean critical = false;
if (ce != null && !ce.isEmpty()) {
for (String oid : ce) {
if (oid.equals("2.5.29.15")) { // KeyUsage OID = 2.5.29.15
critical = true;
break;
}
}
if (critical) {
boolean[] keyUsage = ((X509Certificate) certificate).getKeyUsage();
// As specified in RFC 3280:
// Internet X.509 Public Key Infrastructure
// Certificate and Certificate Revocation List (CRL) Profile.
// Section 4.2.1.3 Key Usage
// http://www.ietf.org/rfc/rfc3280.txt
//
// KeyUsage ::= BIT STRING {digitalSignature (0),
// nonRepudiation (1),
// keyEncipherment (2),
// dataEncipherment (3),
// keyAgreement (4),
// keyCertSign (5),
// cRLSign (6),
// encipherOnly (7),
// decipherOnly (8) }
if (keyUsage != null) {
if (opmode == ENCRYPT_MODE && !keyUsage[3]) {
throw new InvalidKeyException("The public key in the certificate "
+ "cannot be used for ENCRYPT_MODE");
} else if (opmode == WRAP_MODE && !keyUsage[2]) {
throw new InvalidKeyException("The public key in the certificate "
+ "cannot be used for WRAP_MODE");
}
}
}
}
}
// FIXME InvalidKeyException
// if keysize exceeds the maximum allowable keysize
// (jurisdiction policy files)
final Key key = certificate.getPublicKey();
getSpi(key).engineInit(opmode, key, random);
mode = opmode;
}
/**
* Continues a multi-part transformation (encryption or decryption). The
* transformed bytes are returned.
*
* @param input
* the input bytes to transform.
* @return the transformed bytes in a new buffer, or {@code null} if the
* input has zero length.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if the input is {@code null}.
*/
public final byte[] update(byte[] input) {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
if (input == null) {
throw new IllegalArgumentException("input == null");
}
if (input.length == 0) {
return null;
}
return getSpi().engineUpdate(input, 0, input.length);
}
/**
* Continues a multi-part transformation (encryption or decryption). The
* transformed bytes are returned.
*
* @param input
* the input bytes to transform.
* @param inputOffset
* the offset in the input to start.
* @param inputLen
* the length of the input to transform.
* @return the transformed bytes in a new buffer, or {@code null} if the
* input has zero length.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if {@code input} is {@code null}, or if {@code inputOffset} and
* {@code inputLen} do not specify a valid chunk in the input
* buffer.
*/
public final byte[] update(byte[] input, int inputOffset, int inputLen) {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
if (input == null) {
throw new IllegalArgumentException("input == null");
}
checkInputOffsetAndCount(input.length, inputOffset, inputLen);
if (input.length == 0) {
return null;
}
return getSpi().engineUpdate(input, inputOffset, inputLen);
}
private static void checkInputOffsetAndCount(int inputArrayLength,
int inputOffset,
int inputLen) {
if ((inputOffset | inputLen) < 0
|| inputOffset > inputArrayLength
|| inputArrayLength - inputOffset < inputLen) {
throw new IllegalArgumentException("input.length=" + inputArrayLength
+ "; inputOffset=" + inputOffset
+ "; inputLen=" + inputLen);
}
}
/**
* Continues a multi-part transformation (encryption or decryption). The
* transformed bytes are stored in the {@code output} buffer.
*
* If the size of the {@code output} buffer is too small to hold the result,
* a {@code ShortBufferException} is thrown. Use
* {@link Cipher#getOutputSize getOutputSize} to check for the size of the
* output buffer.
*
* @param input
* the input bytes to transform.
* @param inputOffset
* the offset in the input to start.
* @param inputLen
* the length of the input to transform.
* @param output
* the output buffer.
* @return the number of bytes placed in output.
* @throws ShortBufferException
* if the size of the {@code output} buffer is too small.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if the input is {@code null}, the output is {@code null}, or
* if {@code inputOffset} and {@code inputLen} do not specify a
* valid chunk in the input buffer.
*/
public final int update(byte[] input, int inputOffset, int inputLen,
byte[] output) throws ShortBufferException {
return update(input, inputOffset, inputLen, output, 0);
}
/**
* Continues a multi-part transformation (encryption or decryption). The
* transformed bytes are stored in the {@code output} buffer.
*
* If the size of the {@code output} buffer is too small to hold the result,
* a {@code ShortBufferException} is thrown. Use
* {@link Cipher#getOutputSize getOutputSize} to check for the size of the
* output buffer.
*
* @param input
* the input bytes to transform.
* @param inputOffset
* the offset in the input to start.
* @param inputLen
* the length of the input to transform.
* @param output
* the output buffer.
* @param outputOffset
* the offset in the output buffer.
* @return the number of bytes placed in output.
* @throws ShortBufferException
* if the size of the {@code output} buffer is too small.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if the input is {@code null}, the output is {@code null}, or
* if {@code inputOffset} and {@code inputLen} do not specify a
* valid chunk in the input buffer.
*/
public final int update(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset) throws ShortBufferException {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
if (input == null) {
throw new IllegalArgumentException("input == null");
}
if (output == null) {
throw new IllegalArgumentException("output == null");
}
if (outputOffset < 0) {
throw new IllegalArgumentException("outputOffset < 0. outputOffset=" + outputOffset);
}
checkInputOffsetAndCount(input.length, inputOffset, inputLen);
if (input.length == 0) {
return 0;
}
return getSpi().engineUpdate(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Continues a multi-part transformation (encryption or decryption). The
* {@code input.remaining()} bytes starting at {@code input.position()} are
* transformed and stored in the {@code output} buffer.
*
* If the {@code output.remaining()} is too small to hold the transformed
* bytes a {@code ShortBufferException} is thrown. Use
* {@link Cipher#getOutputSize getOutputSize} to check for the size of the
* output buffer.
*
* @param input
* the input buffer to transform.
* @param output
* the output buffer to store the result within.
* @return the number of bytes stored in the output buffer.
* @throws ShortBufferException
* if the size of the {@code output} buffer is too small.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if the input buffer and the output buffer are the identical
* object.
*/
public final int update(ByteBuffer input, ByteBuffer output)
throws ShortBufferException {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
if (input == output) {
throw new IllegalArgumentException("input == output");
}
return getSpi().engineUpdate(input, output);
}
/**
* Continues a multi-part transformation (encryption or decryption) with
* Authenticated Additional Data (AAD). AAD may only be added after the
* {@code Cipher} is initialized and before any data is passed to the
* instance.
*
* This is only usable with cipher modes that support Authenticated
* Encryption with Additional Data (AEAD) such as Galois/Counter Mode (GCM).
*
* @param input bytes of AAD to use with the cipher
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if {@code input} is {@code null}
* @throws UnsupportedOperationException if the cipher does not support AEAD
* @since 1.7
*/
public final void updateAAD(byte[] input) {
if (input == null) {
throw new IllegalArgumentException("input == null");
}
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
if (input.length == 0) {
return;
}
getSpi().engineUpdateAAD(input, 0, input.length);
}
/**
* Continues a multi-part transformation (encryption or decryption) with
* Authenticated Additional Data (AAD). AAD may only be added after the
* {@code Cipher} is initialized and before any data is passed to the
* instance.
*
* This is only usable with cipher modes that support Authenticated
* Encryption with Additional Data (AEAD) such as Galois/Counter Mode (GCM).
*
* @param input bytes of AAD to use with the cipher
* @param inputOffset offset within bytes of additional data to add to cipher
* @param inputLen length of bytes of additional data to add to cipher
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if {@code input} is {@code null}, or if {@code inputOffset} and
* {@code inputLen} do not specify a valid chunk in the input
* buffer.
* @throws UnsupportedOperationException if the cipher does not support AEAD
* @since 1.7
*/
public final void updateAAD(byte[] input, int inputOffset, int inputLen) {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
if (input == null) {
throw new IllegalArgumentException("input == null");
}
checkInputOffsetAndCount(input.length, inputOffset, inputLen);
if (input.length == 0) {
return;
}
getSpi().engineUpdateAAD(input, inputOffset, inputLen);
}
/**
* Continues a multi-part transformation (encryption or decryption) with
* Authenticated Additional Data (AAD). AAD may only be added after the
* {@code Cipher} is initialized and before any data is passed to the
* instance.
*
* This is only usable with cipher modes that support Authenticated
* Encryption with Additional Data (AEAD) such as Galois/Counter Mode (GCM).
*
* @param input buffer of AAD to be used
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws UnsupportedOperationException if the cipher does not support AEAD
* @since 1.7
*/
public final void updateAAD(ByteBuffer input) {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException("Cipher is not initialized");
}
if (input == null) {
throw new IllegalArgumentException("input == null");
}
getSpi().engineUpdateAAD(input);
}
/**
* Finishes a multi-part transformation (encryption or decryption).
*
* Processes any bytes that may have been buffered in previous {@code
* update} calls.
*
* @return the final bytes from the transformation.
* @throws IllegalBlockSizeException
* if the size of the resulting bytes is not a multiple of the
* cipher block size.
* @throws BadPaddingException
* if the padding of the data does not match the padding scheme.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
*/
public final byte[] doFinal() throws IllegalBlockSizeException,
BadPaddingException {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
return getSpi().engineDoFinal(null, 0, 0);
}
/**
* Finishes a multi-part transformation (encryption or decryption).
*
* Processes any bytes that may have been buffered in previous {@code
* update} calls.
*
* The final transformed bytes are stored in the {@code output} buffer.
*
* @param output
* the output buffer.
* @param outputOffset
* the offset in the output buffer.
* @return the number of bytes placed in the output buffer.
* @throws IllegalBlockSizeException
* if the size of the resulting bytes is not a multiple of the
* cipher block size.
* @throws ShortBufferException
* if the size of the {@code output} buffer is too small.
* @throws BadPaddingException
* if the padding of the data does not match the padding scheme.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
*/
public final int doFinal(byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
if (outputOffset < 0) {
throw new IllegalArgumentException("outputOffset < 0. outputOffset=" + outputOffset);
}
return getSpi().engineDoFinal(null, 0, 0, output, outputOffset);
}
/**
* Finishes a multi-part transformation (encryption or decryption).
*
* Processes the bytes in {@code input} buffer, and any bytes that have been
* buffered in previous {@code update} calls.
*
* @param input
* the input buffer.
* @return the final bytes from the transformation.
* @throws IllegalBlockSizeException
* if the size of the resulting bytes is not a multiple of the
* cipher block size.
* @throws BadPaddingException
* if the padding of the data does not match the padding scheme.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
*/
public final byte[] doFinal(byte[] input) throws IllegalBlockSizeException,
BadPaddingException {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
return getSpi().engineDoFinal(input, 0, input.length);
}
/**
* Finishes a multi-part transformation (encryption or decryption).
*
* Processes the {@code inputLen} bytes in {@code input} buffer at {@code
* inputOffset}, and any bytes that have been buffered in previous {@code
* update} calls.
*
* @param input
* the input buffer.
* @param inputOffset
* the offset in the input buffer.
* @param inputLen
* the length of the input
* @return the final bytes from the transformation.
* @throws IllegalBlockSizeException
* if the size of the resulting bytes is not a multiple of the
* cipher block size.
* @throws BadPaddingException
* if the padding of the data does not match the padding scheme.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if {@code inputOffset} and {@code inputLen} do not specify an
* valid chunk in the input buffer.
*/
public final byte[] doFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
checkInputOffsetAndCount(input.length, inputOffset, inputLen);
return getSpi().engineDoFinal(input, inputOffset, inputLen);
}
/**
* Finishes a multi-part transformation (encryption or decryption).
*
* Processes the {@code inputLen} bytes in {@code input} buffer at {@code
* inputOffset}, and any bytes that have been buffered in previous {@code
* update} calls.
*
* @param input
* the input buffer.
* @param inputOffset
* the offset in the input buffer.
* @param inputLen
* the length of the input.
* @param output
* the output buffer for the transformed bytes.
* @return the number of bytes placed in the output buffer.
* @throws ShortBufferException
* if the size of the {@code output} buffer is too small.
* @throws IllegalBlockSizeException
* if the size of the resulting bytes is not a multiple of the
* cipher block size.
* @throws BadPaddingException
* if the padding of the data does not match the padding scheme.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if {@code inputOffset} and {@code inputLen} do not specify an
* valid chunk in the input buffer.
*/
public final int doFinal(byte[] input, int inputOffset, int inputLen,
byte[] output) throws ShortBufferException,
IllegalBlockSizeException, BadPaddingException {
return doFinal(input, inputOffset, inputLen, output, 0);
}
/**
* Finishes a multi-part transformation (encryption or decryption).
*
* Processes the {@code inputLen} bytes in {@code input} buffer at {@code
* inputOffset}, and any bytes that have been buffered in previous {@code
* update} calls.
*
* @param input
* the input buffer.
* @param inputOffset
* the offset in the input buffer.
* @param inputLen
* the length of the input.
* @param output
* the output buffer for the transformed bytes.
* @param outputOffset
* the offset in the output buffer.
* @return the number of bytes placed in the output buffer.
* @throws ShortBufferException
* if the size of the {@code output} buffer is too small.
* @throws IllegalBlockSizeException
* if the size of the resulting bytes is not a multiple of the
* cipher block size.
* @throws BadPaddingException
* if the padding of the data does not match the padding scheme.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
* @throws IllegalArgumentException
* if {@code inputOffset} and {@code inputLen} do not specify an
* valid chunk in the input buffer.
*/
public final int doFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset) throws ShortBufferException,
IllegalBlockSizeException, BadPaddingException {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
checkInputOffsetAndCount(input.length, inputOffset, inputLen);
return getSpi().engineDoFinal(input, inputOffset, inputLen, output,
outputOffset);
}
/**
* Finishes a multi-part transformation (encryption or decryption).
*
* Processes the {@code input.remaining()} bytes in {@code input} buffer at
* {@code input.position()}, and any bytes that have been buffered in
* previous {@code update} calls. The transformed bytes are placed into
* {@code output} buffer.
*
* @param input
* the input buffer.
* @param output
* the output buffer.
* @return the number of bytes placed into the output buffer.
* @throws ShortBufferException
* if the size of the {@code output} buffer is too small.
* @throws IllegalBlockSizeException
* if the size of the resulting bytes is not a multiple of the
* cipher block size.
* @throws BadPaddingException
* if the padding of the data does not match the padding scheme.
* @throws IllegalArgumentException
* if the input buffer and the output buffer are the same
* object.
* @throws IllegalStateException
* if this cipher instance is not initialized for encryption or
* decryption.
*/
public final int doFinal(ByteBuffer input, ByteBuffer output)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) {
throw new IllegalStateException();
}
if (input == output) {
throw new IllegalArgumentException("input == output");
}
return getSpi().engineDoFinal(input, output);
}
/**
* Wraps a key using this cipher instance.
*
* @param key
* the key to wrap.
* @return the wrapped key.
* @throws IllegalBlockSizeException
* if the size of the resulting bytes is not a multiple of the
* cipher block size.
* @throws InvalidKeyException
* if this cipher instance can not wrap this key.
* @throws IllegalStateException
* if this cipher instance is not initialized for wrapping.
*/
public final byte[] wrap(Key key) throws IllegalBlockSizeException,
InvalidKeyException {
if (mode != WRAP_MODE) {
throw new IllegalStateException();
}
return getSpi().engineWrap(key);
}
/**
* Unwraps a key using this cipher instance.
*
* @param wrappedKey
* the wrapped key to unwrap.
* @param wrappedKeyAlgorithm
* the algorithm for the wrapped key.
* @param wrappedKeyType
* the type of the wrapped key (one of: {@code SECRET_KEY
* , PRIVATE_KEY} or {@code PUBLIC_KEY})
* @return the unwrapped key
* @throws InvalidKeyException
* if the {@code wrappedKey} can not be unwrapped to a key of
* type {@code wrappedKeyType} for the {@code
* wrappedKeyAlgorithm}.
* @throws NoSuchAlgorithmException
* if no provider can be found that can create a key of type
* {@code wrappedKeyType} for the {@code wrappedKeyAlgorithm}.
* @throws IllegalStateException
* if this cipher instance is not initialized for unwrapping.
*/
public final Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
int wrappedKeyType) throws InvalidKeyException,
NoSuchAlgorithmException {
if (mode != UNWRAP_MODE) {
throw new IllegalStateException();
}
return getSpi().engineUnwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType);
}
/**
* Returns the maximum key length for the specified transformation.
*
* @param transformation
* the transformation name.
* @return the maximum key length, currently {@code Integer.MAX_VALUE}.
* @throws NoSuchAlgorithmException
* if no provider for the specified {@code transformation} can
* be found.
* @throws NullPointerException
* if {@code transformation} is {@code null}.
*/
public static final int getMaxAllowedKeyLength(String transformation)
throws NoSuchAlgorithmException {
if (transformation == null) {
throw new NullPointerException("transformation == null");
}
checkTransformation(transformation);
//FIXME jurisdiction policy files
return Integer.MAX_VALUE;
}
/**
* Returns the maximum cipher parameter value for the specified
* transformation. If there is no maximum limit, {@code null} is returned.
*
* @param transformation
* the transformation name.
* @return a parameter spec holding the maximum value or {@code null}.
* Currently {@code null}.
* @throws NoSuchAlgorithmException
* if no provider for the specified {@code transformation} can
* be found.
* @throws NullPointerException
* if {@code transformation} is {@code null}.
*/
public static final AlgorithmParameterSpec getMaxAllowedParameterSpec(
String transformation) throws NoSuchAlgorithmException {
if (transformation == null) {
throw new NullPointerException("transformation == null");
}
checkTransformation(transformation);
//FIXME jurisdiction policy files
return null;
}
}