文章目录
Pre
加密与安全_解密AES加密中的IV和Seed
加密与安全_双向RSA+AES加密及Code实现
加密与安全_常见的分组密码 ECB、CBC、CFB、OFB模式介绍
概述
当我们在前端和后端之间传输敏感信息时,安全性变得越来越重要。今天,我们将一起探索如何在前端加密密码,并在后端安全解密的过程。
使用Vue.js作为前端框架,并通过Java来处理后端解密。
前端加密是否有意义?
前端加密是否真的有意义?其实,这个问题的答案并不绝对。
- 一方面,前端加密能够一定程度上防止中间人攻击,从而保护用户的隐私。
- 但另一方面,如果攻击者能够篡改前端代码,前端加密就形同虚设。
因此,前端加密到底值不值得,还是见仁见智。
环境准备
前端使用的是Vue.js,也可以选择纯JS。同时,我们引入
crypto-js
库。至于后端,Java将担任这次任务的重任。
为了让前端和后端能够顺利交流,我们必须确保前后端使用相同的加密方式、模式(MODE)和填充方式(PADDING)。
加密方法、MODE和PADDING的选择
加密方法、MODE和PADDING之间也必须配合得天衣无缝。前后端的这些参数必须一致,才能确保传输的信息能够正确加密和解密。
必须选择前后端都支持的加密方法、模式和填充方式。
https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
https://cryptojs.gitbook.io/docs/#pbkdf2
以下是\选择的参数:
- 加密方法: AES(推荐加密方法,参考:FIPS 197)
- 模式: CBC(默认模式,参考:FIPS 81)
- 填充方式: Pkcs7(与Java的PKCS5Padding基本等价)
在这里,我们选择
AES/CBC/Pkcs7
作为加密组合,而Java端则使用
AES/CBC/PKCS5Padding
。这样一来,我们的前后端就可以在同一个频道上交流了。
前端
需要引入
crypto-js
库,并设置好密钥(key)和初始向量(iv)。这些设置必须与后端保持一致,才能确保加密与解密的正确性。
// aesutils.jsimport CryptoJs from'crypto-js'// 把key、iv设置成固定值,前后端的值要一致let key = CryptoJs.enc.Utf8.parse("xxxxxx");let iv = CryptoJs.enc.Utf8.parse("yyyyyy");exportfunctionEncrypt(word){let srcs = CryptoJs.enc.Utf8.parse(word);var encrypted = CryptoJs.AES.encrypt(srcs, key,{iv: iv,mode: CryptoJs.mode.CBC,padding: CryptoJs.pad.Pkcs7
});return CryptoJs.enc.Base64.stringify(encrypted.ciphertext);}
在这个加密函数中,我们将原始文本转换为Base64编码后的密文,然后将其发送到后端。如此一来,前端的密码在传输过程中便不会以明文形式暴露。
后端
后端的任务是接收到前端传来的密文,并将其解密为原始的明文密码。这一步同样至关重要,因为后端需要将这些密码存储或用于后续操作。
importorg.apache.tomcat.util.codec.binary.Base64;importjavax.crypto.Cipher;importjavax.crypto.spec.IvParameterSpec;importjavax.crypto.spec.SecretKeySpec;importjava.nio.charset.StandardCharsets;publicclassAESUtils{privatestaticfinalString key ="xxxxxx";privatestaticfinalString iv ="yyyyyy";publicstaticStringdeocdeStr(String text){try{byte[] encrypted =newBase64().decode(text);Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding ");SecretKeySpec secretKey =newSecretKeySpec(key.getBytes(StandardCharsets.UTF_8),"AES");IvParameterSpec ivParameter =newIvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameter);byte[] decrypted = cipher.doFinal(encrypted);String originalString =newString(decrypted,StandardCharsets.UTF_8);return originalString;}catch(Exception e){return e.toString();}}}
后端解密的核心步骤与前端加密紧密关联。首先,我们将Base64编码的密文解码为字节数组,然后利用AES/CBC/PKCS5Padding的组合将其解密为原始字符串。
应用:从传输到解密的全过程
在前端,我们使用工具类加密密码,然后通过
axios
等方法将加密后的密文传输到后端:
import{ Encrypt }from"@/utils/aesutils";Encrypt(this.$data.formData.password);
在后端,接收到加密的密文后,我们调用工具类解密并还原密码,然后将其用于后续的数据库操作:
AESUtils.deocdeStr(user.getPassword());
安全性增强
尽管我们使用了固定的密钥和初始向量(IV),但在实际的生产环境中,这种方式的安全性可能还不够强。接下来,我们将进一步提升安全性,探索如何使用动态生成的密钥和初始向量,并研究在生产环境中如何更好地管理这些关键要素。
动态生成密钥和初始向量
使用固定的密钥和IV,虽然简化了前后端的协调工作,但也带来了风险。一旦密钥和IV被泄露,攻击者就可以轻松解密传输的数据。为了解决这个问题,我们可以采取动态生成密钥和IV的方法。
1. 前端:动态生成密钥和IV
在前端,我们可以在每次需要加密时,动态生成密钥和IV。然后,将密钥和IV与加密后的数据一起发送到后端。为了避免密钥和IV直接暴露在网络传输中,我们可以将它们与加密数据一起加密或使用安全的密钥交换算法。
import CryptoJs from'crypto-js';// 生成随机密钥和IVfunctiongenerateKeyAndIV(){let key = CryptoJs.lib.WordArray.random(128/8);// 128-bit keylet iv = CryptoJs.lib.WordArray.random(128/8);// 128-bit IVreturn{ key, iv };}// 加密函数exportfunctionencryptWithDynamicKey(word){const{ key, iv }=generateKeyAndIV();let srcs = CryptoJs.enc.Utf8.parse(word);let encrypted = CryptoJs.AES.encrypt(srcs, key,{iv: iv,mode: CryptoJs.mode.CBC,padding: CryptoJs.pad.Pkcs7
});// 将密钥和IV与加密后的数据一起编码return{ciphertext: CryptoJs.enc.Base64.stringify(encrypted.ciphertext),key: CryptoJs.enc.Base64.stringify(key),iv: CryptoJs.enc.Base64.stringify(iv)};}
generateKeyAndIV
函数生成了一个随机的密钥和IV。然后,我们将这些随机生成的密钥和IV与加密后的数据一起编码并发送给后端。
2. 后端:解密动态密钥和IV
在后端接收到加密数据和动态生成的密钥、IV后,我们需要解码并使用它们进行解密操作。
importorg.apache.tomcat.util.codec.binary.Base64;importjavax.crypto.Cipher;importjavax.crypto.spec.IvParameterSpec;importjavax.crypto.spec.SecretKeySpec;importjava.nio.charset.StandardCharsets;publicclassAESUtils{publicstaticStringdecryptWithDynamicKey(String ciphertext,String keyBase64,String ivBase64){try{byte[] encrypted =newBase64().decode(ciphertext);byte[] key =newBase64().decode(keyBase64);byte[] iv =newBase64().decode(ivBase64);Cipher cipher =Cipher.getInstance("AES/CBC/PKCS5Padding");SecretKeySpec secretKey =newSecretKeySpec(key,"AES");IvParameterSpec ivParameter =newIvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameter);byte[] decrypted = cipher.doFinal(encrypted);returnnewString(decrypted,StandardCharsets.UTF_8);}catch(Exception e){return e.toString();}}}
这个解密函数接受Base64编码的密文、密钥和IV。我们先将它们解码为字节数组,然后使用它们来解密密文,恢复原始数据。
结语
通过动态生成密钥和初始向量,并使用安全的密钥管理服务,我们可以大幅提升系统的安全性。这不仅能防止密钥泄露带来的风险,还能通过安全的密钥交换和管理,确保前后端通信的绝对安全。
在实际的生产环境中,密钥的管理和轮换至关重要,建议使用专业的KMS来简化并加强密钥管理工作。通过这些措施,可以构建一个更加安全和稳健的系统 。
版权归原作者 小小工匠 所有, 如有侵权,请联系我们删除。