爲什麼需要隨機數?
- 生成密鑰
用於對稱密碼和消息認證碼 - 生成公鑰密碼
用於生成公鑰密碼和數字簽名 - 生成初始化向量 IV
用於分組密碼中的 CBC、CFB、OFB 模式 - 生成 nonce
用於防禦重放攻擊和分組密碼中的 CTR 模式 - 生成鹽
用於基於口令密碼的 PBE 等
用隨機數的目的是爲了提高密文的不可預測性,讓攻擊者無法一眼看穿。
什麼是隨機數?
給隨機數下一個嚴密的定義很難。只能從性質去區分一些隨機數的種類。
- 隨機性 —— 不存在統計學偏差,是完全雜亂的數列
- 不可預測性 —— 不能從過去的數列推測出下一個出現的數
- 不可重現性 —— 除非將數列本身保存下來,否則不能重現相同的數列
隨機性 | 不可預測性 | 不可重現性 | 備註 | 生成器 | ||
---|---|---|---|---|---|---|
弱僞隨機數 | ✅ | ❌ | ❌ | 只具備隨機性 | 不可用於密碼技術❌ | 僞隨機數生成器 PRNG (Preudo Random Number Generator) |
強僞隨機數 | ✅ | ✅ | ❌ | 具備不可預測性 | 可用於密碼技術✅ | 密碼學僞隨機數生成器 CPRNG (Cryptography secure Preudo Random Number Generator) |
真隨機數 | ✅ | ✅ | ✅ | 具備不可重現性 | 可用於密碼技術✅ | 真隨機數生成器 TRNG (True Random Number Generator) |
密碼技術上使用到的隨機數至少要達到不可預測性這一等級,即至少是強僞隨機數,最好是真隨機數。
隨機性
隨機性雖然看似雜亂無章,但是卻會被攻擊者看穿。所以被稱爲弱僞隨機數。
用線性同餘生成的僞隨機數列,看起來雜亂無章,但是實際上是能被預測的。
不可預測性
所謂不可預測性,即攻擊者在知道過去生成的僞隨機數列的前提下,依然無法預測出下一個生成出來的僞隨機數。不可預測性是通過使用其他的密碼技術來實現的,例如單向散列函數的單向性和機密性,來保證僞隨機數的不可預測性。
不可重現性
利用熱噪聲這一自然現象,英特爾開發出了能夠生成不可重現的隨機數列的硬件設備。在 CPU 中內置了數字隨機數生成數 (Digital Random Number Generator,DRNG),並提供了生成不可重現的隨機數 RDSEED 指令,以及生成不可預測的隨機數的 RDRAND 指令。
僞隨機數生成器
僞隨機數生成器是由外部輸入的種子和內部狀態兩者生成的僞隨機數列。
由於內部狀態決定了下一個生成的僞隨機數,所以內部狀態不能被攻擊者知道。外部輸入的種子是對僞隨機數生成器的內部狀態進行初始化的。所以種子也不能被攻擊者知道。因爲種子也不能使用容易被預測的值,例如不能使用當前時間作爲種子。
密碼的密鑰與隨機數種子之間的對比如下:
生成僞隨機數有以下幾種算法:
- 雜亂的方法
- 線性同餘法
- 單向散列函數法
- 密碼法
- ANSI X9.17
線性同餘法
線性同餘法就是將當前的僞隨機數值乘以 A 再加上 C,然後將除以 M 得到的餘數作爲下一個僞隨機數。如下。
R0 = (A * 種子 + C) mod M
R1 = (A * R0 + C) mod M
R2 = (A * R1 + C) mod M
R3 = (A * R2 + C) mod M
R4 = (A * R3 + C) mod M
Rn = (A * R(n-1) + C) mod M
線性同餘具有週期性,根據週期即可預測未來的狀態。所以它不具備不可預測性,即不能將它用於密碼技術。
很多僞隨機數生成器的庫函數(library function)都是採用線性同餘法編寫。例如 C 語言的庫函數 rand,以及 Java 的 java.util.Random 類等,都採用了線性同餘法。因此這些函數都不能用於密碼技術。
單向散列函數法
單向散列函數也可以生成不可預測的僞隨機數,且爲強僞隨機數(因爲它的單向性,具備不可預測性)。
- 用僞隨機數的種子初始化內部狀態,即計數器的值
- 用單向散列函數計算計數器的散列值
- 將散列值作爲僞隨機數輸出
- 計數器的值加1
- 根據需要的僞隨機數數量,重複 第 2 步 ~ 第 4 步
單向散列函數的單向性是支撐僞隨機數生成器不可預測性的基礎。
密碼法
使用密碼法也能生成強僞隨機數,既可以使用 AES 對稱加密,也可以使用 RSA 公鑰加密。
- 初始化內部狀態(計數器)
- 用密鑰加密計數器的值
- 將密文作爲僞隨機數輸出
- 計數器的值加1
- 根據需要的僞隨機數數量,重複 第 2 步 ~ 第 4 步
密碼的機密性是支撐僞隨機數生成器不可預測性的基礎。
ANSI X9.17
用 ANSI X9.17 方法也可以生成強僞隨機數。
- 初始化內部狀態
- 將當前時間加密生成密鑰
- 對內部狀態與掩碼求 XOR
- 將步驟 3 的結果進行加密
- 將步驟 4 的結果作爲僞隨機數輸出
- 將步驟 4 的結果與掩碼求 XOR
- 將步驟 6 的結果加密
- 將步驟 7 的結果作爲新的內部狀態
- 根據需要的僞隨機數數量,重複 第 2 步 ~ 第 8 步
其他算法
有一個僞隨機數生成算法叫梅森旋轉算法(Mersenne twister),它並不能用於安全相關的用途,因爲它和線性同餘算法一樣,觀察週期,即可對之後生成的隨時數列進行預測。
Java 中的 java.util.Random 類也不能用於安全相關用途,如果要用於安全相關的用途,可以使用另外一個叫 java.security.SecureRandom 類。
同理 Ruby 中也有這樣對應的兩個類,Random 類和 SecureRandom 類,用於安全用途的也只能使用 SecureRandom 類。
對僞隨機數生成器的攻擊
- 對種子進行攻擊
僞隨機數的種子和密碼的密鑰同等重要,要避免種子被攻擊者知道,需要使用具備不可重現性的真隨機數作爲種子。
- 對隨機數池進行攻擊
一般不會在使用的時候才生成真隨機數,會事先在隨機數池的文件中累計隨機比特序列。當需要用的時候,直接從池子中取出所需長度的隨機比特序列使用即可。(隨機數池本身並不存儲任何意義的信息,但是我們卻需要保護沒有任何意義的比特序列。雖然有點矛盾,但是又是必須的)
參考資料
該系列的主要內容來自《圖解密碼技術第三版》