AesEncryptor.java

package cn.home1.oss.lib.common.crypto;

import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.crypto.Cipher.ENCRYPT_MODE;

import lombok.Getter;
import lombok.SneakyThrows;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.Provider;

import javax.crypto.Cipher;

import cn.home1.oss.lib.common.CodecUtils;

/**
 * Created by zhanghaolun on 16/11/17.
 */
public class AesEncryptor implements EncodeEncryptor {

  @Getter
  private final Charset charset;
  private final Provider provider;
  private final AesCbcKey key;

  public AesEncryptor(final Provider provider, final KeyExpression keyExpression) {
    this.charset = UTF_8;
    this.provider = provider;
    this.key = new AesCbcKey(keyExpression);
  }

  @Override
  public String encrypt(final String plainText) {
    return this.encryptBytes( //
      RandomString.RandomStrings.RANDOM_BASE62.generate(this.key.getCbcIvLength()), //
      plainText.getBytes(this.charset) //
    );
  }

  @Override
  public String encrypt(final String plainText, final Integer maxAge) {
    throw new UnsupportedOperationException();
  }

  public String encryptBytes(final String random, final byte[] plainBytes) {
    final byte[] randomBytes = random.getBytes(this.charset);
    final int nopadLength = randomBytes.length + plainBytes.length;
    // ... + pad: 使用自定义的填充方式对明文进行补位填充
    final byte[] padBytes = Pkcs7Encoder.PKCS7_UTF8_BLOCK32.encode(nopadLength);

    // 获得最终的字节流, 未加密
    // random + plain + pad
    final ByteBuffer unencrypted = ByteBuffer.allocate(nopadLength + padBytes.length);
    unencrypted.put(randomBytes);
    unencrypted.put(plainBytes);
    unencrypted.put(padBytes);
    try {
      // 加密
      final byte[] encrypted = this.encryptCipher().doFinal(unencrypted.array());
      // 使用BASE64对加密后的字符串进行编码
      return CodecUtils.encodeBase64(encrypted);
    } catch (final Exception cause) {
      throw new AesException(AesException.AesError.ENCRYPT_AES_ERROR, cause);
    } finally {
      unencrypted.clear();
    }
  }

  @SneakyThrows
  Cipher encryptCipher() { // arg1: "BC" 指定使用 bouncycastle provider, 不指定则使用jre
    final Cipher cipher = Cipher.getInstance(CryptoConstants.AES_CBC_NOPADDING, this.provider);// 设置加密模式为AES的CBC模式
    cipher.init(ENCRYPT_MODE, this.key.getKeySpec(), this.key.getCbcIv());
    return cipher;
  }
}