View Javadoc
1   package cn.home1.oss.lib.common.crypto;
2   
3   import static java.nio.charset.StandardCharsets.UTF_8;
4   import static javax.crypto.Cipher.ENCRYPT_MODE;
5   
6   import lombok.Getter;
7   import lombok.SneakyThrows;
8   
9   import java.nio.ByteBuffer;
10  import java.nio.charset.Charset;
11  import java.security.Provider;
12  
13  import javax.crypto.Cipher;
14  
15  import cn.home1.oss.lib.common.CodecUtils;
16  
17  /**
18   * Created by zhanghaolun on 16/11/17.
19   */
20  public class AesEncryptor implements EncodeEncryptor {
21  
22    @Getter
23    private final Charset charset;
24    private final Provider provider;
25    private final AesCbcKey key;
26  
27    public AesEncryptor(final Provider provider, final KeyExpression keyExpression) {
28      this.charset = UTF_8;
29      this.provider = provider;
30      this.key = new AesCbcKey(keyExpression);
31    }
32  
33    @Override
34    public String encrypt(final String plainText) {
35      return this.encryptBytes( //
36        RandomString.RandomStrings.RANDOM_BASE62.generate(this.key.getCbcIvLength()), //
37        plainText.getBytes(this.charset) //
38      );
39    }
40  
41    @Override
42    public String encrypt(final String plainText, final Integer maxAge) {
43      throw new UnsupportedOperationException();
44    }
45  
46    public String encryptBytes(final String random, final byte[] plainBytes) {
47      final byte[] randomBytes = random.getBytes(this.charset);
48      final int nopadLength = randomBytes.length + plainBytes.length;
49      // ... + pad: 使用自定义的填充方式对明文进行补位填充
50      final byte[] padBytes = Pkcs7Encoder.PKCS7_UTF8_BLOCK32.encode(nopadLength);
51  
52      // 获得最终的字节流, 未加密
53      // random + plain + pad
54      final ByteBuffer unencrypted = ByteBuffer.allocate(nopadLength + padBytes.length);
55      unencrypted.put(randomBytes);
56      unencrypted.put(plainBytes);
57      unencrypted.put(padBytes);
58      try {
59        // 加密
60        final byte[] encrypted = this.encryptCipher().doFinal(unencrypted.array());
61        // 使用BASE64对加密后的字符串进行编码
62        return CodecUtils.encodeBase64(encrypted);
63      } catch (final Exception cause) {
64        throw new AesException(AesException.AesError.ENCRYPT_AES_ERROR, cause);
65      } finally {
66        unencrypted.clear();
67      }
68    }
69  
70    @SneakyThrows
71    Cipher encryptCipher() { // arg1: "BC" 指定使用 bouncycastle provider, 不指定则使用jre
72      final Cipher cipher = Cipher.getInstance(CryptoConstants.AES_CBC_NOPADDING, this.provider);// 设置加密模式为AES的CBC模式
73      cipher.init(ENCRYPT_MODE, this.key.getKeySpec(), this.key.getCbcIv());
74      return cipher;
75    }
76  }