JavaScript的RSA加密库

  • 一、Rsa利用openssl生成公钥私钥
  • 1、安装openssl:
  • 2、生成公钥:
  • 3、生成私钥:
  • 二、Cryptico
  • 1.优点:
  • 2.缺点:
  • 3.安装:
  • 4.Demo
  • 5.参考链接:
  • 三、Node-rsa
  • 1.优点:
  • 2.缺点:
  • 3.安装:
  • 4.Demo
  • 5.参考
  • 四、Crypto
  • 1.优点:
  • 2.缺点:
  • 3.安装:免安装
  • 4.Demo
  • 4.1、nodejs支持的加密算法和哈希算法有哪些?
  • 4.2、生产rsa公钥私钥(pkcs1的公钥publicKey长度为188)
  • 4.3公钥加密,私钥解密;私钥加密,公钥解密;
  • 4.4、当然,这里的公钥可以使用OpenSSL产生的,也可以用上一步产生的公钥私钥
  • 5.参考
  • 五、jsrsasign
  • 1.优点:
  • 2.缺点:
  • 3.安装:
  • 4.Demo
  • 4.1支持openssl及nodersa生产的标准格式的rsa密钥
  • 4.2支持jsrsasign生产的密钥
  • 4.3 支持crypto生产的密钥,并且加密方式和crypto的解密方式通用
  • 5.参考
  • 六、JSEncrypt
  • 1.优点:
  • 2.缺点:
  • 3.安装:
  • 4.Demo
  • 4.1nodejs端node-jsencrypt
  • 4.2web端JSEncrypt
  • 5.参考


一、Rsa利用openssl生成公钥私钥

1、安装openssl:

参考:Windows安装使用Openssl

2、生成公钥:

openssl genrsa -out rsa_private_key.pem 1024

3、生成私钥:

openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

二、Cryptico

1.优点:

rsa密钥生产、rsa加密、rsa解密,使用方式简单,前端和nodejs服务端都可以使用

2.缺点:

生产的密钥不是标准位数的rsa密钥;不支持标准位数的rsa密钥pem格式;七年前已经停止更新;加密中文会乱码;

3.安装:

1.nodejs端:

npm i cryptico .

2web端

直接require加密代码

4.Demo

const cryptico = require('cryptico')
// The passphrase used to repeatably generate this RSA key.
var PassPhrase = "The Moon is a Harsh Mistress."; 
// The length of the RSA key, in bits.
var Bits = 1024; 
var date1 = new Date().getTime()
var MattsRSAkey = cryptico.generateRSAKey(PassPhrase, Bits);
console.log(new Date().getTime()-date1) //
// Matt's public key string can then be generated like this:
var MattsPublicKeyString = cryptico.publicKeyString(MattsRSAkey);
// var publicKey =keys.exportKey('pkcs8-public-pem')
// var privateKey =keys.exportKey('pkcs1-private-pem')
console.log(MattsPublicKeyString.length)  //172,1024Bits对应的密钥长度为172
// 需要加密的信息PlainText
var PlainText = "Matt, I need you to help me with my Starcraft strategy.";
// Encrypting a message 加密
var EncryptionResult = cryptico.encrypt(PlainText, MattsPublicKeyString);
// Decrypting a message 解密
var DecryptionResult = cryptico.decrypt(EncryptionResult.cipher, MattsRSAkey);
console.log(DecryptionResult.plaintext)

5.参考链接:

三、Node-rsa

1.优点:

可以生产不同填充方式的rsa公钥和私钥,并且生产密钥可以在nodejs中的crypto加密解密使用;

2.缺点:

crypto加密可以使用node-rsa解密,但是node-rsa加密的不可以使用crypto模块解密(更换解密私钥的填充方式应该可以);生产密钥时间较长;

3.安装:

npm i node-rsa。

4.Demo

const NodeRSA = require("node-rsa");
var key = new NodeRSA(); //生成2048位的密钥
key.generateKeyPair(1024)
var publicDer = key.exportKey("pkcs8-public-pem"); //公钥pkcs1对应公钥长度为188,pkcs8对应公钥长度为216
console.log(new Date().getTime()-date1)

