RSA是什么
百度百科的解释为:RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
简单点说则是加密和解密使用的是不同的秘钥,对称加密则是加密和解密都是使用相同的秘钥。本文只介绍RSA加密常规的使用方法,不涉及到加密原理及其底层实现。
RSA生成公钥/私钥对
公钥和私钥一般是成对出现,就好像是一夫一妻制,一个男人只能有一个妻子,当然找小三除外。这就是一个约定,如果不按照约定来,加密和解密工作则不能顺利完成。使用rsa时首先需要生成公钥和私钥,JS代码如下:
/**
* 生成公钥私钥对
*/
function generateKeyPair () {
let key = new NodeRSA({b: '512'}); //可以更改为1024
key.setOptions({encryptionScheme: 'pkcs1'});// 指定加密格式
let public_key = key.exportKey('pkcs8-public');
let private_key = key.exportKey('pkcs8-private');
console.log('公钥:\n', public_key);
console.log('私钥:\n', private_key);
return {
public_key: public_key,
private_key: private_key
}
};
把512修改为1024后,生成的秘钥长度会有明显的变化,可以自己去设置不同的值看看结果。生成的秘钥非常长,并且含有换行符,如果想去掉换行符后直接保存在数据库中,则可以使用下面的代码:
private_key = private_key.trim().split('\n').reduce((str, currentValue) => str + currentValue);
public_key = public_key.trim().split('\n').reduce((str, currentValue) => str + currentValue);
RSA公钥加密/私钥解密
这种应用方式的使用场景为项目中用户登录、重置密码、修改密码时使用,加载登录页面时,将公钥传递到登录页面,用一个隐藏标签保存公钥的值,然后在登录时,密码使用rsa加密传输。前端加密还需要一个加密文件jsencrypt.min.js,github上面有,或者去网上找一搜一大堆。在后端使用私钥解密,可以拿到用户登录时的明文密码,如果需要MD5加密后在进行密码校验,则完全可以放在后端进行。
前端公钥加密代码为:
let encrypt = new JSEncrypt();
encrypt.setPublicKey($('#public_key').val());
password = encrypt.encrypt(password);
后端私钥解密代码为:
/**
* 解密数据
* @param 加密后字符串
* @param 私钥
* @return {Buffer|Object|string}
*/
function decrypt (encryptData, private_key) {
private_key = private_key || config.password_cfg.rsa_private_key;
if (!private_key) throw "私钥不能为空";
let prikey = new NodeRSA(private_key);
prikey.setOptions({encryptionScheme: 'pkcs1'});
return prikey.decrypt(encryptData, "utf-8");
};
RSA私钥加密/公钥解密
据我了解RSA加密的常规用法是公钥加密,私钥解密。可是特殊场景则不能生搬硬套,需要灵活应对。自己在做一个接口项目的开发工作时,也使用到RSA加密,这里使用的则是私钥加密,然后使用公钥解密。因为这个加密工作是和签名和验证签名一起进行的,签名只能使用私钥,安全性非常有保障。
私钥加密代码:
/**
* rsa 私钥加密数据
* @param data 要加密的数据
* @param private_key 私钥数据,如果为空,取配置文件的公钥数据
* @return {string}
*/
const pkcsSize = '512';
const pkcsType = 'pkcs8';
const BASE64 = 'base64';
const UTF8 = 'utf8';
rsa_utils.encrypt = function (data, private_key) {
if (!data){
throw new Error("数据不能为空");
}
if (!private_key){
throw new Error("私钥不能为空");
}
let key = new NodeRSA({ b: pkcsSize });
key.importKey(private_key, pkcsType+'-private-pem');
return key.encryptPrivate(data, BASE64, UTF8);
};
公钥解密代码:
/**
* rsa 公钥解密数据
* @param data 要解密的数据
* @param public_key 公钥
* @return {string}
*/
const pkcsType = 'pkcs8';
rsa_utils.decryptAPI = function (data, public_key) {
if (!data){
throw new Error("数据不能为空");
}
if (!public_key){
throw new Error("公钥不能为空");
}
let key = new NodeRSA({ b: '512'});
key.importKey(public_key, pkcsType+'-public-pem');
return key.decryptPublic(data, UTF8);
};
RSA私钥签名/公钥验证签名
签名和验证签名主要作用是确保数据在传输过程中不被串改,从而保证数据的安全性。这和人的签名有些类似,比如用支票去取钱时需要有钱人的签名,不然不让你取。
私钥签名代码:
/**
* 数据签名
* @param encryptData
* @param public_key
* @return {Buffer|Object|string}
*/
rsa_utils.sign = function (data, public_key) {
if (!data){
throw new Error("数据不能为空");
}
if (!public_key){
throw new Error("公钥不能为空");
}
let key = new NodeRSA({ b: pkcsSize });
key.importKey(public_key, pkcsType+'-private-pem');
return key.sign(Buffer.from(data), BASE64).toString(BASE64);
};
公钥验证签名代码:
/**
* 验证签名
* @param data
* @param private_key
* @return {Buffer|Object|string}
*/
const pkcsType = 'pkcs8';
rsa_utils.validate = function (data, sign, public_key) {
if (!data){
throw new Error("数据不能为空");
}
if (!sign){
throw new Error("签名不能为空");
}
if (!public_key){
throw new Error("公钥不能为空");
}
let publicKey = new NodeRSA({ b: pkcsSize });
publicKey.importKey(public_key, pkcsType+'-public-pem');
return publicKey.verify(Buffer.from(data), sign, 'UTF-8', 'base64');
};
注意事项
加密后的字符串或者是签名后的字符串中可能会含有加号,如果是将参数拼接在URL中进行传输时,需要特别注意。因为在解析时可能会将字符串中的加号解析为空格,这样的话解密就会出错。还有在使用过程中配置的参数一定要一致,如UTF-8和Base64这些参数,细节如果不注意,加密和解密就会出问题。
如果有写得不对的地方,还请各位多多指点,留下各位宝贵的意见,欢迎在下面留言交流。