jsrsasign使用筆記(加密,解密,簽名,驗(yàn)簽)

你將會(huì)收獲:

js如何加密, 解密
js如何簽名, 驗(yàn)簽
js和Java交互如何相互解密, 驗(yàn)簽(重點(diǎn))
通過(guò)谷歌, 發(fā)現(xiàn)jsrsasign庫(kù)使用者較多. 查看api發(fā)現(xiàn)這個(gè)庫(kù)功能很健全. 本文使用方法, 是結(jié)合網(wǎng)上千篇一律的博文, 加上我自己查看源碼總結(jié)出來(lái)的.

公用代碼:
// 公鑰
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標(biāo)準(zhǔn)格式的秘鑰字符串, 解析生成秘鑰實(shí)例: RSAKey. 標(biāo)準(zhǔn)的pem格式秘鑰含有開(kāi)始標(biāo)記和結(jié)束標(biāo)記, 如本文使用的秘鑰: -----BEGIN xxx-----, -----END xxx-----. 至于xxx的具體內(nèi)容不是太重要, 代碼里自動(dòng)通過(guò)正則清洗掉頭和尾標(biāo)記, 所以真的寫成-----BEGIN xxx-----也沒(méi)有關(guān)系.
調(diào)用encrypt方法, 傳入明文和公鑰實(shí)例, 加密后的返回值是16進(jìn)制字符串.
所以, 需要將其轉(zhuǎn)為常用的Base64編碼. 如果為了方便放在URL上, 建議使用使用hextob64u(enc), 它會(huì)將+替換成-,/替換成_,去掉尾部補(bǔ)全的=. 不建議使用encodeURIComponent, 這種編碼方式會(huì)更大程度上擴(kuò)大原數(shù)據(jù)的體積(Base64只會(huì)增加1/3, 而url采用的16進(jìn)制方式, 會(huì)增加1倍, 具體原因可另外谷歌).
解密
基本類似加密流程.

// 加密
// 讀取解析pem格式的秘鑰, 生成秘鑰實(shí)例 (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簽名和驗(yàn)簽
通用流程
RSA簽名驗(yàn)簽基本流程如下, 當(dāng)然, 都會(huì)被封裝成兩個(gè)方法搞定: 簽名和驗(yàn)簽.
簽名:

指定一款摘要算法, 如sha1對(duì)原文哈希.
上述哈希前面填補(bǔ)上摘要算法標(biāo)識(shí), 便于驗(yàn)簽時(shí)識(shí)別用的什么算法.
用rsa私鑰對(duì)上述哈希加密.
完成簽名.
驗(yàn)簽:

用rsa公鑰對(duì)簽名解密, 得到摘要.
原文取摘要.
對(duì)比兩個(gè)摘要, 一樣則驗(yàn)簽通過(guò), 否則驗(yàn)簽不通過(guò).
使用jsrsasign簽名驗(yàn)簽
簽名
網(wǎng)上資料很多比較雷同, 在簽名時(shí)代碼開(kāi)起來(lái)比較麻煩.
這里先給出大家通常步驟, 最后給出我自己看源碼總結(jié)簡(jiǎn)化調(diào)用方式.

方式1: 創(chuàng)建秘鑰實(shí)例 -> 構(gòu)建Signature實(shí)例 -> 傳入秘鑰實(shí)例, 初始化 -> 簽名
// 方式1: 先建立 key 對(duì)象, 構(gòu)建 signature 實(shí)例, 傳入 key 初始化 -> 簽名
var key = KEYUTIL.getKey(priK);
console.log(key);
// 創(chuàng)建 Signature 對(duì)象
let signature=new KJUR.crypto.Signature({alg:"SHA1withRSA"});
// 傳入key實(shí)例, 初始化signature實(shí)例
signature.init(key);
// 傳入待簽明文
signature.updateString(src);
// 簽名, 得到16進(jìn)制字符結(jié)果
let a = signature.sign();
let sign = hextob64(a);

