All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jsondb.crypto.DefaultAESCBCCipher Maven / Gradle / Ivy

There is a newer version: 1.0.85
Show newest version
/*
 * Copyright (c) 2016 Farooq Khan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package io.jsondb.crypto;

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Base64;
import java.util.concurrent.locks.ReentrantLock;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.jsondb.JsonDBException;

/**
 * @author Farooq Khan
 * @version 1.0 25-Sep-2016
 */
public class DefaultAESCBCCipher implements ICipher {
  private Logger logger = LoggerFactory.getLogger(DefaultAESCBCCipher.class);

  private static final String AES_ENCRYPTION_ALGORITHM = "AES";
  
  private final String charset;

  private Cipher encryptCipher;
  private Cipher decryptCipher;
  private ReentrantLock encryptionLock;
  private ReentrantLock decryptionLock;

  /**
   * This constructor assumes data to be of type 'UTF-8'
   * 
   * A default AES (CBC Mode) Cipher. AES is a 128-bit block cipher supporting keys of 128, 192, and 256 bits.
   * CBC may not be the most secure mode, maybe you want to use the AEAD mode. This implementation is just a 
   * example you can create your own implementation similar to this one using AES AEAD or entirely a different
   * crypto algorithm like say DES
   * 
   * Imp Note: This constructor takes a Base64 Encoded encryption key of type String, provided only for ease of use.
   *           Generally for such type of data a good practice is to use char[] so that it can be explicitly wiped of,
   *           Strings are immutable so it is possible that some other process may dump this process memory and locate
   *           the key, so yes this is a security concern. So if you wish you can create your own implementation of
   *           {@link io.jsondb.crypto.ICipher} that accepts a char[] or byte[] as key and you take care of filling
   *           up that array with garbage when done.
   * 
   * This constructor does not check if the given bytes indeed specify a secret key of the specified algorithm.
   * For example, this constructor does not check if key is 128, 192, 256 bytes long, and also does not check for weak or semi-weak keys.
   * 
   * Note: If you want to use Key > 128 bits then you need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction 
   *           Policy files.
   *
   * @param base64CodedEncryptionKey  A base 64 encoded symmetric key to be used during encryption and decryption.
   * @throws GeneralSecurityException  a general security exception
   */
  public DefaultAESCBCCipher(String base64CodedEncryptionKey) throws GeneralSecurityException {
    this(Base64.getDecoder().decode(base64CodedEncryptionKey), "UTF-8");
  }

  /**
   * A default AES (CBC Mode) Cipher. AES is a 128-bit block cipher supporting keys of 128, 192, and 256 bits.
   * CBC may not be the most secure mode, maybe you want to use the AEAD mode. This implementation is just a 
   * example you can create your own implementation similar to this one using AES AEAD or entirely a different
   * crypto algorithm like say DES
   * 
   * Imp Note: This constructor takes a Base64 Encoded encryption key of type String, provided only for ease of use.
   *           Generally for such type of data a good practice is to use char[] so that it can be explicitly wiped of,
   *           Strings are immutable so it is possible that some other process may dump this process memory and locate
   *           the key, so yes this is a security concern. So if you wish you can create your own implementation of
   *           {@link io.jsondb.crypto.ICipher} that accepts a char[] or byte[] as key and you take care of filling
   *           up that array with garbage when done.
   * 
   * This constructor does not check if the given bytes indeed specify a secret key of the specified algorithm.
   * For example, this constructor does not check if key is 128, 192, 256 bytes long, and also does not check for weak or semi-weak keys.
   * 
   * Note: If you want to use Key > 128 bits then you need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction 
   *           Policy files.
   *
   * @param base64CodedEncryptionKey  A base 64 encoded symmetric key to be used during encryption and decryption.
   * @param charset charset to be considered when encrypting plaintext, or decrypting cipher text
   * @throws GeneralSecurityException  a general security exception
   */
  public DefaultAESCBCCipher(String base64CodedEncryptionKey, String charset) throws GeneralSecurityException {
    this(Base64.getDecoder().decode(base64CodedEncryptionKey), charset);
  }
  
