阅读 104

AES 前后端加解密方案

AES 前后端加解密方案

背景

最近有一个需求:后端对敏感数据进行加密传输给前端,由前端解密后进行回显。在讨论之后,定下了AES加解密方案

概念

AES: 密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准,是最为常见的对称加密算法

密码说明

AES算法主要有四种操作处理,分别是:

  1. 密钥轮加(Add Round Key)

  2. 字节代换层(SubBytes)

  3. 行位移层(Shift Rows)

  4. 列混淆层(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 加解密。真是方便呀,老规矩,感兴趣的同学可以移步这里

以下是我又一轮的解决步骤:

  1. npm install crypto-js

  2. 在utils目录下新建一个文件aes.js

  3. 封装如下代码:

    // 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); } 复制代码

  4. 然后就可以正常调用了!

最后,终于成功解密!

一点点小感悟

在日常工作中真的很少使用算法,对称加密在学校里听起来好像非常简单的样子,但是真的应用到生活中,特别是安全领域,还是非常复杂的。哎,学无止境吧~


作者:溜溜球形废物
链接:https://juejin.cn/post/6951368041590423582


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