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
1. 传⼊pem标准格式的秘钥字符串, 解析⽣成秘钥实例: RSAKey. 标准的pem格式秘钥含有开始标记和结束标记, 如本⽂使⽤的秘钥: -----BEGIN xxx-----, -----END xxx-----. ⾄于xxx的具体内
容不是太重要, 代码⾥⾃动通过正则清洗掉头和尾标记, 所以真的写成-----BEGIN xxx-----也没有关系.
2. 调⽤encrypt⽅法, 传⼊明⽂和公钥实例, 加密后的返回值是16进制字符串.
3. 所以, 需要将其转为常⽤的Base64编码. 如果为了⽅便放在URL上, 建议使⽤使⽤hextob64u(enc), 它会将+替换成-,/替换成_,去掉尾部补全的=. 不建议使⽤
encodeURIComponent, 这种编码⽅式会更⼤程度上扩⼤原数据的体积(Base64只会增加1/3, ⽽url采⽤的16进制⽅式, 会增加1倍, 具体原因可另外⾕歌).
基本类似加密流程.
// 加密
// 读取解析pem格式的秘钥, ⽣成秘钥实例 (RSAKey)
var pub = Key(pk);
var enc = pt(src,pub);
//    console.log(enc);
//    console.log(hextob64(enc));
// 解密
var prv = Key(priK);
var dec = pto.Cipher.decrypt(enc,prv);
console.log("jsrsasign decrypt: "+dec);
jsrsasign
RSA签名验签基本流程如下, 当然, 都会被封装成两个⽅法搞定: 签名和验签.
签名:
1. 指定⼀款摘要算法, 如sha1对原⽂哈希.
2. 上述哈希前⾯填补上摘要算法标识, 便于验签时识别⽤的什么算法.
3. ⽤rsa私钥对上述哈希加密.
4. 完成签名.
验签:
1. ⽤rsa公钥对签名解密, 得到摘要.
2. 原⽂取摘要.
3. 对⽐两个摘要, ⼀样则验签通过, 否则验签不通过.
使⽤jsrsasign
⽹上资料很多⽐较雷同, 在签名时代码开起来⽐较⿇烦.
这⾥先给出⼤家通常步骤, 最后给出我⾃⼰看源码总结简化调⽤⽅式.
⽅式1: 创建秘钥实例 -> 构建Signature实例 -> 传⼊秘钥实例, 初始化 ->
// ⽅式1: 先建⽴ key 对象, 构建 signature 实例, 传⼊ key 初始化 -> 签名
var key = Key(priK);
console.log(key);
// 创建 Signature 对象
let signature=pto.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的基础上, 去掉显⽰读取私钥, 去掉初始化步骤(
// 创建 Signature 对象
let signature=pto.Signature({alg:"SHA1withRSA",prvkeypem:priK});    //!这⾥指定私钥 pem!
signature.updateString(src);
let a = signature.sign();
let sign = hextob64(a);
console.log(sign);
注意点看注释.
// 验签
// !要重新new ⼀个Signature, 否则, 取摘要和签名时取得摘要不⼀样, 导致验签误报失败(原因不明)!
let signatureVf = pto.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 = Key(priK);
// Java加密的密⽂(Base64Url)
let encJava = "8S2KlcygY8eUvq_Dzro81IQd6oA5fxW9l9hsy8iOvtByMMJI1wKedO5sR_pJmJFYEZl6wfD4BQ-FzvSYftnO5xO8kJaHNtnrFE7R0mqpLIkf6aN02K4F9zWLad3emFTN8Ze_GqooVaa0oX6XHqpDFBQJF3kUB6cfS9mDJNq_boE"
// 解密 / Base64Url -> 16进制 / 私钥实例
var dec4Java = pto.Cipher.decrypt(b64utohex(encJava), prv);
console.log("jsrsasign decrypt 4 java: "+dec4Java);
// 验证Java的签名
// 构建Signature实例
// 这⾥ prvkeypem 放公钥pem看起来有点怪, 但是这是可⾏的, 内部还是使⽤的上⽂经常出现的 Key(pk) 来⽣成公钥实例的
var sign4Java = pto.Signature({alg:"SHA1withRSA",prvkeypem:pk});
sign4Java.updateString(src);
/
/ Java⽣成签名
var signByJava = "O6uEQFPPEmRfEiZcLQjMB7yYLpO2ohmCJvn95Izu8LveUWqFtoYJbvWRYwKCCV-Z3iurjpEw5nExvHQghwoYIxpB7p97G29WXWhfiaA0AUNlxDM2cOus-CIAq-Kyqee7vDsewp6ixaHThu0CxoPFGpBTpo5kuOFlPFR6CRS3Q9    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 {*}
*/
var G = ASN1HEX, L = G.getChildIdx, v = G.getV, d = G.getVbyList, c = pto, 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");
Key(m, null, "pkcs5prv")
}
...
/**
*
* @param {String} e 明⽂
* @param {RSAKey} f 公钥
* @param {String} d 算法名称, ⼤写, 如 RSA, 缺省 RSA
* @returns {String} 16进制字符串
*/
if (f instanceof RSAKey && f.isPublic) {
var c = AlgByKeyAndName(f, d);
if (c === "RSA") {
pt(e)
}
if (c === "RSAOAEP") {
ptOAEP(e, "sha1")
}
var b = c.match(/^RSAOAEP(\d+)$/);
if (b !== null) {
ptOAEP(e, "sha" + b[1])
}
pt: unsupported algorithm for RSAKey: " + d
} else {
pt: unsupported key or algorithm"
}
};
/**
*
* @param {String} e 16进制密⽂字符串
* @param {RSAKey} f 私钥
* @param {String} d 算法名称, ⼤写, 如 RSA, 缺省 RSA
* @returns {String} 明⽂
*/
if (f instanceof RSAKey && f.isPrivate) {
var c = AlgByKeyAndName(f, d);
if (c === "RSA") {
return f.decrypt(e)
}
if (c === "RSAOAEP") {
return f.decryptOAEP(e, "sha1")
js代码加密软件}
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
*/
var q = null;
...
/**签名⽅法*/
this.sign = function () {
...
} else {
if (this.prvKey instanceof RSAKey && this.pubkeyAlgName === "rsa") {
this.hSign = this.prvKey.signWithMessageHash(this.sHashHex, this.mdAlgName)  ...

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。