  /**
   * A default AES (CBC Mode) Cipher. AES is a 128-bit block cipher supporting keys of 128, 192, and 256 bits.
   * CBC may not be the most secure mode, maybe you want to use the AEAD mode. This implementation is just a 
   * example you can create your own implementation similar to this one using AES AEAD or entirely a different
   * crypto algorithm like say DES
   * 
   * This constructor does not check if the given bytes indeed specify a secret key of the specified algorithm.
   * For example, this constructor does not check if key is 128, 192, 256 bytes long, and also does not check for weak or semi-weak keys.
   * 
   * Note: If you want to use Key > 128 bits then you need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction 
   *           Policy files.
   *
   * @param encryptionKey  symmetric key to be used during encryption or decryption.
   * @param charset charset to be considered when encrypting plaintext, or decrypting cipher text
   * @throws GeneralSecurityException  a general security exception
   */
  public DefaultAESCBCCipher(byte[] encryptionKey, String charset) throws GeneralSecurityException {
    this.charset = charset;

    encryptionLock = new ReentrantLock();
    decryptionLock = new ReentrantLock();
    try {
      this.encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
      this.decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
      SecretKeySpec key = new SecretKeySpec(encryptionKey, AES_ENCRYPTION_ALGORITHM);
      encryptCipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(encryptionKey));
      decryptCipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(encryptionKey));
    } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
      logger.error("Failed to create DefaultAESCBCCipher", e);
      throw e;
    } catch (InvalidKeyException e) {
      logger.error("Failed to create DefaultAESCBCCipher", e);
      throw new InvalidKeyException("Failed to create DefaultAESCBCCipher, something wrong with the key");
    } catch (InvalidAlgorithmParameterException e) {
      logger.error("Failed to create DefaultAESCBCCipher", e);
      throw new InvalidKeyException("Failed to create DefaultAESCBCCipher, something wrong with the key");
    }
  }

  /**
   * This method is used to encrypt(Symmetric) plainText coming in input using AES algorithm
   * @param plainText the plain text string to be encrypted
   * @return Base64 encoded AES encrypted cipher text
   */
  @Override
  public String encrypt(String plainText) {
    this.encryptionLock.lock();
    try{
      byte[] cipherBytes = null;
      try {
        cipherBytes = encryptCipher.doFinal(plainText.getBytes(charset));
      } catch (UnsupportedEncodingException e) {
        logger.error("DefaultAESCBCCipher failed to encrypt text", e);
        throw new JsonDBException("DefaultAESCBCCipher failed to encrypt text", e);
      } catch (IllegalBlockSizeException e) {
        logger.error("DefaultAESCBCCipher failed to encrypt text", e);
        throw new JsonDBException("DefaultAESCBCCipher failed to encrypt text", e);
      } catch (BadPaddingException e) {
        logger.error("DefaultAESCBCCipher failed to encrypt text", e);
        throw new JsonDBException("DefaultAESCBCCipher failed to encrypt text", e);
      }
      return Base64.getEncoder().encodeToString(cipherBytes);
    } finally{
      this.encryptionLock.unlock();
    }

  }

  /**
   * A method to decrypt the provided cipher text.
   *
   * @param cipherText AES encrypted cipherText
   * @return decrypted text
   */
  @Override
  public String decrypt(String cipherText) {
    this.decryptionLock.lock();
    try{
      String decryptedValue = null;
      try {
        byte[] bytes = Base64.getDecoder().decode(cipherText);
        decryptedValue = new String(decryptCipher.doFinal(bytes), charset);
      } catch (UnsupportedEncodingException e) {
        logger.error("DefaultAESCBCCipher failed to decrypt text", e);
        throw new JsonDBException("DefaultAESCBCCipher failed to decrypt text", e);
      } catch (IllegalBlockSizeException e) {
        logger.error("DefaultAESCBCCipher failed to decrypt text", e);
        throw new JsonDBException("DefaultAESCBCCipher failed to decrypt text", e);
      } catch (BadPaddingException e) {
        logger.error("DefaultAESCBCCipher failed to decrypt text", e);
        throw new JsonDBException("DefaultAESCBCCipher failed to decrypt text", e);
      }
      return decryptedValue;
    } finally{
      this.decryptionLock.unlock();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy