最近一个vue项目要求所有密码数据需要使用RSA非对称加密传输,以为挺简单,结果开发过程中还是遇到了些问题,简单做个笔记。同时也希望可以帮助到遇到同样问题的道友门。
概念
对称加密(算法):在加密和解密时使用的是同一个秘钥;
非对称加密(算法):与对称加密算法不同的是,非对称加密算法需要两个不同的密钥(公钥和私钥)。公钥和私钥是一对,用公钥加密的数据,只有用对应的私钥才能解密。
实际开发过程中,后台生成一对公私钥,私钥存在服务器,把公钥给前端,前端加密后传给后端,这是相对比较安全的做法。
实现—生成密钥
重点来了:使用jsencrypt实现RSA非对称加解密
因为这里直接在前端加解密,所以需要一对现成的密钥,我们通过密钥在线生成器得到:
密钥密钥有了,该上代码了
首先安装jsencrypt依赖包
npm install jsencrypt
然后在需要使用的文件中引入JSEncrypt,我是将所有工具函数都封装在一个js文件的,我就直接在该文件中引入,我看也有人是在main.js中引入的。
import { JSEncrypt } from 'jsencrypt'
// 或者下面这种方式引入
// import JSEncrypt from 'jsencrypt/bin/jsencrypt'
实现—加密
/**
* RSA非对称加密
* @param data 需要加密的数据
*/
export function rsaEncrypt(data) {
// 公钥,,复制前面生成的公钥
const PUBLIC_KEY = `MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLCf1tQoxpXg6FQPZnCmUpZLvN
IjXRjtFmsQ8LEarKnamjnwAhDnHOpz4pCWXHFcox40FhJQzUmyAzKgIluALvbjYl
HamB1IY+XBeEsR/h2BiRufwSWj1YV4r1rIbhd5p+ymqEZcQeWpcY4ueke+k98RZ0
+VBOuf4DGTdrb8L/PwIDAQAB`
// 使用公钥加密
const encrypt = new JSEncrypt()
encrypt.setPublicKey(PUBLIC_KEY)
let result = encrypt.encrypt(data)
return result
}
实现—解密
/**
* RSA 解密
* @param data 需要解密的数据
*/
export function rsaDecrypt(data) {
//私钥,复制对应的私钥用于解密
const PRIVATE_KEY = `MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMsJ/W1CjGleDoVA
9mcKZSlku80iNdGO0WaxDwsRqsqdqaOfACEOcc6nPikJZccVyjHjQWElDNSbIDMq
AiW4Au9uNiUdqYHUhj5cF4SxH+HYGJG5/BJaPVhXivWshuF3mn7KaoRlxB5alxji
56R76T3xFnT5UE65/gMZN2tvwv8/AgMBAAECgYBeFe4C0GDCftxZsWW9D9sa2FwS
TbUEu5qbbJbc+T3ckDzI3mgv6UKhkWxDleA85gMBJR7pxkJwzsWYD/JYyjFJMJRm
5T7muGxm9CnywUH4dng+IlJc+JtqNOGVF9QmCfA5dI+WQu7xqoBAh3GI5vEEHJMo
qYaDSGboYHSd+y5eYQJBAO6aITksboR7TflI6zwDtGmef5xq6DmcdEUtVGe+Bpx3
HOIABKIqnf3znTVe9MYkk95fXlDJlgPTmjDlo8TvxVECQQDZ2AWZKbcFqrpucv7B
O2uLwWfh5daBbmpt7ixhNRNrEFYopCJTUKFm/9Rk4NUQ89HBQdjSMUnlaYhqGjVB
IZePAj8UgSpZv3e/6tjIk3ujrK3UZcqRpp5OVSOozjxyreHjkFjrExVS2la5fDYG
YCKo5HvQoGF6j9hUe9rEWPe59OECQHwjL72CGfuuuKJsAWRX2gc/5VTDRqNnKlsO
mFekiTY/jvmF3tGfZvps2rnJrWEFsAfy3/2XfMawhr3/xU0iOV0CQQC74OOHYKhm
C6rvN+Kzk63OrUmC8F/a9xmLSSG8Jd0gmHGkmRMeYxRUxgs+LS/htHDAKYyGH2uD
msqCj47HstQs`
//使用私钥解密
const decrypt = new JSEncrypt()
decrypt.setPrivateKey(PRIVATE_KEY)
let result = decrypt.decrypt(data)
return result
}
实现—测试
到这里我们的加密解密方法就完成了,在需要的地方直接拿过来用就好了!
import { rsaEncrypt, rsaDecrypt } from '@/utils'
// 在需要的地方使用
console.log('加密:', rsaEncrypt('123456'))
console.log('解密:', rsaDecrypt (rsaEncrypt('123456')))
执行效果如下:
测试
大功告成!这样就完了?我以为这样就ok了。
然而!!!当我重新启动项目的时候报错了。。报错了。。。
引入JSEncrypt报错
实现—解决bug
当然,如果没有遇到这个bug,就可以忽略下面的内容了。
从上面截图可以看到,重启项目的时候报错:navigator is not defined
而且这个bug有点奇葩,先启动项目再引入jsencrypt就什么问题都没有,但是先引入jsencrypt再启动项目就报错。这也是我前面能顺利执行的原因所在。
通过好一通折腾,用了网上的各种方法,比如在main.js引入jsencrypt、引入jsdom之类的,都没能解决这个问题,最终还是在jsencrypt的git相关issue下找到了这个问题的解决方案。
解决方案
翻译下,用这里的jsencrypt替换node_modules/jsencrypt/bin 文件夹中的jsencrypt.js的。所以是修改了源码,我将这个文件下载下来,里面是一个jsencrypt.js文件,替换掉原本的jsencrypt.js,问题解决了。
附上链接:jsencrypt.zip
对比了一下两个文件,发现是删掉了navigator相关的内容,对比图:
对比图1
对比图2
对比图3
对比图4
实现—更换实现方式
到这里问题就算基本解决了,但是由于项目组不止我一个前端,我不能要求每个同事或者以后接手维护项目的同事都要在node_modules中去替换文件。
所以就采用了另外一种方案:将jsencrypt.js通过在线js压缩器压缩至jsencrypt.min.js中,然后把jsencrypt.min.js放到src/assets/jsencrypt文件夹中,就不用npm install的方式了。
换了种方式,jsencrypt的引用方式需要做出相应的调整:
// 引用JSEncrypt
import JSEncrypt from '../assets/jsencrypt/jsencrypt.min.js'