记录一下jsrsasign加密,解密,签名,验签

为防止原博主删除原文章,这里记录一下

你将会收获:

1.js如何加密, 解密
2.js如何签名, 验签
3.js和Java交互如何相互解密, 验签(重点)

通过谷歌, 发现jsrsasign库使用者较多. 查看api发现这个库功能很健全. 本文使用方法, 是结合网上千篇一律的博文, 加上我自己查看源码总结出来的.
公用代码:

// 公钥
   let pk="-----BEGIN PUBLIC KEY-----\n" +
        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3XSdz1MnzazBEN5KOfTx0IyVJ\n" +
        "Z5wb57isrCuHDhnYXwtmdhQalgII0fozeeFpMpAvlnmHC1kpW7XVGvZnLx3bWbCE\n" +
        "bf+pMSW4kmQuI+5cxRUJbCl7sdaODBrINgERHPICVC18AJLThEVMHyjuR6Jn4zQm\n" +
        "yYNbReSktY/BrFTvMQIDAQAB\n" +
        "-----END PUBLIC KEY-----";
  // 私钥
    let priK = "-----BEGIN PRIVATE KEY-----\n" +
        "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAPddJ3PUyfNrMEQ3\n" +
        "ko59PHQjJUlnnBvnuKysK4cOGdhfC2Z2FBqWAgjR+jN54WkykC+WeYcLWSlbtdUa\n" +
        "9mcvHdtZsIRt/6kxJbiSZC4j7lzFFQlsKXux1o4MGsg2AREc8gJULXwAktOERUwf\n" +
        "KO5HomfjNCbJg1tF5KS1j8GsVO8xAgMBAAECgYEA6eG1JMrj63jEmStmMb1txG1a\n" +
        "mu4Q5z2QGgtr2HVXsIIlGEq6tWxyHf7TL4qkuz9onuYKn8n2Eqm44fZtVaBx+5ES\n" +
        "zRpIvlTvaxmVu0HZ1hYAzUw1XyRnXNMKpL5tT4GCjm8+QGPzlGxgXI1sNg8r9Jaw\n" +
        "9zRUYeA6LQR9RIMkHWUCQQD8QojjVoGjtiunoh/N8iplhUszZIavAEvmDIE+kVy+\n" +
        "pA7hvlukLw6JMc7cfTcnHyxDo9iHVIzrWlTuKRq9KWVLAkEA+wgJS2sgtldnCVn6\n" +
        "tJKFVwsHrWhMIU29msPPbNuWUD23BcKE/vehIyFu1ahNA/TiM40PEnzprQ5JfPxU\n" +
        "16S78wJANTfMLTnYy7Lo7sqTLx2BuD0wqjzw9QZ4/KVytsJv8IAn65P/PVn4FRV+\n" +
        "8KEx+3zmF7b/PT2nJRe/hycAzxtmlQJBAMrFwQxEqpXfoAEzx4lY2ZBn/nmaR/SW\n" +
        "4VNEXCbocVC7qT1j1R5HVMgV13uKiTtq8dUGWmhqsi7x3XayNK5ECPUCQQDZaAN6\n" +
        "tvIHApz9OLsXSw0jZirQ6KEYdharXbIVDy1W1sVE3lzLbqLdFp1bxAHQIvsYS5PM\n" +
        "A9veSJh372RLJKkj\n" +
        "-----END PRIVATE KEY-----";
 
  // 原文
  var src = "好厉害";

jsrsasign加密和解密

加密

