在js上作rsa,感受jsencrypt這個是封裝的比較好的,但用起來仍是遇到了些坑,因此踩進代碼裏填填坑~html
項目在這裏 https://github.com/travist/jsencryptgit
【rsa算法】github
首先科普一下rsa:公鑰私鑰成對,用其中一個加密只能用另外一個解密,經常使用公鑰加密私鑰解密。算法
一開始看到斯坦佛那個庫,原始的算法實現:服務器
長度,建議至少1024。模數n(常取默認65537)兩邊都要用。ssh
指數e,和n一塊兒就是公鑰。指數d,和n一塊兒就是私鑰。質數p和q用於生成密鑰對,而後就丟棄不公開。工具
具體算法網上一大把,就很少說了。ui
【ssl/ssh密鑰對】this
用服務器上的工具生成的密鑰對,格式通常是以下,有開始行結束行,內容用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()讀入是不對應的。