[Node.js] 對稱加密、公鑰加密和RSA


原文地址:http://www.moye.me/2015/06/14/cryptography_rsa/

 

引子

對於加解密,我一直處於一種知其然不知其所以然的狀態,項目核心部分並不倚重加解密算法時,可以勉強對付過去,一旦需要頻繁應用諸如 AES/RSA等算法,這種狀態就頗令人捉急了。

是時候了解一下原理了,所以找來了這本圖解密碼技術 給自己補補課:

圖解密碼技術

在該書深入淺出的指引下 ,補充了一些常識,在此進行一番梳理:

 

對稱加密算法(共享密鑰)

顧名思義,對稱加密就是用相同的密鑰進行加密和解密。說到對稱加密,異或加密又是一個不得不提的概念:

XOR(異或加密)

明文與密鑰進行一次 異或 (記做㊉ ) 運算將成為密文,密文再與密鑰進行一次異或運算將還原為明文:

例如,字符串“Wiki”(8位ASCII:01010111 01101001 01101011 01101001) 可以按如下的方式用密鑰11110011進行加密:

  01010111 01101001 01101011 01101001
\oplus 11110011 11110011 11110011 11110011
= 10100100 10011010 10011000 10011010

此種加密方法類似對稱加密,故解密的方式如下:

  10100100 10011010 10011000 10011010
\oplus 11110011 11110011 11110011 11110011
= 01010111 01101001 01101011 01101001

我們也可以用代碼驗證這一特性:

var key = 0b10110010;
var number = 22;
var encrypted = number ^ key;   // ㊉ => 密文 164
console.log(encrypted ^ key);   // ㊉ => 明文 22

光使用XOR,就能實現最基本的對稱加密,前提是選擇一個合適的密鑰。而其它對稱加密算法 如DES/AES 等,無不是在 XOR 基礎上的擴展。

AES 對稱加密

AES 即 高級加密標准(Advanced Encryption Standard),是取代前任標准(DES)成為新標准的一種對稱加密算法(DES被取代是因為其算法有缺陷,導致其能被短時間內暴力破解,所以DES被棄用,建議使用AES)。現行AES的實現算法為 Rijndael,是比利時科學家Joan Daemen 和 Vincent Rijmen 設計的分組密碼算法。

分組的意思是說,AES 算法的輸入是要分組的,分組長度可以在 128/196/256 比特中進行選擇(即一輪可加密這么多比特的明文生成同樣長度的密文,一次加密可能需要迭代多輪)。

模式

分組密碼算法只能加密固定長度的分組,但是我們需要加密的明文長度可能會超過分組密碼的分組長度,這時就需要對分組密碼算法進行迭代,以便將一段很長的明文全部加密。而迭代的方法就稱為分組密碼的模式。

模式有很多種類,分組密碼的主要模式有:

  • ECB 模式:Electronic Codebook mode (電子密碼本模式)
  • CBC 模式:Cipher Block Channing mode(密碼分組鏈接模式)
  • CFB 模式:Cipher FeedBack mode (密文反饋模式)
  • OFB 模式:Output FeedBack mode(輸出反饋模式)
  • CTR 模式:CounTeR mode(計數器模式)

這幾種模式的運作流程這里不做贅述,只需知道:

  • ECB過於簡單而不安全,已被棄用;
  • CFB可被施以重放攻擊;
  • OFB 和 CTR 都可被主動攻擊者反轉密文,而引起解密后明文中的相應比特也發生變化;CTR比之OFB,多出能支持並發計算的特性,此外CTR是流式密碼;
  • CBC雖不支持並行計算,但是卻是這些模式中最為安全的

下圖為CBC模式的算法結構圖:
AES cbc pattern

 

 

公鑰加密算法

 

公開密鑰加密,也稱為非對稱加密(asymmetric cryptography),一種密碼學算法類型,在這種密碼學方法中,需要一對密鑰,一個是私人密鑰,另一個則是公開密鑰。這兩個密鑰是數學相關,用某用戶密鑰加密后所得的信息,只能用該用戶的解密密鑰才能解密。如果知道了其中一個,並不能計算出另外一個。因此如果公開了一對密鑰中的一個,並不會危害到另外一個的秘密性質。稱公開的密鑰為公鑰;不公開的密鑰為私鑰。

