package org.bouncycastle.tls.crypto.impl.jcajce; import java.lang.reflect.Constructor; import java.security.GeneralSecurityException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jcajce.spec.AEADParameterSpec; import org.bouncycastle.tls.crypto.impl.TlsAEADCipherImpl; /** * A basic wrapper for a JCE Cipher class to provide the needed AEAD cipher functionality for TLS. */ public class JceAEADCipherImpl implements TlsAEADCipherImpl { private static Constructor<AlgorithmParameterSpec> initSpecConstructor() { try { Class<AlgorithmParameterSpec> clazz = (Class<AlgorithmParameterSpec>) Class.forName("javax.crypto.spec.GCMParameterSpec", true, IvParameterSpec.class.getClassLoader()); return clazz.getConstructor(int.class, byte[].class); } catch (Exception ignore) { // TODO[logging] Log the fact that we are falling back to BC-specific class return null; } } private static final Constructor<AlgorithmParameterSpec> specConstructor = initSpecConstructor(); private final int cipherMode; private final Cipher cipher; private final String algorithm; private SecretKey key; public JceAEADCipherImpl(Cipher cipher, String algorithm, boolean isEncrypting) throws GeneralSecurityException { this.cipher = cipher; this.algorithm = algorithm; this.cipherMode = (isEncrypting) ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; } public void setKey(byte[] key) { this.key = new SecretKeySpec(key, algorithm); } public void init(byte[] nonce, int macSize, byte[] additionalData) { try { // Try to use GCMParameterSpec (introduced in JDK 7) if (specConstructor != null) { try { AlgorithmParameterSpec spec = specConstructor.newInstance(macSize * 8, nonce); cipher.init(cipherMode, key, spec); if (additionalData != null && additionalData.length > 0) { cipher.updateAAD(additionalData); } return; } catch (GeneralSecurityException e) { // no point in falling back if it's one of these throw e; } catch (Exception e) { // we don't have the spec class, ignore. } } // Otherwise fall back to the BC-specific AEADParameterSpec cipher.init(cipherMode, key, new AEADParameterSpec(nonce, macSize * 8, additionalData)); } catch (GeneralSecurityException e) { throw new IllegalStateException(e); } } public int getOutputSize(int inputLength) { return cipher.getOutputSize(inputLength); } public int doFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) { try { return cipher.doFinal(input, inputOffset, inputLength, output, outputOffset); } catch (GeneralSecurityException e) { throw new IllegalStateException(e); } } }