AES 前后端加解密方案
AES 前后端加解密方案
背景
最近有一个需求:后端对敏感数据进行加密传输给前端,由前端解密后进行回显。在讨论之后,定下了AES加解密方案
概念
AES: 密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准,是最为常见的对称加密算法
密码说明
AES算法主要有四种操作处理,分别是:
- 密钥轮加(Add Round Key)
- 字节代换层(SubBytes)
- 行位移层(Shift Rows)
- 列混淆层(Mix Column)
主要是讲使用方案,所以这里不说太多废话了,对算法感兴趣的同学移步这里, 讲的非常详细,不过文章里的代码是使用C语言写的,为此找到了github上aes.js
的源码,感兴趣的同学移步这里
前端实现
现在简单说一下前端的实现:
我先找到了github上的源码,看了一下大概800行的样子。本来打算直接改吧改吧,封装成一个加解密的工具方法,直接扔在utils目录里的。本来也很成功的改好了,本地加解密试了一下,效果也很不错。根据github链接上的readme文档说明,封装了如下函数:
// 省略了改完的aes.js的代码。。。
// 加密 text 需要加密的文本 key 密钥
const toAESBytes = (text, key) => {
const textBytes = aesjs.utils.utf8.toBytes(text);
const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
const encryptedBytes = aesCtr.encrypt(textBytes);
const encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
console.log('加密后的文本:', encryptedHex);
return encryptedHex;
};
// 解密
const fromAESBytes = (text, key) => {
const encryptedBytes = aesjs.utils.hex.toBytes(text);
const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
const decryptedBytes = aesCtr.decrypt(encryptedBytes);
const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes);
console.log('解密后的文本:', decryptedText);
return decryptedText;
}
但是这个方法在和后端对接的时候出现了一点偏差,死活也不能将后端加密后的数据成功解密。于是又向后端同学请教了一下,发现原因如下:
在AES加解密算法中,除了加解密的密文,也就是key需要一样之外,还有几样东西也非常重要:
-
AES 的算法模式需要保持一致
关于算法模式,主要有以下几种:
1. 电码本模式 Electronic Codebook Book (ECB); 2. 密码分组链接模式 Cipher Block Chaining (CBC) 3. 计算器模式Counter (CTR) 4. 密码反馈模式(Cipher FeedBack (CFB) 5. 输出反馈模式Output FeedBack (OFB)
这么看的话,我上面的demo应该使用的就是计算器模式了!关于算法模式的介绍,感兴趣的同学请移步这里
-
补码方式保持一致
关于补码方式,我查到的以下几种:
1. PKCS5Padding PKCS7Padding的子集,块大小固定为8字节 2. PKCS7Padding 假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小。 3. ZeroPadding 数据长度不对齐时使用0填充,否则不填充
-
密钥长度保持一致
AES算法一共有三种密钥长度:128、192、256。这个前后端的密钥长度确实是保持一致的。
-
加密结果编码方式保持一致
一般情况下,AES加密结果有两种编码方式:base64 和 16进制
所以到底是哪里出了问题呢?后端同学好心发给了我他后端的代码:
/**
* aes 加密 Created by xingxiping on 2017/9/20.
*/
public class AesUtils {
private static final String CIPHER_ALGORITHM = "AES"; // optional value AES/DES/DESede
private AesUtils(){
}
/**
* 加密
*
* @param content
* 源内容
* @param key
* 加密密钥
* @return
*/
public static String encrypt(String content, String key) throws Exception {
Cipher cipher = getCipher(key, Cipher.ENCRYPT_MODE);
byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
byte[] result = cipher.doFinal(byteContent);
return Base64Utils.encode(result);
}
/**
* 解密
*
* @param content
* 内容
* @param key
* 解密密钥
* @return
*/
public static byte[] decrypt(String content, String key) throws Exception {
Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);
byte[] bytes = Base64Utils.decode(content);
bytes = cipher.doFinal(bytes);
return bytes;
}
private static Cipher getCipher(String key, int cipherMode) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
cipher.init(cipherMode, secretKey);
return cipher;
}
}
破案了,后端老哥对加密后的结果进行了base64编码,然后我又仔细去看了一下aes.js源码,根本没有找到base64的影子啊!
于是在查找一翻资料以后,决定使用crypto-j
,使用 Crypto-JS 可以非常方便地在 JavaScript 进行 MD5、SHA1、SHA2、SHA3、RIPEMD-160 哈希散列,进行 AES、DES、Rabbit、RC4、Triple DES 加解密。真是方便呀,老规矩,感兴趣的同学可以移步这里
以下是我又一轮的解决步骤:
-
npm install crypto-js
-
在utils目录下新建一个文件aes.js
-
封装如下代码:
// aes 解密 import CryptoJS from 'crypto-js'; // 解密 encryptedStr待解密字符串 pass 密文 export const aesDecode = (encryptedStr, pass) => { const key = CryptoJS.enc.Utf8.parse(pass); // 通过密钥获取128位的key const encryptedHexStr = CryptoJS.enc.Base64.parse(encryptedStr); // 解码base64编码结果 const encryptedBase64Str = CryptoJS.enc.Base64.stringify(encryptedHexStr); const decryptedData = CryptoJS.AES.decrypt(encryptedBase64Str, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); return decryptedData.toString(CryptoJS.enc.Utf8); }
-
然后就可以正常调用了!
最后,终于成功解密!
一点点小感悟
在日常工作中真的很少使用算法,对称加密在学校里听起来好像非常简单的样子,但是真的应用到生活中,特别是安全领域,还是非常复杂的。哎,学无止境吧~
感谢大家的阅读!
本文参考:
- 密码学基础:AES加密算法;
- 高级加密标准
- AES加密算法的详细介绍与实现
- github.com/ricmoo/aes-…
- github.com/brix/crypto…