var privateDer = key.exportKey("pkcs1-private-pem");//私钥
console.log("公钥:", publicDer);
console.log("私钥:", privateDer);
const text = "Hello RSA!";
//导入私钥
//key.importKey(privateDer, "pkcs1-private-pem");
// 加签并加密
//var key = new NodeRSA()
const sign = key.sign(text, "base64", "utf8");
console.log("A 私钥加签:", sign);
const encrypted = key.encrypt(sign, "base64");
console.log("B 公钥加密:", encrypted);
// 解密并验签
//var key = new NodeRSA()
//key.setOptions({ encryptionScheme: 'pkcs1' })
const decrypted = key.decrypt(encrypted, "utf8");
console.log("C 私钥解密:", decrypted);
const verify = key.verify(text, decrypted, "utf8", "base64");
console.log("D 公钥验签:", verify);

const crypto = require("crypto");
// 加密
const crypto_encrypted = crypto.publicEncrypt(publicDer, Buffer.from(text)).toString("base64");
console.log("E 公钥加密:", crypto_encrypted);
// 解密
var crypto_decrypted = crypto.privateDecrypt({
    key: privateDer,
    padding: crypto.constants.RSA_PKCS1_OAEP_PADDING
},
    Buffer.from(crypto_encrypted, "base64")
).toString("utf8");
console.log("F 私钥解密:", crypto_decrypted);
const decrypted2 = key.decrypt(crypto_encrypted, "utf8");
console.log("G 私钥解密:", decrypted2);

5.参考

  • 5.1Node-RSA 生成公私钥对、私钥签名、公钥验签
  • 5.2https://www.npmjs.com/package/node-rsa

四、Crypto

crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。
NodeJS的模块crypto,有以下4个与公钥加密相关的类。

  • ① Cipher: 用于加密数据;
  • ② Decipher: 用于解密数据;
  • ③ Sign: 用于生成签名;
  • ④ Verify: 用于验证签名;

1.优点:

生产密钥速度快,耗时是cryptcio和node-rsa的十分之一;nodejs内置模块;

2.缺点:

公钥不支持pcks8,前端和其对应的库较少;只可以在nodejs端使用(从后面的例子可以看到,更改密钥的填充方式,可以和前端部分rsa加密方式通用,估计这也就是为何node加密模块crypto用的少的原因)

3.安装:免安装

4.Demo

4.1、nodejs支持的加密算法和哈希算法有哪些?

const crypto = require('crypto')
// 查看含有哪些加密算法
var Ciphers=crypto.getCiphers()
console.log(Ciphers)
// (171) ['aes-128-cbc', 'aes-128-ccm', 'aes-128-cfb',...]可以看到共有171种加密算法(没有rsa)

// 查看含有哪些哈希算法
var Hashes = crypto.getHashes()
console.log(Hashes);
// (59) ['RSA-MD4', 'RSA-MD5', 'RSA-MDC2', 'RSA-RIPEMD160', 'RSA-SHA1',...]可以看到共有59种哈希算法

4.2、生产rsa公钥私钥(pkcs1的公钥publicKey长度为188)

const { generateKeyPairSync } = require('crypto');
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
  modulusLength: 1024,
  publicKeyEncoding: {
    type: 'pkcs1',
    format: 'pem'
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret'
  }
});

4.3公钥加密,私钥解密;私钥加密,公钥解密;

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const root = __dirname;

const publicKey = fs.readFileSync(path.join(root, '../rsa_public_key.pem')).toString('ascii');
const privateKey = fs.readFileSync(path.join(root, '../rsa_private_key.pem')).toString('ascii');
const data = 'data to crypt';

// 公钥加密
const encryptData = crypto.publicEncrypt(publicKey, Buffer.from(data)).toString('base64');
console.log('encode', encryptData);
// 私钥解密
const decryptData = crypto.privateDecrypt(privateKey, Buffer.from(encryptData.toString('base64'), 'base64'));
console.log('decode', decryptData.toString());

// 私钥加密
const encryptData2 = crypto.privateEncrypt(privateKey, Buffer.from(data)).toString('base64');
console.log('encode', encryptData2);
// 公钥解密
const decryptData2 = crypto.publicDecrypt(publicKey, Buffer.from(encryptData2.toString('base64'), 'base64'));
console.log('decode', decryptData2.toString());

4.4、当然,这里的公钥可以使用OpenSSL产生的,也可以用上一步产生的公钥私钥