公鑰加密解決了一個對稱加密的密鑰配送難題:如何安全的傳遞解密用的密鑰。方案是:不傳遞,加密者和解密者所持密鑰不一樣,特點如下:

  • 密文發送者只需要加密密鑰(公鑰
  • 密文接收者只需要解密密鑰(私鑰
  • 解密密鑰不可以被竊聽者獲取
  • 加密密鑰被竊聽者獲取也沒有安全問題

RSA 公鑰加密

RSA 是一種公鑰加密算法,它的名字是用三位開發者 R. Rivest、A. Shamir 和 L. Adleman 的姓氏首字母組成。RSA 可被用於公鑰密碼和數字簽名,算法於1983年在美國取得專利,目前該專利已過期(由於該算法在申請專利前就已經被發表了,在世界上大多數其它地區這個專利權不被承認)。

在RSA中,明文、密鑰和密文都是數字,公私鑰對是兩對數字:

  • 公鑰是 (數E,數N)
  • 私鑰是 (數D,數N)

加密就是 用明文數字的E次方求 mod N (取余)的結果,過程可以用下列公式來表達:

密文 = 明文 E mod N

對密文的數字的D次方求 mod N就可以得到明文,解密過程可以用如下公式來表達:

明文 = 密文 D mod N

生成密鑰對流程

(1) 求 N

隨機生成兩個很大的質數 p 和 q,那么 N = p * q

(2) 求 L

臨時量 L 僅被用於生成密鑰對的過程中,它是 p -1 和 q - 1 的最小公倍數(least common multiple,  lcm),用lcm(X, Y) 來表示 “X和Y的最小公倍數” ,則L可用公式表示為:

L = lcm(p-1, q-1)

(3) 求E

E 和 L 之間存在如下關系:

1 < E < L

gcd(E, L) = 1                     E 和 L 的最大公約數為1 (E 和 L 互質)

要找出滿足 gcd(E, L) = 1 的數,還是要使用偽隨機數生成器。通過偽隨機數生成器在 1 < E < L 的范圍內生成 E 的候選數,然后再判斷其是否滿足 gcd(E, L) = 1 這個條件。

(4) 求D

數D 是由數 E 計算得到的。D、E 和  L 之間必須具備如下關系:

1 < D < L

E * D mod L = 1

只要數 D 滿足上述條件,則通過 (數E,數N) 加密的密文,都可以通過  (數D,數N) 進行解密。

模擬實踐

用較小的數來實踐一把RSA的密鑰生成和加解密:

(1) 求N

選擇兩個質數,比如: p = 17  和 q = 19

N = 17 * 19 = 323

(2) 求 L

L = lcm (p-1, q-1) = lcm(16, 18) = 144

(3) 求 E

E 和 L 的最大公約數必須是1:

gcd(E, L) = 1

滿足條件的數 E有很多,100以內的質數有:

5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97

我們挑一個,比如 5 作為E,那么 公鑰對就是(E=5, N=323)

(4) 求 D

D 必須滿足:

E * D mod L = 1

在 E = 3 的情況下,D = 59 是滿足的,因為:

5 * 29 mod 144 = 1

所以 私鑰對就是(D=29, N=323)

公/私鑰對都有了,就可用於加解密了,假設有明文 42:

(5) 加密

密文 = 明文 E mod N  =>      42 5 mod 323 = 264

(6) 解密

明文 = 密文 D mod N =>      264 29 mod 323     => 數比較大,可分解求冪 :

(264 10 mod 323)  * (264 10 mod 323) * (264 9 mod 323) mod 323    =  42

RSA 的算法原理

想了解更多RSA背后的數學知識,可以參考阮一峰的 RSA算法原理(一) 和 (二)

 

AES和RSA的應用

混合密碼系統

通過比較,我們知道:

  • RSA 比 AES 更難破解,因為它不需要擔心密鑰在傳遞過程中有泄露,只存在暴力破解一種可能;
  • AES的優勢是以分組為輪,加解密速度非常快,一般而言,AES 速度上數百倍於 RSA

所以在實際應用中,我們會混合應用AES和RSA,比如 需要加密一個尺寸不小的文件,可能會這么干:

  1. 生成一個一次性隨機密鑰,算法上采用 AES 的CBC模式  aes-256-cbc(加密分組為256比特)對文件進行加密
  2. 加密完成后,為了安全的傳遞這個一次性隨機密鑰,我們使用 接收方的RSA公鑰 對其進行加密,隨加密后的文件一起發送
  3. 接收方使用私鑰進行解密,得到AES密鑰原文,並用其解密文件

例子

以上場景的應用,比如在 Node.js 中,可以這么實現:

(1) 生成 AES 隨機密鑰:

var passwdLength = 256;  // 初始化隨機向量長度
var aesPassword = require('crypto').randomBytes(passwdLength);
require('fs').writeFileSync('aesPassword', aesPassword); // 寫入文件供openssl使用

(2) 使用openssl aes 加密 filename代表的文件:

openssl enc -aes-256-cbc -kfile aesPassword -in filename -out filename.out

(3) 使用open rsa 加密密鑰

openssl enc rsautl -encrypt -pubin -inkey id_rsa.pub -in aespassword -out aespassword.out

將 filename.out 和 aespassword.out 一並發給對方即可,接收方使用openssl 進行一次逆操作即可實現解密。

 

參考

  1. openssl 命令手冊:http://netkiller.github.io/cryptography/openssl/index.html
  2. Node-RSA:  https://github.com/rzcoder/node-rsa

 

更多文章請移步我的blog新地址: http://www.moye.me/ 


免責聲明!

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



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