jsencrypt代碼分析——openssl的rsa加密解密在js的實現


在js上做rsa,感覺jsencrypt這個是封裝的比較好的,但用起來還是遇到了些坑,所以踩進代碼里填填坑~

項目在這里 https://github.com/travist/jsencrypt

【rsa算法】

首先科普一下rsa:公鑰私鑰成對,用其中一個加密只能用另一個解密,常用公鑰加密私鑰解密。

一開始看到斯坦佛那個庫,原始的算法實現:

長度,建議至少1024。模數n(常取默認65537)兩邊都要用。

指數e,和n一起就是公鑰。指數d,和n一起就是私鑰。質數p和q用於生成密鑰對,然后就丟棄不公開。

具體算法網上一大把,就不多說了。

【ssl/ssh密鑰對】

用服務器上的工具生成的密鑰對,格式一般是如下,有開始行結束行,內容用base64轉碼。這里涉及一些國際編碼規范,代碼分析時在逐一解釋。

-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIA4OVgB4FRq4l5zjEmd4r/jswRcHlZQ
kg10p9rzG3VyXCPpa/ZkwOYy+kGq7a7BjAKTpic2cUNRim4m8HKTdc8CAwEAAQ==
-----END PUBLIC KEY-----

【代碼分析】

加載公鑰:setPublicKey->setKey->JSEncryptRSAKey->parseKey

RSAKey.prototype.parseKey = function (pem) {...}

注釋寫得非常好!

/**
...省略...
 *This method accepts public key * in the rsaencryption pkcs #1 format (oid: 1.2.840.113549.1.1.1). * The format is defined as: * PublicKeyInfo ::= SEQUENCE { * algorithm AlgorithmIdentifier, * PublicKey BIT STRING * } * Where AlgorithmIdentifier is: * AlgorithmIdentifier ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, the OID of the enc algorithm * parameters ANY DEFINED BY algorithm OPTIONAL (NULL for PKCS #1) * } * and PublicKey is a SEQUENCE encapsulated in a BIT STRING * RSAPublicKey ::= SEQUENCE { * modulus INTEGER, -- n * publicExponent INTEGER -- e * }
*/

代碼如下:

 1 RSAKey.prototype.parseKey = function (pem) {
 2   try {
 3 ...
 4     var der = reHex.test(pem) ? Hex.decode(pem) : Base64.unarmor(pem);
 5     var asn1 = ASN1.decode(der);
 6 ...
 7     if (asn1.sub.length === 9) {
 8       // Parse the private key.
 9     }
10     else if (asn1.sub.length === 2) {
11       // Parse the public key.
12       var bit_string = asn1.sub[1];
13       var sequence = bit_string.sub[0];
14       modulus = sequence.sub[0].getHexStringValue();
15       this.n = parseBigInt(modulus, 16);
16       public_exponent = sequence.sub[1].getHexStringValue();
17       this.e = parseInt(public_exponent, 16);
18     }
19     else {
20       return false;
21     }
22     return true;
23   }
24   catch (ex) {
25     return false;
26   }
27 };

這里需要了解一下各種編碼格式。hex和base64就不解釋了。

ASN.1抽象語法標記,我的理解就是對數據進行結構化解析的規范:一個標准的ASN.1編碼對象有四個域:對象標識域、數據長度域、數據域以及結束標志(可選,在長度不可知情況下需要,openssl中沒有該標志)。

DER則是具體的編碼實現。 http://baike.baidu.com/view/100318.htm#4

PKCS#1則是RSA中最基礎的算法定義和密鑰規定,講人話就是定義了:公鑰是元組(n,e),算法是n=q*p等一系列公式。https://en.wikipedia.org/wiki/PKCS1

這段代碼兩處亮點,一個是else if (asn1.sub.length === 2)判斷公鑰(沒深究,反正靠子節點數判斷);
兩一個是var bit_string = asn1.sub[1];即前面有一段asn1.sub[0]是算法標識。

這里本身沒有坑,但是生成公鑰時如果命令不對應是會踩坑的(有一種命令是生成無算法標記的公鑰)~~~

 

加密:encrypt->RSAEncrypt->pkcs1pad2 | doPublic->RSADoPublic

pkcs1pad2是做補位處理:

入參 var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); 是(模位數+7)/8得到模的字節長度?

然后是一系列的移位操作,此處的作用是填入隨機位使得每次加密的密文都不一樣。http://blog.chinaunix.net/uid-21880738-id-1813144.html

RSADoPublic最終執行加密算法x^e (mod n)

 

至此,主要的算法、規范和坑基本上覆蓋了,私鑰和解密坑比較少,就不多說了。

最后,附送一個千年大坑,跨語言跨類庫的時候不小心可能會遇到的 http://blog.chinaunix.net/uid-23069658-id-4282969.html

於這個js類庫而言,-pubout出來的是可用的,而-RSAPublicKey_out出來的是不可用的。

與此對應,服務器openssl的類庫中PEM_read_RSA_PUBKEY()讀入是對應的,而PEM_read_RSAPublicKEY()讀入是不對應的。
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM