001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2013-2015 ForgeRock AS.
015 */
016
017package org.forgerock.json.jose.jwe.handlers.encryption;
018
019import java.security.GeneralSecurityException;
020import java.security.InvalidAlgorithmParameterException;
021import java.security.InvalidKeyException;
022import java.security.Key;
023import java.security.NoSuchAlgorithmException;
024import java.util.logging.Level;
025import java.util.logging.Logger;
026
027import javax.crypto.BadPaddingException;
028import javax.crypto.Cipher;
029import javax.crypto.IllegalBlockSizeException;
030import javax.crypto.NoSuchPaddingException;
031import javax.crypto.spec.IvParameterSpec;
032import javax.crypto.spec.SecretKeySpec;
033
034import org.forgerock.json.jose.exceptions.JweDecryptionException;
035import org.forgerock.json.jose.exceptions.JweEncryptionException;
036
037/**
038 * A base implementation of an EncryptionHandler that provides common encryption and decryption methods for all
039 * concrete EncryptionHandler implementations.
040 *
041 * @since 2.0.0
042 */
043public abstract class AbstractEncryptionHandler implements EncryptionHandler {
044    private static final Logger LOGGER = Logger.getLogger(AbstractEncryptionHandler.class.getName());
045
046    /**
047     * Encrypts the given plaintext using the specified key with the specified encryption algorithm.
048     *
049     * @param algorithm The Java Cryptographic encryption algorithm.
050     * @param key The encryption key.
051     * @param data The data to encrypt.
052     * @return An array of bytes representing the encrypted data.
053     */
054    protected byte[] encrypt(String algorithm, Key key, byte[] data) {
055        try {
056            Cipher cipher = Cipher.getInstance(algorithm);
057            cipher.init(Cipher.ENCRYPT_MODE, key);
058            return cipher.doFinal(data);
059        } catch (NoSuchAlgorithmException e) {
060            throw new JweEncryptionException("Unsupported Encryption Algorithm, " + algorithm, e);
061        } catch (IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException | BadPaddingException e) {
062            throw new JweEncryptionException(e);
063        }
064    }
065
066    /**
067     * Encrypts the given plaintext using the specified key and initialisation vector with the specified encryption
068     * algorithm.
069     *
070     * @param algorithm The Java Cryptographic encryption algorithm.
071     * @param key The encryption key.
072     * @param initialisationVector The initialisation vector.
073     * @param data The data to encrypt.
074     * @return An array of bytes representing the encrypted data.
075     */
076    protected byte[] encrypt(String algorithm, Key key, byte[] initialisationVector, byte[] data) {
077
078        try {
079            Cipher cipher = Cipher.getInstance(algorithm);
080            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), key.getAlgorithm());
081            IvParameterSpec ivParameterSpec = new IvParameterSpec(initialisationVector);
082            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
083            return cipher.doFinal(data);
084        } catch (NoSuchAlgorithmException e) {
085            throw new JweEncryptionException("Unsupported Encryption Algorithm, " + algorithm, e);
086        } catch (IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException | BadPaddingException
087                | InvalidAlgorithmParameterException e) {
088            throw new JweEncryptionException(e);
089        }
090    }
091
092    /**
093     * Decrypts the given ciphertext using the private key and with the same encryption algorithm that was used in the
094     * encryption.
095     *
096     * @param algorithm The Java Cryptographic encryption algorithm.
097     * @param privateKey The private key pair to the public key used in the encryption.
098     * @param data The ciphertext to decrypt.
099     * @return An array of bytes representing the decrypted data.
100     */
101    public byte[] decrypt(String algorithm, Key privateKey, byte[] data) {
102
103        try {
104            Cipher cipher = Cipher.getInstance(algorithm);
105            cipher.init(Cipher.DECRYPT_MODE, privateKey);
106            return cipher.doFinal(data);
107        } catch (GeneralSecurityException e) {
108            logDecryptionFailure(e);
109            throw new JweDecryptionException();
110        }
111    }
112
113    /**
114     * Decrypts the given ciphertext using the private key and initialisation vector with the same encryption algorithm
115     * that was used in the encryption.
116     *
117     * @param algorithm The Java Cryptographic encryption algorithm.
118     * @param key The private key pair to the public key used in the encryption.
119     * @param initialisationVector The same initialisation vector that was used in the encryption.
120     * @param data The ciphertext to decrypt.
121     * @return An array of bytes representing the decrypted data.
122     */
123    protected byte[] decrypt(String algorithm, Key key, byte[] initialisationVector, byte[] data) {
124
125        try {
126            Cipher cipher = Cipher.getInstance(algorithm);
127            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), key.getAlgorithm());
128            IvParameterSpec ivParameterSpec = new IvParameterSpec(initialisationVector);
129            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
130            return cipher.doFinal(data);
131        } catch (GeneralSecurityException e) {
132            logDecryptionFailure(e);
133            throw new JweDecryptionException();
134        }
135    }
136
137    /**
138     * Log the root cause of any decryption error before throwing a generic exception.
139     */
140    private void logDecryptionFailure(Throwable cause) {
141        if (LOGGER.isLoggable(Level.FINE)) {
142            LOGGER.log(Level.FINE, "Decryption failed: " + cause, cause);
143        }
144    }
145}