因公司做的产品为金融项目,所以对数据安全性有很高要求,因为项目中的数据都会通过3DES 对称加密,和RSA非对称加密进行数据传输。
在这里先简单介绍一下什么是对称加密和非对称加密
对称加密:对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥加密。
非对称加密:对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。(ps:公钥放在客户端,用于加密;私钥放在服务器端,用于解密)
本文先实现RSA非对称加解密,3DES对称加密的文章在
https://blog.csdn.net/qq_34024275/article/details/82421075
RSA一般用于重要数据加密,例如用户密码,金额,优惠卷等,并且解密速度比3DES解密慢许多,所以慎用。
提供一个生成RSA秘钥的网站
http://web.chacuo.net/netrsakeypair
需要注意的是:区分RSA私钥的类型,有pkcs1和pkcs8。pkcs8格式的私钥主要用于java中。
// pkcs1格式:
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY------
// pkcs8格式:
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
本文生成的key为pkcs1格式的。
第一步:构建前端加解密工具
另一篇微信小程序3DES加密文章中用到加密的库为google的Cryptojs,里面有很多种加密方式,但唯独没有RSA的,这就比较坑爹,不得不寻找新的加解密工具。
由于并非专业前端工程师,所以只有请求度娘的帮助,查到了现在前端工程师一般都是用JSEncrypt,下面附上官网地址:
http://travistidwell.com/jsencrypt/
由于JSEncrypt主要是用于前端H5开发加解密,工具内部本身会调用浏览器Window对象和navigator对象,而微信小程序本身不属于浏览器环境,没有Window对象和navigator对象。所以我们需要去修改原文件jsencrypt.min.js(这是压缩版)或jsencrypt.js,去构建Window对象和navigator对象,不然会提示undefined。
主要将以下代码插入到jsencrypt.js文件中:
var navigator = {
appName: 'Netscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
};
var window={
ASN1:null,
Base64:null,
Hex:null,
crypto:null,
href:null
};
插入位置,,
插入到 'use strict'; 后面。
第二步:在项目加密工具类用引入JSEncrypt
import { JSEncrypt } from '../js/jsencrypt.min.js'
/**rsa加密
**@param text 需要加密的文本
**@param publicKey 加密需要的公钥 pkcs1 类型的公钥
**/
export const encryptByRsa = (text,publicKey) => {
var encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
var encrypted = encrypt.encrypt(text);
return encrypted
}
ps:由于项目中RSA前端只加密,所以这里只写了加密方法,解密方法类似,具体可以看文档
第三步:创建java后端加解密工具类RSAUtil.java
package com.uaf.wxAppWeb.utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
/**
* RSA算法
*
*/
public class RSAUtil {
/**
* 公钥加密
*
* @param content
* @param public_key
* @return
* @throws Exception
*/
public static String signWithPublicKey(String content, String public_key) throws Exception {
byte[] buffer = Base64.decodeBase64(public_key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(1, publicKey);
byte[] data = content.getBytes("utf-8");
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
int key_len = publicKey.getModulus().bitLength() / 8 - 11;
for (int i = 0; inputLen - offSet > 0; offSet = i * key_len) {
byte[] cache;
if (inputLen - offSet > key_len) {
cache = cipher.doFinal(data, offSet, key_len);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
++i;
}
byte[] encryptedData = out.toByteArray();
out.close();
return new String(Base64.encodeBase64(encryptedData));
}
/**
* 私钥解密
*
* @param content 密文
* @param private_key 私钥
* @param input_charset 编码UTF-8
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String content, String private_key) throws Exception {
String input_charset = "UTF-8";
PrivateKey prikey = getPrivateKey(private_key);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, prikey);
InputStream ins = new ByteArrayInputStream(Base64.decodeBase64(content));
ByteArrayOutputStream writer = new ByteArrayOutputStream();
// rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密
byte[] buf = new byte[128];
int bufl;
while ((bufl = ins.read(buf)) != -1) {
byte[] block = null;
if (buf.length == bufl) {
block = buf;
} else {
block = new byte[bufl];
for (int i = 0; i < bufl; i++) {
block[i] = buf[i];
}
}
writer.write(cipher.doFinal(block));
}
return new String(writer.toByteArray(), input_charset);
}
}
到这里小程序所需要的RSA加密工具类都已经构建完毕。
需要注意的地方是:这里的代码适用于生成PKCS1秘钥,一定要在JSencrypt里面构建Window对象和navigator对象。
由于小程序主包大小限制,所以选择的是min压缩的js文件,修改的时候需要注意一点。
小白程序员一枚,如有问题欢迎留言一起探讨~~