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

com.theicenet.cryptography.cipher.symmetric.aes.JCAAESCipherService Maven / Gradle / Ivy

There is a newer version: 1.3.2
Show newest version
/*
 * Copyright 2019-2021 the original author or authors.
 *
 * Licensed 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
 *
 *      https://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 com.theicenet.cryptography.cipher.symmetric.aes;

import static com.theicenet.cryptography.util.ByteArraysUtil.concat;
import static com.theicenet.cryptography.util.ByteArraysUtil.split;

import com.theicenet.cryptography.cipher.symmetric.BlockCipherIVModeOfOperation;
import com.theicenet.cryptography.cipher.symmetric.BlockCipherModeOfOperation;
import com.theicenet.cryptography.cipher.symmetric.BlockCipherNonIVModeOfOperation;
import com.theicenet.cryptography.cipher.symmetric.SymmetricCipherService;
import com.theicenet.cryptography.cipher.symmetric.SymmetricCipherServiceException;
import com.theicenet.cryptography.cipher.symmetric.SymmetricIVCipherService;
import com.theicenet.cryptography.cipher.symmetric.SymmetricNonIVCipherService;
import com.theicenet.cryptography.random.SecureRandomDataService;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import javax.crypto.SecretKey;
import org.apache.commons.lang.Validate;

/**
 * Java Cryptography Architecture (JCA) based component which makes easy to encrypt and decrypt
 * using AES algorithm, hiding the underlying complexities, and requiring only the content
 * to encrypt/decrypt and the secret key, regardless of the block mode of operation used.
 *
 * In case the implemented block mode of operation is IV based, then the implementation will
 * generate the IV on the fly and will prefix/read it to/from the output/input.
 *
 * @see Advanced Encryption Standard (AES)
 * @see Block cipher mode of operation
 * @see Initialization vector (IV)
 * @see Java Cryptography Architecture (JCA)
 *
 * @implNote This implementation is unconditionally thread-safe as required by the API interface.
 * @implNote
 *    This components ensures, as required by the API interface that the IV (when required)
 *    prefixed/read by the 'encrypt' and 'decrypt' methods has identical size and structure,
 *    so the output of the 'encrypt' method can be passed with no alteration into the 'decrypt'
 *    method to produce the clear content (as long as the secret key used is the same).
 *
 * @author Juan Fidalgo
 * @since 1.2.0
 */
public class JCAAESCipherService implements SymmetricCipherService {

  private static final int IV_SIZE_16_BYTES = 16;

  private final BlockCipherModeOfOperation blockMode;
  private final SymmetricNonIVCipherService aesNonIVCipherService;
  private final SymmetricIVCipherService aesIVCipherService;
  private final SecureRandomDataService randomDataService;

  public JCAAESCipherService(
      BlockCipherNonIVModeOfOperation blockMode,
      SymmetricNonIVCipherService aesNonIVCipherService,
      SecureRandomDataService randomDataService) {

    Validate.notNull(blockMode);
    Validate.notNull(aesNonIVCipherService);
    Validate.notNull(randomDataService);

    this.blockMode = BlockCipherModeOfOperation.valueOf(blockMode.name());
    this.aesNonIVCipherService = aesNonIVCipherService;
    this.aesIVCipherService = null;
    this.randomDataService = randomDataService;
  }

  public JCAAESCipherService(
      BlockCipherIVModeOfOperation blockMode,
      SymmetricIVCipherService aesIVCipherService,
      SecureRandomDataService randomDataService) {

    Validate.notNull(blockMode);
    Validate.notNull(aesIVCipherService);
    Validate.notNull(randomDataService);

    this.blockMode = BlockCipherModeOfOperation.valueOf(blockMode.name());
    this.aesNonIVCipherService = null;
    this.aesIVCipherService = aesIVCipherService;
    this.randomDataService = randomDataService;
  }

  @Override
  public byte[] encrypt(SecretKey secretKey, byte[] clearContent) {
    Validate.notNull(secretKey);
    Validate.notNull(clearContent);

    if (isNonIVBlockMode(blockMode)) {
      return aesNonIVCipherService.encrypt(secretKey, clearContent);
    }

    final byte[] iv = randomDataService.generateSecureRandomData(IV_SIZE_16_BYTES);
    final byte[] encrypted =
        aesIVCipherService.encrypt(
            secretKey,
            iv,
            clearContent);

    return concat(iv, encrypted);
  }

  @Override
  public byte[] decrypt(SecretKey secretKey, byte[] encryptedContent) {
    Validate.notNull(secretKey);
    Validate.notNull(encryptedContent);

    if (isNonIVBlockMode(blockMode)) {
      return aesNonIVCipherService.decrypt(secretKey, encryptedContent);
    }

    final byte[][] ivAndEncrypted = split(encryptedContent, IV_SIZE_16_BYTES);
    final byte[] iv = ivAndEncrypted[0];
    final byte[] encrypted = ivAndEncrypted[1];

    return
        aesIVCipherService.decrypt(
            secretKey,
            iv,
            encrypted);
  }

  @Override
  public void encrypt(
      SecretKey secretKey,
      InputStream clearContentInputStream,
      OutputStream encryptedContentOutputStream) {

    Validate.notNull(secretKey);
    Validate.notNull(clearContentInputStream);
    Validate.notNull(encryptedContentOutputStream);

    if (isNonIVBlockMode(blockMode)) {
      aesNonIVCipherService.encrypt(
          secretKey,
          clearContentInputStream,
          encryptedContentOutputStream);

      return;
    }

    final byte[] iv = randomDataService.generateSecureRandomData(IV_SIZE_16_BYTES);
    try {
      encryptedContentOutputStream.write(iv);
    } catch (IOException e) {
      throw new SymmetricCipherServiceException("Exception prefixing IV to output stream", e);
    }

    aesIVCipherService.encrypt(
        secretKey,
        iv,
        clearContentInputStream,
        encryptedContentOutputStream);
  }

  @Override
  public void decrypt(
      SecretKey secretKey,
      InputStream encryptedContentInputStream,
      OutputStream clearContentOutputStream) {

    Validate.notNull(secretKey);
    Validate.notNull(encryptedContentInputStream);
    Validate.notNull(clearContentOutputStream);

    if (isNonIVBlockMode(blockMode)) {
      aesNonIVCipherService.decrypt(
          secretKey,
          encryptedContentInputStream,
          clearContentOutputStream);

      return;
    }

    final byte[] iv;
    try {
      iv = encryptedContentInputStream.readNBytes(IV_SIZE_16_BYTES);
    } catch (IOException e) {
      throw new SymmetricCipherServiceException("Exception reading IV from output stream", e);
    }

    aesIVCipherService.decrypt(
        secretKey,
        iv,
        encryptedContentInputStream,
        clearContentOutputStream);
  }

  private boolean isNonIVBlockMode(BlockCipherModeOfOperation blockMode) {
    return Objects.equals(blockMode, BlockCipherModeOfOperation.ECB);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy