AES加密过程分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013821237/article/details/83176893

最近做前后端AES加解密遇到了一个麻烦的问题,因为之前只是按照网上的Demo照着Copy了一份,当时测试可用:

  public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
        return cipher.doFinal(stringToBytes(content));
    }
/**
Cipher 类为加密和解密提供密码功能
Cipher 的 getInstance ()参数有两种格式:“算法/模式/填充”或“算法”,后一种情况下,
使用模式和填充方案特定于提供者的默认值
cipher.init(a,b),两个参数:a表示当前的加密模式,b代表用于加密的最终密钥
public static final int ENCRYPT_MODE  用于将 Cipher 初始化为加密模式的常量。
public static final int DECRYPT_MODE  用于将 Cipher 初始化为解密模式的常量。
public static final int WRAP_MODE     用于将 Cipher 初始化为密钥包装模式的常量。
public static final int UNWRAP_MODE   用于将 Cipher 初始化为密钥解包模式的常量。
public static final int PUBLIC_KEY    用于表示要解包的密钥为“公钥”的常量。
public static final int PRIVATE_KEY   用于表示要解包的密钥为“私钥”的常量。
public static final int SECRET_KEY    用于表示要解包的密钥为“秘密密钥”的常量。

*/

这里我们把约定的加密Key:encryptKey直接转为byte[]数组的形式来用于加密:encryptKey.getBytes()。我们知道AES加密是分段加密的,不清楚的可以参考:AES详解,每一段的长度是约定好的,128或256,同时,密钥Key长度也是定好的,128或192或256。因为AES是对称加密,也就是加密完的东西要能完整的解密出来,所以这些约定的规则很重要。刚刚的代码,我们把约定的key直接用于加密了,所以约定的key长度也必须符合128/192/256位。当我们希望key更随意,不是必须为32位的倍数时,我们需要在执行加密前对密钥做一次加工:

KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(stringToBytes(encryptKey));
kgen.init(256, secureRandom);

Cipher cipher = Cipher.getInstance("AES");
byte[] tt=  kgen.generateKey().getEncoded();
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(tt, ENCRYPT_AES_TYPE));
return cipher.doFinal(stringToBytes(content));

这里我们没有直接对密钥encryptKey.getBytes(),而是利用KeyGenerator 和SecureRandom 两个类对密钥先做处理,将其转化为32的倍数位。SecureRandom.getInstance("SHA1PRNG")指定使用随机数的算法, secureRandom.setSeed(stringToBytes(encryptKey))指定随机数生成的种子,它的实现是伪随机数生成器 (PRNG) 形式,这意味着它们将使用确定的算法根据实际的随机种子生成伪随机序列。kgen.init(256, secureRandom)指定生成的密钥位数是256位,随机源是刚刚的随机数对象。这样当我们使用 kgen.generateKey().getEncoded();生成密钥时,密钥就是256位的根据约定的encryptKey产生的新的密钥了。

前端我们使用的是JS库CryptoJs,如何对应实现和java匹配的加密呢?先安装CryptoJs.js包,再用法如下:

encrypt(word) {
    const key = CryptoJS.enc.Hex.parse("这里是真实的密钥,16位字符串形式");//方式一
//  const key = CryptoJS.enc.Utf8.parse("这里是约定的密钥,普通字符串形式");//方式二
    const srcs = CryptoJS.enc.Utf8.parse(word);
    const encrypted = CryptoJS.AES.encrypt(srcs, key, {
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7
    });
    return encrypted.ciphertext.toString();
  }

我遇到的问题是直接把双方约定的密钥按方式二的形式使用。但是我们知道,我们上面真正解密前是对约定的密钥做了处理的,所以这里不能直接用,否则必然不匹配。

怎么办呢?,很简单,只需要把处理后的结果拷贝过来当作最终密钥就好了!

上述java代码中: 
byte[] tt=  kgen.generateKey().getEncoded();
//得到字符数组,再将数组转化为16进制字符串如:6D6F6FB1290134B63038E7B384E89A7F79CBCFB3F7B235580742C70F5B3AF269

用这个密钥当作最终密钥,就可以前后端加解密成功了。

这里贴出一个CrypJs的快速开发指南:https://blog.csdn.net/u013821237/article/details/83176037

非常的有用!

猜你喜欢

转载自blog.csdn.net/u013821237/article/details/83176893
今日推荐