const crypto = require('crypto');
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
  modulusLength: 1024,
  publicKeyEncoding: {
    type: 'pkcs1',
    format: 'pem'
  },
  privateKeyEncoding: {
    type: 'pkcs1',
    format: 'pem',
    // cipher: 'aes-256-cbc',
    // passphrase: 'top secret'
  }
});
const data = 'data to crypt';

// 公钥加密
const encryptData = crypto.publicEncrypt(publicKey, Buffer.from(data)).toString('base64');
console.log('encode', encryptData);
// 私钥解密
const decryptData = crypto.privateDecrypt(privateKey, Buffer.from(encryptData.toString('base64'), 'base64'));
console.log('decode', decryptData.toString());

5.参考

  • 5.1【node.js_crypto 模块】https://www.jianshu.com/p/c5c8c19bc80a
  • 5.2【Node.js v14.14.0 文档】http://nodejs.cn/api/crypto.html#crypto_crypto_constants_1
  • 5.3【crypto】https://www.liaoxuefeng.com/wiki/1022910821149312/1023025778520640

五、jsrsasign

1.优点:

加密解密方法比较全,nodejs和web端均可使用,支持nodejs的crypto模块生产的pkcs1格式的公钥;

2.缺点:

生成rsa密钥耗时较长,加密解密过程耗时较长;

3.安装:

  • nodejs:

npm i jsrsasign ,npm i jsrsasign-util

  • web端:

引入jsrsasign-all-min.js(https://github.com/kjur/jsrsasign)

4.Demo

4.1支持openssl及nodersa生产的标准格式的rsa密钥

const fs = require('fs')
var rs = require('jsrsasign');
const path = require('path');
const root = __dirname;

const publicKey = fs.readFileSync(path.join(root, './rsa_public_key.pem')).toString('ascii');
const privateKey = fs.readFileSync(path.join(root, './rsa_private_key.pem')).toString('ascii');

// 加密
// 读取解析pem格式的秘钥, 生成秘钥实例 (RSAKey) 
var date1 = new Date().getTime()
var pub = rs.KEYUTIL.getKey(publicKey);
var enc = rs.KJUR.crypto.Cipher.encrypt('src', pub);
console.log(enc);
//38b5835801da14a7efb1d15ee7c92d87e12b5535de29335401a7df4fc190ca251beeff6fa5f8bd3be47cefea6533dcd7bdc1120dcad91504b5b428d5c5ac20446c6849511081b96dbb67668dd81f42b3c46e5a68b52c85a26feee49d4f62b3f8f39e6feaaffc91a8d637a697d63b2e68806ce8919c1133c44fbf2d99c91c6650
console.log(rs.hextob64(enc));
//OLWDWAHaFKfvsdFe58kth+ErVTXeKTNUAaffT8GQyiUb7v9vpfi9O+R87+plM9zXvcESDcrZFQS1tCjVxawgRGxoSVEQgbltu2dmjdgfQrPEblpotSyFom/u5J1PYrP4855v6q/8kajWN6aX1jsuaIBs6JGcETPET78tmckcZlA=
// 解密
var prv = rs.KEYUTIL.getKey(privateKey);
var dec = rs.KJUR.crypto.Cipher.decrypt(enc, prv);
console.log(new Date().getTime() - date1)
console.log("jsrsasign decrypt: " + dec);
//jsrsasign decrypt:src

4.2支持jsrsasign生产的密钥

var rs = require('jsrsasign');
const path = require('path');
const root = __dirname;
// 生产密钥对
var date1 = new Date().getTime()
var rsaKeypair = rs.KEYUTIL.generateKeypair("RSA", 512);
console.log(new Date().getTime() - date1) //314
// 密钥对象获取pem格式的密钥
var pub = rs.KEYUTIL.getPEM(rsaKeypair.pubKeyObj);
var prv = rs.KEYUTIL.getPEM(rsaKeypair.prvKeyObj,'PKCS8PRV');
console.log(pub);
// -----BEGIN PUBLIC KEY-----
// MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMBAvyyQ7ZvG+vKmAvb3zrA816O5i0tg
// FWKaveQbuZ/xwFdHRFB/f27pEKo7jbUWBMDDIBGNu7QZ/uG6birsJ0UCAwEAAQ==
// -----END PUBLIC KEY-----
console.log(prv);
// -----BEGIN PRIVATE KEY-----
// MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEAt1huGEUWoa1z2Ruw
// DRdJX+EWfZI64QNJ8UUVwSyEou7PB0fwTdczr5UbxjH2zRsNQo5wPHAOTKvr2GGh
// DH1nFwIDAQABAkBBjwM+9mVTRnxoI3heFfeMqyWpnQIkt1JXTUasHkkHIRVaZMFx
// b5rnSRSpj/QzWHUAT9CaffZqRoqM9EWig2wRAiEA8WRJZpvac7CK0OPvE8sD2THZ
// D49fzyvNf9dRZUI9IAsCIQDCcN9XASIhFOkrrVC9ySr4+m+VTlJfVKO6P95/7V9A
// pQIhALrPH7bW2mI5t9Qc8YJh1GKbnx3ZmQ3dGjXbTlSMxH0tAiEAlvMFkAfjNQeE
// 1VGhwxSvdccGZUT+kd+lk+wNkgb30bkCIQDwS8xr2P4bm3vkVBCDvrttjTKPIjtG
// apnvPZ2yh1OIqQ==
// -----END PRIVATE KEY-----
// 加密
// 读取解析pem格式的秘钥, 生成秘钥实例 (RSAKey) 
var date1 = new Date().getTime()
// 公钥加密
var enc = rs.KJUR.crypto.Cipher.encrypt('src', rsaKeypair.pubKeyObj);
console.log(enc);
//5ee71af15aa5fcc652d8ccaab81a1883e49232b4726c31a538ed98347de972c3f86cb736fa4622f14c7bb872b33ebc0bb57657dcdddc22992c27d1c71600a8f4
console.log(rs.hextob64(enc));
// Xuca8Vql/MZS2MyquBoYg+SSMrRybDGlOO2YNH3pcsP4bLc2+kYi8Ux7uHKzPrwLtXZX3N3cIpksJ9HHFgCo9A==

// 私钥解密
//    var enc=rs.hextob64(enc)
var dec = rs.KJUR.crypto.Cipher.decrypt(enc, rsaKeypair.prvKeyObj);
console.log("jsrsasign decrypt: " + dec);
// jsrsasign decrypt: src

4.3 支持crypto生产的密钥,并且加密方式和crypto的解密方式通用

  • (需要指定生成密钥对的填充方式,及解密时私钥的填充方式为pkcs1)
var crypto = require('crypto');
var rs = require('jsrsasign');

date1 = new Date().getTime()
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
    modulusLength: 512,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs1',
        format: 'pem',
        // cipher: 'aes-256-cbc',
        // passphrase: 'top secret'
    }
});
console.log(new Date().getTime() - date1)