传入pem标准格式的秘钥字符串, 解析生成秘钥实例: RSAKey. 标准的pem格式秘钥含有开始标记和结束标记, 如本文使用的秘钥: -----BEGIN xxx-----, -----END xxx-----. 至于xxx的具体内容不是太重要, 代码里自动通过正则清洗掉头和尾标记, 所以真的写成-----BEGIN xxx-----也没有关系.
调用encrypt方法, 传入明文和公钥实例, 加密后的返回值是16进制字符串.
所以, 需要将其转为常用的Base64编码. 如果为了方便放在URL上, 建议使用使用hextob64u(enc), 它会将+替换成-,/替换成_,去掉尾部补全的=. 不建议使用encodeURIComponent, 这种编码方式会更大程度上扩大原数据的体积(Base64只会增加1/3, 而url采用的16进制方式, 会增加1倍, 具体原因可另外谷歌).

解密
基本类似加密流程.

	// 加密
    // 读取解析pem格式的秘钥, 生成秘钥实例 (RSAKey) 
    var pub = KEYUTIL.getKey(pk);
    var enc = KJUR.crypto.Cipher.encrypt(src,pub);
	//    console.log(enc);
	//    console.log(hextob64(enc));

   	// 解密
    var prv = KEYUTIL.getKey(priK);
    var dec = KJUR.crypto.Cipher.decrypt(enc,prv);
    console.log("jsrsasign decrypt: "+dec);

jsrsasign签名和验签

通用流程
RSA签名验签基本流程如下, 当然, 都会被封装成两个方法搞定: 签名和验签.
签名:

指定一款摘要算法, 如sha1对原文哈希.
上述哈希前面填补上摘要算法标识, 便于验签时识别用的什么算法.
用rsa私钥对上述哈希加密.
完成签名.

验签:

用rsa公钥对签名解密, 得到摘要.
原文取摘要.
对比两个摘要, 一样则验签通过, 否则验签不通过.

使用jsrsasign签名验签
签名
网上资料很多比较雷同, 在签名时代码开起来比较麻烦.
这里先给出大家通常步骤, 最后给出我自己看源码总结简化调用方式.
方式1: 创建秘钥实例 -> 构建Signature实例 -> 传入秘钥实例, 初始化 -> 签名

    // 方式1: 先建立 key 对象, 构建 signature 实例, 传入 key 初始化 -> 签名
    var key = KEYUTIL.getKey(priK);
    console.log(key);
    // 创建 Signature 对象
    let signature=new KJUR.crypto.Signature({alg:"SHA1withRSA"});
    // 传入key实例, 初始化signature实例
    signature.init(key);
    // 传入待签明文
    signature.updateString(src);
    // 签名, 得到16进制字符结果
    let a = signature.sign();
    let sign = hextob64(a);

    console.log(sign);

方式2: 我的简化方式: 方式1的基础上, 去掉显示读取私钥, 去掉初始化步骤

init(..)
    // 创建 Signature 对象
    let signature=new KJUR.crypto.Signature({alg:"SHA1withRSA",prvkeypem:priK});    //!这里指定 私钥 pem!
    signature.updateString(src);
    let a = signature.sign();
    let sign = hextob64(a);

    console.log(sign);

验签
注意点看注释.

    // 验签
    // !要重新new 一个Signature, 否则, 取摘要和签名时取得摘要不一样, 导致验签误报失败(原因不明)!
    let signatureVf = new KJUR.crypto.Signature({alg:"SHA1withRSA",prvkeypem:pk});
    signatureVf.updateString(src);
    // !接受的参数是16进制字符串!
    let b = signatureVf.verify(b64tohex(sign));
    console.log("jsrsasign verify: "+b);

jsrsasign和Java交互

