diff options
author | Peter Hallam <peterhal@google.com> | 2010-04-26 12:53:37 -0700 |
---|---|---|
committer | Peter Hallam <peterhal@google.com> | 2010-04-27 16:26:27 -0700 |
commit | cec4dd4b1d33f78997603d0f89c0d0e56e64dbcd (patch) | |
tree | e71d43da21749bfeb4524b0adec05c91d1f89a5f /luni/src/main/java/javax/crypto/Cipher.java | |
parent | f205f06be1ce65f132be1b7c850675086f26c0f7 (diff) | |
download | libcore-cec4dd4b1d33f78997603d0f89c0d0e56e64dbcd.zip libcore-cec4dd4b1d33f78997603d0f89c0d0e56e64dbcd.tar.gz libcore-cec4dd4b1d33f78997603d0f89c0d0e56e64dbcd.tar.bz2 |
merge more modules into luni
Diffstat (limited to 'luni/src/main/java/javax/crypto/Cipher.java')
-rw-r--r-- | luni/src/main/java/javax/crypto/Cipher.java | 1384 |
1 files changed, 1384 insertions, 0 deletions
diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java new file mode 100644 index 0000000..8e084ae --- /dev/null +++ b/luni/src/main/java/javax/crypto/Cipher.java @@ -0,0 +1,1384 @@ +/* + * 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.SecureRandom; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Set; +import java.util.StringTokenizer; + +import org.apache.harmony.crypto.internal.NullCipherSpi; +import org.apache.harmony.crypto.internal.nls.Messages; +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: + * <ul> + * <li><i>"algorithm/mode/padding"</i></li> or + * <li><i>"algorithm"</i></li> + * </ul> + * <i>algorithm</i> is the name of a cryptographic algorithm, <i>mode</i> is the + * name of a feedback mode and <i>padding</i> is the name of a padding scheme. + * If <i>mode</i> and/or <i>padding</i> values are omitted, provider specific + * default values will be used. + * <p> + * A valid transformation would be: + * <ul> + * {@code Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");} + * </ul> + * When a block cipher is requested in 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. <i>"AES/CFB8/NoPadding"</i>. 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; + + /** + * The service name. + */ + private static final String SERVICE = "Cipher"; //$NON-NLS-1$ + + /** + * Used to access common engine functionality. + */ + private static final Engine engine = new Engine(SERVICE); + + /** + * The provider. + */ + private Provider provider; + + /** + * The SPI implementation. + */ + private CipherSpi spiImpl; + + /** + * The transformation. + */ + private String transformation; + + private static SecureRandom sec_rand; + + /** + * 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(); + } + if (!(cipherSpi instanceof NullCipherSpi) && provider == null) { + throw new NullPointerException(); + } + this.provider = provider; + this.transformation = transformation; + this.spiImpl = cipherSpi; + } + + /** + * 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 + * <i>transformation</i>, or it is {@code null}, empty or in an + * invalid format. + * @throws NoSuchPaddingException + * if no installed provider can provide the padding scheme in + * the <i>transformation</i>. + */ + 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 + * <i>transformation</i>, 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 <i>transformation</i> + * 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(Messages.getString("crypto.04")); //$NON-NLS-1$ + } + + Provider p = Security.getProvider(provider); + if (p == null) { + throw new NoSuchProviderException(Messages.getString("crypto.16", provider)); //$NON-NLS-1$ + } + return getInstance(transformation, p); + } + + /** + * Creates a new cipher for the specified transformation. + * + * @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 + * <i>transformation</i>, or it is {@code null}, empty or in an + * invalid format. + * @throws NoSuchPaddingException + * if the requested padding scheme in the <i>transformation</i> + * 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(Messages.getString("crypto.04")); //$NON-NLS-1$ + } + Cipher c = getCipher(transformation, provider); + return c; + } + + /** + * Find appropriate Cipher according the specification rules + * + * @param transformation + * @param provider + * @return + * @throws NoSuchAlgorithmException + * @throws NoSuchPaddingException + */ + private static synchronized Cipher getCipher(String transformation, Provider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + + if (transformation == null || "".equals(transformation)) { //$NON-NLS-1$ + throw new NoSuchAlgorithmException(Messages.getString("crypto.17", //$NON-NLS-1$ + transformation)); + } + + String[] transf = checkTransformation(transformation); + + boolean needSetPadding = false; + boolean needSetMode = false; + if (transf[1] == null && transf[2] == null) { // "algorithm" + if (provider == null) { + engine.getInstance(transf[0], null); + } else { + engine.getInstance(transf[0], provider, null); + } + } else { + String[] searhOrder = { + transf[0] + "/" + transf[1] + "/" + transf[2], // "algorithm/mode/padding" //$NON-NLS-1$ //$NON-NLS-2$ + transf[0] + "/" + transf[1], // "algorithm/mode" //$NON-NLS-1$ + transf[0] + "//" + transf[2], // "algorithm//padding" //$NON-NLS-1$ + transf[0] // "algorithm" + }; + int i; + for (i = 0; i < searhOrder.length; i++) { + try { + if (provider == null) { + engine.getInstance(searhOrder[i], null); + } else { + engine.getInstance(searhOrder[i], provider, null); + } + break; + } catch (NoSuchAlgorithmException e) { + if ( i == searhOrder.length-1) { + throw new NoSuchAlgorithmException(transformation); + } + } + } + switch (i) { + case 1: // "algorithm/mode" + needSetPadding = true; + break; + case 2: // "algorithm//padding" + needSetMode = true; + break; + case 3: // "algorithm" + needSetPadding = true; + needSetMode = true; + } + } + CipherSpi cspi; + try { + cspi = (CipherSpi) engine.spi; + } catch (ClassCastException e) { + throw new NoSuchAlgorithmException(e); + } + Cipher c = new Cipher(cspi, engine.provider, transformation); + if (needSetMode) { + c.spiImpl.engineSetMode(transf[1]); + } + if (needSetPadding) { + c.spiImpl.engineSetPadding(transf[2]); + } + return c; + } + + private static String[] checkTransformation(String transformation) + throws NoSuchAlgorithmException { + String[] transf = { null, null, null }; + StringTokenizer st; + int i = 0; + for (st = new StringTokenizer(transformation, "/"); st //$NON-NLS-1$ + .hasMoreElements();) { + if (i > 2) { + throw new NoSuchAlgorithmException(Messages.getString("crypto.17", //$NON-NLS-1$ + transformation)); + } + transf[i] = st.nextToken(); + if (transf[i] != null) { + transf[i] = transf[i].trim(); + if ("".equals(transf[i])) { //$NON-NLS-1$ + transf[i] = null; + } + i++; + } + } + if (transf[0] == null) { + throw new NoSuchAlgorithmException(Messages.getString("crypto.17", //$NON-NLS-1$ + transformation)); + } + if (!(transf[1] == null && transf[2] == null) + && (transf[1] == null || transf[2] == null)) { + throw new NoSuchAlgorithmException(Messages.getString("crypto.17", //$NON-NLS-1$ + transformation)); + } + return transf; + } + + /** + * Returns the provider of this cipher instance. + * + * @return the provider of this cipher instance. + */ + public final Provider getProvider() { + return provider; + } + + /** + * Returns the name of the algorithm of this cipher instance. + * <p> + * This is the name of the <i>transformation</i> 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 spiImpl.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( + Messages.getString("crypto.18")); //$NON-NLS-1$ + } + return spiImpl.engineGetOutputSize(inputLen); + } + + /** + * Returns the <i>initialization vector</i> for this cipher instance. + * + * @return the <i>initialization vector</i> for this cipher instance. + */ + public final byte[] getIV() { + return spiImpl.engineGetIV(); + } + + /** + * Returns the parameters that where used to create this cipher instance. + * <p> + * 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 spiImpl.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; + // } + + } + + /** + * Initializes this cipher instance with the specified key. + * <p> + * The cipher is initialized for the specified operational mode (one of: + * encryption, decryption, key wrapping or key unwrapping) depending on + * {@code opmode}. + * <p> + * 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). + * <p> + * 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 (sec_rand == 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() + sec_rand = new SecureRandom(); + } + init(opmode, key, sec_rand); + } + + /** + * Initializes this cipher instance with the specified key and a source of + * randomness. + * <p> + * The cipher is initialized for the specified operational mode (one of: + * encryption, decryption, key wrapping or key unwrapping) depending on + * {@code opmode}. + * <p> + * 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}; + * <p> + * 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 { + if (opmode != ENCRYPT_MODE && opmode != DECRYPT_MODE + && opmode != UNWRAP_MODE && opmode != WRAP_MODE) { + throw new InvalidParameterException(Messages.getString("crypto.19")); //$NON-NLS-1$ + } + // FIXME InvalidKeyException + // if keysize exceeds the maximum allowable keysize + // (jurisdiction policy files) + spiImpl.engineInit(opmode, key, random); + mode = opmode; + } + + /** + * Initializes this cipher instance with the specified key and algorithm + * parameters. + * <p> + * The cipher is initialized for the specified operational mode (one of: + * encryption, decryption, key wrapping or key unwrapping). + * <p> + * 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). + * <p> + * 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 (sec_rand == null) { + sec_rand = new SecureRandom(); + } + init(opmode, key, params, sec_rand); + } + + /** + * Initializes this cipher instance with the specified key, algorithm + * parameters and a source of randomness. + * <p> + * The cipher is initialized for the specified operational mode (one of: + * encryption, decryption, key wrapping or key unwrapping) depending on + * {@code opmode}. + * <p> + * 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}; + * <p> + * 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 { + if (opmode != ENCRYPT_MODE && opmode != DECRYPT_MODE + && opmode != UNWRAP_MODE && opmode != WRAP_MODE) { + throw new InvalidParameterException(Messages.getString("crypto.19")); //$NON-NLS-1$ + } + // FIXME InvalidKeyException + // if keysize exceeds the maximum allowable keysize + // (jurisdiction policy files) + // FIXME InvalidAlgorithmParameterException + // cryptographic strength exceed the legal limits + // (jurisdiction policy files) + spiImpl.engineInit(opmode, key, params, random); + mode = opmode; + } + + /** + * Initializes this cipher instance with the specified key and algorithm + * parameters. + * <p> + * The cipher is initialized for the specified operation (one of: + * encryption, decryption, key wrapping or key unwrapping) depending on + * {@code opmode}. + * <p> + * 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). + * <p> + * 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 (sec_rand == null) { + sec_rand = new SecureRandom(); + } + init(opmode, key, params, sec_rand); + } + + /** + * Initializes this cipher instance with the specified key, algorithm + * parameters and a source of randomness. + * <p> + * The cipher will be initialized for the specified operation (one of: + * encryption, decryption, key wrapping or key unwrapping) depending on + * {@code opmode}. + * <p> + * 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}. + * <p> + * 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 { + if (opmode != ENCRYPT_MODE && opmode != DECRYPT_MODE + && opmode != UNWRAP_MODE && opmode != WRAP_MODE) { + throw new InvalidParameterException(Messages.getString("crypto.19")); //$NON-NLS-1$ + } + // FIXME InvalidKeyException + // if keysize exceeds the maximum allowable keysize + // (jurisdiction policy files) + // FIXME InvalidAlgorithmParameterException + // cryptographic strength exceed the legal limits + // (jurisdiction policy files) + spiImpl.engineInit(opmode, key, params, random); + mode = opmode; + } + + /** + * Initializes this cipher instance with the public key from the specified + * certificate. + * <p> + * The cipher will be initialized for the specified operation (one of: + * encryption, decryption, key wrapping or key unwrapping) depending on + * {@code opmode}. + * <p> + * It the type of the certificate is X.509 and the certificate has a <i>key + * usage</i> extension field marked as critical, the specified {@code + * opmode} has the be enabled for this key, otherwise an {@code + * InvalidKeyException} is thrown. + * <p> + * 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). + * <p> + * 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 (sec_rand == null) { + sec_rand = new SecureRandom(); + } + init(opmode, certificate, sec_rand); + } + + /** + * Initializes this cipher instance with the public key from the specified + * certificate and a source of randomness. + * <p> + * The cipher will be initialized for the specified operation (one of: + * encryption, decryption, key wrapping or key unwrapping) depending on + * {@code opmode}. + * <p> + * It the type of the certificate is X.509 and the certificate has a <i>key + * usage</i> extension field marked as critical, the specified {@code + * opmode} has the be enabled for this key, otherwise an {@code + * InvalidKeyException} is thrown. + * <p> + * 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}. + * <p> + * 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 { + if (opmode != ENCRYPT_MODE && opmode != DECRYPT_MODE + && opmode != UNWRAP_MODE && opmode != WRAP_MODE) { + throw new InvalidParameterException(Messages.getString("crypto.19")); //$NON-NLS-1$ + } + if (certificate instanceof X509Certificate) { + Set<String> 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 //$NON-NLS-1$ + 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. + // (http://www.ietf.org/rfc/rfc3280.txt) + // + // KeyUsage ::= BIT STRING {digitalSignature (0), + // ... + // encipherOnly (7), + // decipherOnly (8) } + if (keyUsage != null) { + if (opmode == ENCRYPT_MODE && (!keyUsage[7])) { + throw new InvalidKeyException( + Messages.getString("crypto.1A")); //$NON-NLS-1$ + } else if (opmode == DECRYPT_MODE && (!keyUsage[8])) { + throw new InvalidKeyException( + Messages.getString("crypto.1B")); //$NON-NLS-1$ + } + } + } + } + } + // FIXME InvalidKeyException + // if keysize exceeds the maximum allowable keysize + // (jurisdiction policy files) + spiImpl.engineInit(opmode, certificate.getPublicKey(), 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + if (input == null) { + throw new IllegalArgumentException(Messages.getString("crypto.1D")); //$NON-NLS-1$ + } + if (input.length == 0) { + return null; + } + return spiImpl.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 the 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + if (input == null) { + throw new IllegalArgumentException(Messages.getString("crypto.1D")); //$NON-NLS-1$ + } + if (inputOffset < 0 || inputLen < 0 + || inputLen > input.length + || inputOffset > input.length - inputLen) { + throw new IllegalArgumentException( + Messages.getString("crypto.1E")); //$NON-NLS-1$ + } + if (input.length == 0) { + return null; + } + return spiImpl.engineUpdate(input, inputOffset, inputLen); + } + + /** + * Continues a multi-part transformation (encryption or decryption). The + * transformed bytes are stored in the {@code output} buffer. + * <p> + * 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. + * <p> + * 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + if (input == null) { + throw new IllegalArgumentException(Messages.getString("crypto.1D")); //$NON-NLS-1$ + } + if (output == null) { + throw new IllegalArgumentException(Messages.getString("crypto.1F")); //$NON-NLS-1$ + } + if (outputOffset < 0) { + throw new IllegalArgumentException( + Messages.getString("crypto.20")); //$NON-NLS-1$ + } + if (inputOffset < 0 || inputLen < 0 + || inputLen > input.length + || inputOffset > input.length - inputLen) { + throw new IllegalArgumentException( + Messages.getString("crypto.21")); //$NON-NLS-1$ + } + if (input.length == 0) { + return 0; + } + return spiImpl.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. + * <p> + * 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + if (input == output) { + throw new IllegalArgumentException( + Messages.getString("crypto.22")); //$NON-NLS-1$ + } + return spiImpl.engineUpdate(input, output); + } + + /** + * Finishes a multi-part transformation (encryption or decryption). + * <p> + * 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + return spiImpl.engineDoFinal(null, 0, 0); + } + + /** + * Finishes a multi-part transformation (encryption or decryption). + * <p> + * Processes any bytes that may have been buffered in previous {@code + * update} calls. + * <p> + * 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + if (outputOffset < 0) { + throw new IllegalArgumentException( + Messages.getString("crypto.20")); //$NON-NLS-1$ + } + return spiImpl.engineDoFinal(null, 0, 0, output, outputOffset); + } + + /** + * Finishes a multi-part transformation (encryption or decryption). + * <p> + * 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + return spiImpl.engineDoFinal(input, 0, input.length); + } + + /** + * Finishes a multi-part transformation (encryption or decryption). + * <p> + * 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + if (inputOffset < 0 || inputLen < 0 + || inputOffset + inputLen > input.length) { + throw new IllegalArgumentException( + Messages.getString("crypto.1E")); //$NON-NLS-1$ + } + return spiImpl.engineDoFinal(input, inputOffset, inputLen); + } + + /** + * Finishes a multi-part transformation (encryption or decryption). + * <p> + * 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). + * <p> + * 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + if (inputOffset < 0 || inputLen < 0 + || inputOffset + inputLen > input.length) { + throw new IllegalArgumentException( + Messages.getString("crypto.1E")); //$NON-NLS-1$ + } + return spiImpl.engineDoFinal(input, inputOffset, inputLen, output, + outputOffset); + } + + /** + * Finishes a multi-part transformation (encryption or decryption). + * <p> + * 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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + if (input == output) { + throw new IllegalArgumentException( + Messages.getString("crypto.2E")); //$NON-NLS-1$ + } + return spiImpl.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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + return spiImpl.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 + * <code>, <code>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( + Messages.getString("crypto.1C")); //$NON-NLS-1$ + } + return spiImpl.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(); + } + 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(); + } + checkTransformation(transformation); + //FIXME jurisdiction policy files + return null; + } +} |