var date1 = new Date().getTime()
var pub = rs.KEYUTIL.getKey(publicKey);
var enc = rs.KJUR.crypto.Cipher.encrypt('src', pub);
// console.log(enc);
console.log(rs.hextob64(enc));
//DZzrNuD730u+5AsRcikeSMoU1yCscUutQlqXJ5WU/2iUSuCsJxyOJOJjVbI87r7EGL7EjeS278PziN9pZ7TWOg==

// 使用jsrsasign解密
var prv = rs.KEYUTIL.getKey(privateKey);
var dec = rs.KJUR.crypto.Cipher.decrypt(enc, prv);
console.log(new Date().getTime() - date1)
console.log("jsrsasign decrypt: " + dec);
//jsrsasign decrypt:src
// 使用crypto模块解密
var enc = rs.hextob64(enc)
const dec2 = crypto.privateDecrypt({
    key: privateKey,
    padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(enc.toString('base64'), 'base64')).toString();
console.log("jsrsasign decrypt: " + dec2);
//jsrsasign decrypt:src

5.参考

  • 5.1https://kjur.github.io/jsrsasign/api/index.html
  • 5.2前端 rsa加密参数的实现(加密库:jsrsasign)
  • 5.3https://www.npmjs.com/package/jsrsasign
  • 5.4jsrsasign使用笔记(加密,解密,签名,验签)https://www.jianshu.com/p/b32fc387d8ad

六、JSEncrypt

1.优点:

支持rsa加密解密,支持标准的rsa密钥;比jsrsasign加密速度稍微快几毫秒;加密内容可以用nodejs标准的加密模块crypto解密

2.缺点:

不能生产rsa密钥;

3.安装:

  • Web端

html文件引入jsencrypt.min.js(https://github.com/travist/jsencrypt)

  • Nodejs端:

npm i node-jsencrypto

4.Demo

4.1nodejs端node-jsencrypt

ar crypto = require('crypto');

date1 = new Date().getTime()
var { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
    modulusLength: 512,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs1',
        format: 'pem',
        // cipher: 'aes-256-cbc',
        // passphrase: 'top secret'
    }
});
console.log(new Date().getTime() - date1)
// 引入加密模块
JSEncrypt = require('node-jsencrypt')
// 加密
date1 = new Date().getTime()
const jsEncrypt = new JSEncrypt()
jsEncrypt.setPublicKey(publicKey)
enc = jsEncrypt.encrypt('src')
console.log(new Date().getTime() - date1)
//解密
date1 = new Date().getTime()
var decrypt = new JSEncrypt();
decrypt.setPrivateKey(privateKey);
var uncrypted = decrypt.decrypt(enc);
console.log(new Date().getTime() - date1)
// 使用crypto模块解密
const dec3 = crypto.privateDecrypt({
    key: privateKey,
    padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(enc.toString('base64'), 'base64')).toString();
console.log("jsrsasign decrypt: " + dec2);

4.2web端JSEncrypt

<!doctype html>
<html>

<head>
  <title>JavaScript RSA Encryption</title>
  <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
  <script src=""></script>
  <script src="./jsrsasign-all-min.js"></script>
  <script type="text/javascript">
    // Call this code when the page is done loading.
    $(function () {
      // Run a quick encryption/decryption when they click.
      $('#testme').click(function () {
        // Encrypt with the public key...
        var date1 = new Date().getTime()
        var encrypt = new JSEncrypt();
        encrypt.setPublicKey($('#pubkey').val());
        var encrypted = encrypt.encrypt('123');
        console.log(new Date().getTime() - date1)
        // jsrsasign加密
        var date1 = new Date().getTime()
        var pub = new KEYUTIL.getKey($('#pubkey').val());
        var enc = KJUR.crypto.Cipher.encrypt('src', pub);
        //console.log(rs.hextob64(enc));
        console.log(new Date().getTime() - date1)
        // Decrypt with the private key...
        var decrypt = new JSEncrypt();
        decrypt.setPrivateKey($('#privkey').val());
        var uncrypted = decrypt.decrypt(encrypted);
        // Now a simple check to see if the round-trip worked.
        if (uncrypted == $('#input').val()) {
          alert('It works!!!');
        }
        else {
          alert('Something went wrong....');
        }
      });
    });
  </script>
</head>
<body>
  <label for="privkey">Private Key</label><br />
  <textarea id="privkey" rows="15" cols="65">-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAL1o9pLm8ErFu4rzJIizJY+4W3aTZlrEv0WhRKxVXkCt5Lwm2+aE
/YpwtEkIdCpwr//PTLN7y554MhR7tFNu748CAwEAAQJABOkKs2Y/RoD3yrNg+BZE
3AP4gwtxNNsy5jg3EoyoA98yxQQAe3/093Qll19zQVCwHqM9Ijy7EmK6k8NcE5TF
oQIhAOrEV8k3LiYCxnn2ogbRkgRQH1xUju01pvX5fpRBpy4RAiEAzopzsBEP9Bi/
6yNh9n+aMqTI1ceqhj71jbK62gN9I58CIQCAYB9U43yzwl7AALK3IdBD1YBgn8iM
RANpjCXAcmo10QIgHbczv9AkoHTzH8x+aq2fLMwijQdmFFx4jcN6OKWp2ncCIFZX
fwHIo+ZpL/avDe1P5I8FY9KlKnTXDCM9GnI9Unwb
-----END RSA PRIVATE KEY-----</textarea><br />
  <label for="pubkey">Public Key</label><br />
  <textarea id="pubkey" rows="15" cols="65">-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL1o9pLm8ErFu4rzJIizJY+4W3aTZlrE
v0WhRKxVXkCt5Lwm2+aE/YpwtEkIdCpwr//PTLN7y554MhR7tFNu748CAwEAAQ==
-----END PUBLIC KEY-----</textarea><br />
  <label for="input">Text to encrypt:</label><br />
  <textarea id="input" name="input" type="text" rows=4 cols=70>This is a test!</textarea><br />
  <input id="testme" type="button" value="Test Me!!!" /><br />
</body>
</html>

5.参考

  • 5.1https://www.npmjs.com/package/jsencrypt?activeTab=versions
  • 5.2在VUE中使用RSA加密解密加签解签