这是很关键的, 任何js插件在好用, 如果和Java不能兼容, 也是白搭. 之前就是过jsencrypt.js库, 但是发现Java在签名验签时貌似不兼容.

    // 解密Java的密文
    var prv = KEYUTIL.getKey(priK);
    // Java加密的密文(Base64Url)
    let encJava = "8S2KlcygY8eUvq_Dzro81IQd6oA5fxW9l9hsy8iOvtByMMJI1wKedO5sR_pJmJFYEZl6wfD4BQ-FzvSYftnO5xO8kJaHNtnrFE7R0mqpLIkf6aN02K4F9zWLad3emFTN8Ze_GqooVaa0oX6XHqpDFBQJF3kUB6cfS9mDJNq_boE";
    // 解密 / Base64Url -> 16进制 / 私钥实例
    var dec4Java = KJUR.crypto.Cipher.decrypt(b64utohex(encJava), prv);
    console.log("jsrsasign decrypt 4 java: "+dec4Java);


    // 验证Java的签名
    // 构建Signature实例
    // 这里 prvkeypem 放公钥pem看起来有点怪, 但是这是可行的, 内部还是使用的上文经常出现的 KEYUTIL.getKey(pk) 来生成公钥实例的
    var sign4Java = new KJUR.crypto.Signature({alg:"SHA1withRSA",prvkeypem:pk});
    sign4Java.updateString(src);
    // Java生成签名
    var signByJava = "O6uEQFPPEmRfEiZcLQjMB7yYLpO2ohmCJvn95Izu8LveUWqFtoYJbvWRYwKCCV-Z3iurjpEw5nExvHQghwoYIxpB7p97G29WXWhfiaA0AUNlxDM2cOus-CIAq-Kyqee7vDsewp6ixaHThu0CxoPFGpBTpo5kuOFlPFR6CRS3Q9M";
    var b2 = sign4Java.verify(b64utohex(signByJava));
    console.log("jsrsasign verify 4 java: " + b2);

本文测试代码的运行结果:

jsrsasign signing: O6uEQFPPEmRfEiZcLQjMB7yYLpO2ohmCJvn95Izu8LveUWqFtoYJbvWRYwKCCV+Z3iurjpEw5nExvHQghwoYIxpB7p97G29WXWhfiaA0AUNlxDM2cOus+CIAq+Kyqee7vDsewp6ixaHThu0CxoPFGpBTpo5kuOFlPFR6CRS3Q9M=
jsrsasign verify: true
jsrsasign decrypt: 好厉害
jsrsasign decrypt 4 java: 好厉害
jsrsasign verify 4 java: true

附录: jsrsasign部分方法源码

本来想讲测试用的源文件附上来, 但是这里貌似不支持附件, 所以部分主要的方法代码. 通过阅读, 加上了部分注释, 所以api看起来更容易理解. 另外, 本文调用方式是在页面引入js方式使用的, 若使用其他框架, 可能调用方式略有区别, 但是核心api是不变的.

/**
 * 
 * @param l RSAKey / ECDSA / DSA / 标准的pem格式秘钥Base64字符
 * @param k
 * @param n
 * @returns {*}
 */