console.log(sign);

方式2: 我的簡(jiǎn)化方式: 方式1的基礎(chǔ)上, 去掉顯示讀取私鑰, 去掉初始化步驟(init(..))
// 創(chuàng)建 Signature 對(duì)象
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);

驗(yàn)簽
注意點(diǎn)看注釋.

// 驗(yàn)簽
// !要重新new 一個(gè)Signature, 否則, 取摘要和簽名時(shí)取得摘要不一樣, 導(dǎo)致驗(yàn)簽誤報(bào)失敗(原因不明)!
let signatureVf = new KJUR.crypto.Signature({alg:"SHA1withRSA",prvkeypem:pk});
signatureVf.updateString(src);
// !接受的參數(shù)是16進(jìn)制字符串!
let b = signatureVf.verify(b64tohex(sign));
console.log("jsrsasign verify: "+b);

jsrsasign和Java交互
這是很關(guān)鍵的, 任何js插件在好用, 如果和Java不能兼容, 也是白搭. 之前就是過(guò)jsencrypt.js庫(kù), 但是發(fā)現(xiàn)Java在簽名驗(yàn)簽時(shí)貌似不兼容.

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


// 驗(yàn)證Java的簽名
// 構(gòu)建Signature實(shí)例
// 這里 prvkeypem 放公鑰pem看起來(lái)有點(diǎn)怪, 但是這是可行的, 內(nèi)部還是使用的上文經(jīng)常出現(xiàn)的 KEYUTIL.getKey(pk) 來(lái)生成公鑰實(shí)例的
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);

本文測(cè)試代碼的運(yùn)行結(jié)果:

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

附錄: jsrsasign部分方法源碼

本來(lái)想講測(cè)試用的源文件附上來(lái), 但是這里貌似不支持附件, 所以部分主要的方法代碼. 通過(guò)閱讀, 加上了部分注釋, 所以api看起來(lái)更容易理解. 另外, 本文調(diào)用方式是在頁(yè)面引入js方式使用的, 若使用其他框架, 可能調(diào)用方式略有區(qū)別, 但是核心api是不變的.

/**
 * 
 * @param l RSAKey / ECDSA / DSA / 標(biāo)準(zhǔn)的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;
  ...
  // 這里通過(guò)判斷pem結(jié)束標(biāo)記來(lái)判斷傳入的是什么類型的秘鑰字符
 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進(jìn)制字符串
 */
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進(jìn)制密文字符串
 * @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文件標(biāo)識(shí); 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)
  ...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市娇豫,隨后出現(xiàn)的幾起案子匙姜,更是在濱河造成了極大的恐慌,老刑警劉巖冯痢,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件氮昧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡浦楣,警方通過(guò)查閱死者的電腦和手機(jī)袖肥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)振劳,“玉大人椎组,你說(shuō)我怎么就攤上這事±郑” “怎么了寸癌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)弱贼。 經(jīng)常有香客問(wèn)我蒸苇,道長(zhǎng),這世上最難降的妖魔是什么吮旅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任溪烤,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘檬嘀。我一直安慰自己莺葫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布枪眉。 她就那樣靜靜地躺著捺檬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贸铜。 梳的紋絲不亂的頭發(fā)上堡纬,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音蒿秦,去河邊找鬼烤镐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛棍鳖,可吹牛的內(nèi)容都是我干的炮叶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼渡处,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼镜悉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起医瘫,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤侣肄,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后醇份,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體稼锅,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年僚纷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矩距。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怖竭,死狀恐怖锥债,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情侵状,我是刑警寧澤赞弥,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布毅整,位于F島的核電站趣兄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏悼嫉。R本人自食惡果不足惜艇潭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蹋凝,春花似錦鲁纠、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至迄汛,卻和暖如春捍壤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鞍爱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工鹃觉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人睹逃。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓盗扇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親沉填。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疗隶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354