KEYUTIL.getKey = function (l, k, n) {
    var G = ASN1HEX, L = G.getChildIdx, v = G.getV, d = G.getVbyList, c = KJUR.crypto, i = c.ECDSA, C = c.DSA,
        w = RSAKey, M = pemtohex, F = KEYUTIL;
  ...
  // 这里通过判断pem结束标记来判断传入的是什么类型的秘钥字符
 if (l.indexOf("-END PUBLIC KEY-") != -1) {
        var O = pemtohex(l, "PUBLIC KEY");
        return F._getKeyFromPublicPKCS8Hex(O)
    }
    if (l.indexOf("-END RSA PRIVATE KEY-") != -1 && l.indexOf("4,ENCRYPTED") == -1) {
        var m = M(l, "RSA PRIVATE KEY");
        return F.getKey(m, null, "pkcs5prv")
    }
  ...
/**
 *
 * @param {String} e 明文
 * @param {RSAKey} f 公钥
 * @param {String} d 算法名称, 大写, 如 RSA, 缺省 RSA
 * @returns {String} 16进制字符串
 */
KJUR.crypto.Cipher.encrypt = function (e, f, d) {
    if (f instanceof RSAKey && f.isPublic) {
        var c = KJUR.crypto.Cipher.getAlgByKeyAndName(f, d);
        if (c === "RSA") {
            return f.encrypt(e)
        }
        if (c === "RSAOAEP") {
            return f.encryptOAEP(e, "sha1")
        }
        var b = c.match(/^RSAOAEP(\d+)$/);
        if (b !== null) {
            return f.encryptOAEP(e, "sha" + b[1])
        }
        throw"Cipher.encrypt: unsupported algorithm for RSAKey: " + d
    } else {
        throw"Cipher.encrypt: unsupported key or algorithm"
    }
};
/**
 *
 * @param {String} e 16进制密文字符串
 * @param {RSAKey} f 私钥
 * @param {String} d 算法名称, 大写, 如 RSA, 缺省 RSA
 * @returns {String} 明文
 */
KJUR.crypto.Cipher.decrypt = function (e, f, d) {
    if (f instanceof RSAKey && f.isPrivate) {
        var c = KJUR.crypto.Cipher.getAlgByKeyAndName(f, d);
        if (c === "RSA") {
            return f.decrypt(e)
        }
        if (c === "RSAOAEP") {
            return f.decryptOAEP(e, "sha1")
        }
        var b = c.match(/^RSAOAEP(\d+)$/);
        if (b !== null) {
            return f.decryptOAEP(e, "sha" + b[1])
        }
        throw"Cipher.decrypt: unsupported algorithm for RSAKey: " + d
    } else {
        throw"Cipher.decrypt: unsupported key or algorithm"
    }
};
/**
 *
 * @param {Object}o o.alg:算法名称; o.prov:支持的js文件标识; o.prvkeypem:pem格式秘钥(base64);
 * @constructor
 */
KJUR.crypto.Signature = function (o) {
    var q = null;
  ...
            /**签名方法*/
            this.sign = function () {
          ...
                    } else {
                        if (this.prvKey instanceof RSAKey && this.pubkeyAlgName === "rsa") {
                            this.hSign = this.prvKey.signWithMessageHash(this.sHashHex, this.mdAlgName)
  ...
`jsrsasign` 是一个用纯 JavaScript 实现的密码学算法库,它支持 RSA、AES、SHA、HMAC 等多种加密和散列算法。使用 `jsrsasign` 进行 RSA 加密的基本步骤如下: 1. 引入 `jsrsasign` 库到你的项目中。如果你是通过 npm 安装,可以使用如下命令: ```bash npm install jsrsasign ``` 2. 使用 `jsrsasign` 库中的 RSA 类来生成密钥对或加载已有的公钥/私钥。 3. 使用公钥对数据进行加密。 4. (可选)使用私钥对数据进行解密。 下面是一个简单的示例代码,展示了如何使用 `jsrsasign` 进行 RSA 加密: ```javascript // 引入 jsrsasign 库 var KJUR = require('jsrsasign'); // 或者通过 HTML 引入 jsrsasign 的脚本文件 // <script src="https://cdn.jsdelivr.net/npm/jsrsasign@latest/dist/jsrsasign-all-min.js"></script> // 生成 RSA 密钥对,参数是密钥长度,这里以 2048 位为例 var pubKey = KJUR.KEYUTIL.generateKeyPair("rsa", 2048); var privateKey = pubKey.privateKey; // 假设要加密的消息 var msg = "Hello, RSA!"; // 使用公钥对消息进行加密 var encryptedData = KJUR.KEYUTIL.encrypt("RSA", msg, pubKey.pubKeyHex, { alg: 'SHA256withRSA', enc: 'base64' }); // 输出加密后的数据 console.log("Encrypted Data: " + encryptedData); // 如果需要,可以使用私钥进行解密 // var decryptedData = KJUR.KEYUTIL.decrypt("RSA", encryptedData, privateKey, { alg: 'SHA256withRSA', enc: 'base64' }); // console.log("Decrypted Data: " + decryptedData); ``` 请注意,当使用公钥进行加密时,只有相应的私钥才能解密。在实际应用中,应当妥善保管私钥,防止泄露。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值