與偽隨機(jī)數(shù):密鑰生成中的隨機(jī)源與熵池管理)
1. 從一個(gè)看似矛盾的問(wèn)題說(shuō)起公鑰密碼學(xué)里有一個(gè)特別反直覺(jué)的事實(shí)你用來(lái)加密的密鑰是公開(kāi)的任何人都能拿到但別人依然解不開(kāi)你的密文。這件事第一次接觸會(huì)覺(jué)得像魔術(shù)但支撐它的數(shù)學(xué)基礎(chǔ)其實(shí)相當(dāng)樸素——單向函數(shù)。正向計(jì)算容易反向計(jì)算在算力上不可行。RSA依賴大整數(shù)分解橢圓曲線依賴離散對(duì)數(shù)它們都是這類函數(shù)的實(shí)例。但這里有個(gè)容易被忽略的環(huán)節(jié)密鑰是怎么來(lái)的。如果生成密鑰的那個(gè)隨機(jī)數(shù)本身不夠隨機(jī)整個(gè)公鑰體系的安全性就是紙糊的。我見(jiàn)過(guò)不少開(kāi)發(fā)者算法選型很講究參數(shù)配置也到位結(jié)果栽在隨機(jī)數(shù)生成上。這篇文章就圍繞公鑰密碼學(xué)和偽隨機(jī)數(shù)之間的關(guān)系展開(kāi)把密鑰生成、隨機(jī)源選擇、熵池管理這些環(huán)節(jié)拆開(kāi)講清楚。適合有一定編程基礎(chǔ)、正在做安全相關(guān)功能、或者單純想搞明白“隨機(jī)數(shù)為什么能決定加密強(qiáng)度”的讀者。2. 公鑰密碼學(xué)的核心機(jī)制拆解2.1 對(duì)稱加密的困境與公鑰的破局思路對(duì)稱加密的邏輯很直接一把鑰匙開(kāi)一把鎖加密解密用同一個(gè)密鑰。問(wèn)題在于通信雙方怎么安全地把這把鑰匙交到對(duì)方手里。如果雙方能面對(duì)面交換那沒(méi)問(wèn)題但如果隔著網(wǎng)絡(luò)你沒(méi)法保證傳輸通道本身是安全的。這就是經(jīng)典的密鑰分發(fā)難題。公鑰密碼學(xué)的破局思路是把密鑰拆成一對(duì)公鑰和私鑰。公鑰可以隨便公開(kāi)私鑰只有自己持有。別人用你的公鑰加密你用私鑰解密。反過(guò)來(lái)你用私鑰簽名別人用公鑰驗(yàn)證。整個(gè)過(guò)程中私鑰永遠(yuǎn)不需要傳輸密鑰分發(fā)問(wèn)題從根上被繞開(kāi)了。這個(gè)設(shè)計(jì)的美妙之處在于它把“保密”這件事從傳輸環(huán)節(jié)轉(zhuǎn)移到了數(shù)學(xué)計(jì)算環(huán)節(jié)。你不需要保護(hù)公鑰的傳輸因?yàn)楣€泄露不影響安全性。你只需要保護(hù)私鑰不被竊取。安全邊界從“通道”收縮到了“本地存儲(chǔ)”可控性大幅提升。2.2 單向函數(shù)公鑰密碼學(xué)的數(shù)學(xué)地基公鑰密碼學(xué)的安全性建立在單向函數(shù)之上。所謂單向函數(shù)就是正向計(jì)算很簡(jiǎn)單反向求解在計(jì)算上不可行。注意這里的“不可行”不是理論上的不可能而是指以當(dāng)前已知的最佳算法和算力水平求解所需的時(shí)間超出了實(shí)際意義。以RSA為例。選兩個(gè)大質(zhì)數(shù)p和q計(jì)算n p × q這一步對(duì)計(jì)算機(jī)來(lái)說(shuō)瞬間完成。但如果只知道n要反推出p和q當(dāng)n足夠大時(shí)比如2048位目前最好的分解算法也需要天文數(shù)字的時(shí)間。這就是RSA的安全基礎(chǔ)。橢圓曲線密碼學(xué)ECC用的是另一類單向函數(shù)橢圓曲線上的離散對(duì)數(shù)問(wèn)題。在特定橢圓曲線上已知基點(diǎn)G和私鑰k計(jì)算公鑰K kG很容易但已知G和K求k在計(jì)算上不可行。ECC的優(yōu)勢(shì)在于達(dá)到同等安全強(qiáng)度所需的密鑰長(zhǎng)度遠(yuǎn)小于RSA。256位的ECC密鑰大約等價(jià)于3072位的RSA密鑰這對(duì)資源受限的環(huán)境非常友好。2.3 密鑰生成隨機(jī)數(shù)登場(chǎng)的時(shí)刻到這里隨機(jī)數(shù)還沒(méi)有正式出場(chǎng)。但密鑰生成這一步隨機(jī)數(shù)是絕對(duì)的主角。以RSA為例生成密鑰的第一步是選兩個(gè)大質(zhì)數(shù)p和q。這兩個(gè)質(zhì)數(shù)怎么來(lái)不是從質(zhì)數(shù)表里挑而是隨機(jī)生成一個(gè)大奇數(shù)然后做素性檢測(cè)不是質(zhì)數(shù)就換一個(gè)繼續(xù)試。這個(gè)“隨機(jī)生成”的過(guò)程用的就是隨機(jī)數(shù)生成器。ECC的密鑰生成更直接私鑰k就是一個(gè)隨機(jī)數(shù)在橢圓曲線的階的范圍內(nèi)隨機(jī)選取。公鑰K由k計(jì)算得出。如果k的隨機(jī)性不夠攻擊者可以通過(guò)窮舉或統(tǒng)計(jì)分析縮小搜索范圍私鑰就可能被推出來(lái)。所以隨機(jī)數(shù)的質(zhì)量直接決定了密鑰的質(zhì)量密鑰的質(zhì)量直接決定了整個(gè)公鑰體系的安全上限。算法再?gòu)?qiáng)隨機(jī)數(shù)不行一切白搭。3. 偽隨機(jī)數(shù)的真相不是真隨機(jī)但夠用3.1 真隨機(jī)與偽隨機(jī)的本質(zhì)區(qū)別嚴(yán)格來(lái)說(shuō)計(jì)算機(jī)里沒(méi)有真正的隨機(jī)。計(jì)算機(jī)是確定性系統(tǒng)給定相同的輸入和狀態(tài)輸出必然相同。所謂“隨機(jī)數(shù)”要么來(lái)自物理世界的不可預(yù)測(cè)現(xiàn)象要么來(lái)自算法對(duì)種子的確定性變換。真隨機(jī)數(shù)生成器TRNG依賴物理熵源熱噪聲、光電效應(yīng)、放射性衰變的時(shí)間間隔等。這些現(xiàn)象在量子層面本質(zhì)上是不可預(yù)測(cè)的所以TRNG的輸出具有真正的隨機(jī)性。但TRNG的問(wèn)題是速度慢、成本高而且不是所有環(huán)境都能提供高質(zhì)量的物理熵源。偽隨機(jī)數(shù)生成器PRNG則是純算法。給定一個(gè)種子它通過(guò)確定性算法生成一長(zhǎng)串看起來(lái)隨機(jī)的數(shù)字序列。如果種子相同輸出序列完全相同。PRNG的優(yōu)點(diǎn)是速度快、可復(fù)現(xiàn)、實(shí)現(xiàn)簡(jiǎn)單。缺點(diǎn)是如果種子被猜到或泄露整個(gè)序列都可被預(yù)測(cè)。3.2 密碼學(xué)安全偽隨機(jī)數(shù)生成器CSPRNG普通PRNG和CSPRNG的區(qū)別就像普通鎖和防盜鎖的區(qū)別。普通PRNG比如線性同余生成器的設(shè)計(jì)目標(biāo)是統(tǒng)計(jì)特性好、速度快不關(guān)心抗攻擊能力。CSPRNG則必須滿足兩個(gè)額外條件一是給定部分輸出無(wú)法推算出后續(xù)輸出二是給定內(nèi)部狀態(tài)無(wú)法反推出之前的輸出。CSPRNG的典型構(gòu)造方式包括基于密碼學(xué)哈希函數(shù)如SHA-256的構(gòu)造、基于分組密碼如AES-CTR模式的構(gòu)造、以及專門(mén)設(shè)計(jì)的流密碼如ChaCha20。這些構(gòu)造的共同特點(diǎn)是即使攻擊者獲得了部分輸出也無(wú)法有效預(yù)測(cè)其余部分。在實(shí)際開(kāi)發(fā)中選擇CSPRNG是密鑰生成的底線要求。Python的secrets模塊、Java的SecureRandom、Go的crypto/rand、OpenSSL的RAND_bytes這些都是CSPRNG的實(shí)現(xiàn)。而random模塊、Math.random()、C語(yǔ)言的rand()這些都不是密碼學(xué)安全的絕對(duì)不能用于密鑰生成。3.3 熵池隨機(jī)數(shù)的“水庫(kù)”操作系統(tǒng)層面隨機(jī)數(shù)的來(lái)源通常是一個(gè)熵池。Linux的/dev/random和/dev/urandom就是熵池的兩個(gè)接口。/dev/random在熵不足時(shí)會(huì)阻塞直到收集到足夠的熵/dev/urandom則不阻塞熵不足時(shí)用CSPRNG繼續(xù)生成。熵的來(lái)源包括鍵盤(pán)敲擊的時(shí)間間隔、鼠標(biāo)移動(dòng)軌跡、磁盤(pán)I/O的完成時(shí)間、網(wǎng)絡(luò)中斷的到達(dá)時(shí)間等。這些事件的時(shí)間戳具有足夠的不可預(yù)測(cè)性被內(nèi)核收集后混入熵池。這里有個(gè)常見(jiàn)的誤區(qū)很多人認(rèn)為/dev/urandom不如/dev/random安全。實(shí)際上在現(xiàn)代Linux內(nèi)核中一旦熵池完成初始化/dev/urandom的輸出在密碼學(xué)上是安全的。/dev/random的阻塞行為在大多數(shù)場(chǎng)景下只會(huì)帶來(lái)不必要的性能問(wèn)題。只有在系統(tǒng)剛啟動(dòng)、熵池尚未初始化完成的極短窗口內(nèi)/dev/urandom的輸出才可能不夠安全。注意在嵌入式設(shè)備或虛擬機(jī)中熵源可能非常有限。系統(tǒng)啟動(dòng)后熵池可能長(zhǎng)時(shí)間處于低熵狀態(tài)。這種情況下建議使用haveged或rng-tools等工具補(bǔ)充熵或者在應(yīng)用層使用硬件隨機(jī)數(shù)生成器。4. 密鑰生成實(shí)操?gòu)碾S機(jī)數(shù)到密鑰對(duì)4.1 RSA密鑰生成的完整流程RSA密鑰生成的核心步驟可以拆解為以下幾個(gè)環(huán)節(jié)隨機(jī)生成兩個(gè)大質(zhì)數(shù)p和q長(zhǎng)度各為密鑰長(zhǎng)度的一半計(jì)算n p × q計(jì)算歐拉函數(shù)φ(n) (p-1) × (q-1)選擇一個(gè)整數(shù)e滿足1 e φ(n)且gcd(e, φ(n)) 1計(jì)算d滿足d × e ≡ 1 (mod φ(n))公鑰為(n, e)私鑰為(n, d)其中第一步的“隨機(jī)生成大質(zhì)數(shù)”是最關(guān)鍵的環(huán)節(jié)。具體做法是用CSPRNG生成一個(gè)指定長(zhǎng)度的隨機(jī)奇數(shù)然后做素性檢測(cè)。常用的素性檢測(cè)算法是Miller-Rabin它有一定的誤判概率但通過(guò)多輪檢測(cè)可以將誤判概率降到可忽略的程度。這里有一個(gè)實(shí)操中的細(xì)節(jié)生成隨機(jī)奇數(shù)時(shí)最高位必須置1保證質(zhì)數(shù)的位數(shù)正確最低位必須置1保證是奇數(shù)。中間位由CSPRNG填充。這個(gè)細(xì)節(jié)看似微小但如果最高位沒(méi)置1生成的質(zhì)數(shù)可能比預(yù)期短很多直接削弱密鑰強(qiáng)度。4.2 ECC密鑰生成的簡(jiǎn)潔之美ECC的密鑰生成比RSA簡(jiǎn)潔得多選定一條標(biāo)準(zhǔn)橢圓曲線確定基點(diǎn)G和階n用CSPRNG生成一個(gè)隨機(jī)整數(shù)d滿足1 ≤ d ≤ n-1計(jì)算公鑰Q d × G私鑰為d公鑰為Q整個(gè)過(guò)程只需要一次隨機(jī)數(shù)生成和一次橢圓曲線乘法。沒(méi)有素性檢測(cè)沒(méi)有模逆運(yùn)算速度快、實(shí)現(xiàn)簡(jiǎn)單。這也是ECC在移動(dòng)端和物聯(lián)網(wǎng)設(shè)備上受歡迎的原因之一。但簡(jiǎn)潔不意味著可以掉以輕心。私鑰d的隨機(jī)性要求極高。如果d的生成使用了弱隨機(jī)數(shù)攻擊者可以通過(guò)格攻擊lattice attack等方法從多個(gè)簽名中恢復(fù)私鑰。歷史上一些區(qū)塊鏈項(xiàng)目就因?yàn)镋CDSA簽名時(shí)使用了弱隨機(jī)數(shù)導(dǎo)致私鑰被批量恢復(fù)。4.3 代碼示例用Python生成密鑰對(duì)下面用Python演示如何用CSPRNG生成RSA和ECC密鑰對(duì)。注意使用的是secrets模塊和cryptography庫(kù)而不是random模塊。import secrets from cryptography.hazmat.primitives.asymmetric import rsa, ec from cryptography.hazmat.primitives import serialization # 生成RSA密鑰對(duì) def generate_rsa_keypair(key_size2048): private_key rsa.generate_private_key( public_exponent65537, key_sizekey_size, backendNone # 使用默認(rèn)后端 ) public_key private_key.public_key() return private_key, public_key # 生成ECC密鑰對(duì) def generate_ecc_keypair(curveec.SECP256R1()): private_key ec.generate_private_key(curve) public_key private_key.public_key() return private_key, public_key # 序列化私鑰 def serialize_private_key(private_key, passwordNone): if password: encryption serialization.BestAvailableEncryption(password.encode()) else: encryption serialization.NoEncryption() return private_key.private_bytes( encodingserialization.Encoding.PEM, formatserialization.PrivateFormat.PKCS8, encryption_algorithmencryption ) # 使用示例 rsa_priv, rsa_pub generate_rsa_keypair(2048) ecc_priv, ecc_pub generate_ecc_keypair() print(RSA私鑰長(zhǎng)度:, len(serialize_private_key(rsa_priv))) print(ECC私鑰長(zhǎng)度:, len(serialize_private_key(ecc_priv)))這段代碼里cryptography庫(kù)底層會(huì)調(diào)用操作系統(tǒng)的CSPRNG。在Linux上它讀取/dev/urandom在Windows上它調(diào)用BCryptGenRandom。這些都是密碼學(xué)安全的隨機(jī)源。提示public_exponent65537是RSA的標(biāo)準(zhǔn)選擇。這個(gè)值不能太小比如3否則可能受到小指數(shù)攻擊也不能太大否則加密速度會(huì)變慢。65537是一個(gè)兼顧安全和效率的平衡點(diǎn)。5. 隨機(jī)數(shù)質(zhì)量對(duì)安全性的實(shí)際影響5.1 弱隨機(jī)數(shù)導(dǎo)致的真實(shí)攻擊模式弱隨機(jī)數(shù)導(dǎo)致的密鑰泄露在現(xiàn)實(shí)中發(fā)生過(guò)多次。最典型的攻擊模式有兩種第一種是種子空間過(guò)小。如果PRNG的種子只有32位攻擊者最多嘗試2^32種可能就能窮舉出所有可能的密鑰。這在現(xiàn)代算力下是秒級(jí)的事情。歷史上某些嵌入式設(shè)備的密鑰生成就犯過(guò)這個(gè)錯(cuò)誤種子來(lái)自設(shè)備啟動(dòng)時(shí)間戳的低32位攻擊者可以輕松窮舉。第二種是種子可預(yù)測(cè)。如果種子來(lái)自可預(yù)測(cè)的事件比如進(jìn)程ID、當(dāng)前時(shí)間戳、或者某個(gè)固定值攻擊者可以通過(guò)觀察系統(tǒng)狀態(tài)來(lái)縮小種子搜索范圍。Debian在2006年到2008年間發(fā)生過(guò)一次著名的OpenSSL漏洞就是因?yàn)樽⑨尩袅艘恍写a導(dǎo)致PRNG的熵源被大幅削弱生成的密鑰可被預(yù)測(cè)。第三種是隨機(jī)數(shù)復(fù)用。在ECDSA簽名中每次簽名都需要一個(gè)隨機(jī)數(shù)k。如果兩次簽名使用了相同的k攻擊者可以通過(guò)簡(jiǎn)單的代數(shù)運(yùn)算直接計(jì)算出私鑰。這個(gè)錯(cuò)誤在區(qū)塊鏈領(lǐng)域出現(xiàn)過(guò)多次導(dǎo)致大量資產(chǎn)被盜。5.2 如何評(píng)估隨機(jī)數(shù)質(zhì)量評(píng)估隨機(jī)數(shù)質(zhì)量可以從以下幾個(gè)維度入手統(tǒng)計(jì)隨機(jī)性通過(guò)NIST SP 800-22測(cè)試套件檢查輸出序列的頻率、游程、自相關(guān)等統(tǒng)計(jì)特性不可預(yù)測(cè)性給定部分輸出檢查是否能預(yù)測(cè)后續(xù)輸出種子強(qiáng)度種子的熵有多少位是否來(lái)自不可預(yù)測(cè)的物理源前向安全性即使當(dāng)前狀態(tài)泄露是否能反推出之前的輸出后向安全性即使當(dāng)前狀態(tài)泄露是否能預(yù)測(cè)未來(lái)的輸出對(duì)于開(kāi)發(fā)者來(lái)說(shuō)最實(shí)用的評(píng)估方法是確認(rèn)使用的API是否是CSPRNG。如果不是直接換掉。不需要自己去做統(tǒng)計(jì)測(cè)試那是庫(kù)開(kāi)發(fā)者的事情。5.3 不同場(chǎng)景下的隨機(jī)數(shù)選擇建議場(chǎng)景推薦方案不推薦方案原因生成RSA/ECC密鑰secrets/SecureRandom/crypto/randrandom/Math.random()密鑰必須用CSPRNG生成會(huì)話令牌secrets.token_urlsafe()UUID4某些實(shí)現(xiàn)會(huì)話令牌需要不可預(yù)測(cè)生成初始化向量os.urandom()固定值或計(jì)數(shù)器IV需要隨機(jī)且唯一生成鹽值os.urandom()用戶名或時(shí)間戳鹽值需要隨機(jī)且唯一模擬測(cè)試數(shù)據(jù)random/Math.random()CSPRNG測(cè)試數(shù)據(jù)不需要密碼學(xué)安全游戲中的隨機(jī)事件普通PRNGCSPRNG性能優(yōu)先安全無(wú)關(guān)這張表的核心邏輯是只要隨機(jī)數(shù)的可預(yù)測(cè)性會(huì)影響安全性就必須用CSPRNG否則可以用普通PRNG。判斷標(biāo)準(zhǔn)很簡(jiǎn)單如果攻擊者猜到了這個(gè)隨機(jī)數(shù)會(huì)不會(huì)造成安全問(wèn)題會(huì)就用CSPRNG不會(huì)就隨意。6. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄6.1 密鑰生成速度慢怎么辦RSA密鑰生成尤其是4096位密鑰在低性能設(shè)備上可能需要幾秒甚至幾十秒。這不是bug是正?,F(xiàn)象。質(zhì)數(shù)生成本身就需要反復(fù)嘗試位數(shù)越大找到質(zhì)數(shù)的平均嘗試次數(shù)越多。如果速度成為瓶頸可以考慮以下方案降低密鑰長(zhǎng)度。2048位RSA在當(dāng)前算力下仍然安全4096位是過(guò)度設(shè)計(jì)改用ECC。256位ECC密鑰的安全強(qiáng)度約等于3072位RSA但生成速度快幾個(gè)數(shù)量級(jí)預(yù)生成密鑰池。在系統(tǒng)空閑時(shí)預(yù)生成一批密鑰需要時(shí)直接取用使用硬件加速。某些CPU有專門(mén)的指令集加速大數(shù)運(yùn)算注意不要為了速度而降低隨機(jī)數(shù)質(zhì)量。用弱隨機(jī)數(shù)生成的密鑰生成再快也沒(méi)有意義。6.2 熵不足的典型癥狀與解決熵不足的典型癥狀是密鑰生成阻塞、/dev/random讀取緩慢、系統(tǒng)啟動(dòng)后一段時(shí)間內(nèi)隨機(jī)數(shù)質(zhì)量下降。在虛擬機(jī)、容器、嵌入式設(shè)備中尤其常見(jiàn)。排查方法# 查看當(dāng)前熵池大小 cat /proc/sys/kernel/random/entropy_avail # 查看熵池容量 cat /proc/sys/kernel/random/poolsize如果entropy_avail長(zhǎng)期低于200說(shuō)明熵池處于低熵狀態(tài)。解決方法包括安裝haveged用CPU時(shí)間抖動(dòng)作為熵源安裝rng-tools如果有硬件隨機(jī)數(shù)生成器用它來(lái)補(bǔ)充熵池在虛擬機(jī)中確保宿主機(jī)啟用了熵傳遞功能在容器中確保/dev/urandom可訪問(wèn)6.3 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因排查方法解決方案密鑰生成阻塞熵池不足查看entropy_avail補(bǔ)充熵源密鑰可被預(yù)測(cè)使用了弱PRNG檢查代碼中的隨機(jī)數(shù)API換用CSPRNGECDSA簽名私鑰泄露隨機(jī)數(shù)k復(fù)用檢查簽名實(shí)現(xiàn)使用RFC 6979確定性簽名不同設(shè)備密鑰相同種子相同檢查種子來(lái)源使用設(shè)備唯一熵源密鑰生成速度慢密鑰長(zhǎng)度過(guò)大檢查密鑰長(zhǎng)度配置降低到2048位或改用ECC隨機(jī)數(shù)序列重復(fù)PRNG狀態(tài)被重置檢查PRNG初始化邏輯確保種子只初始化一次6.4 一個(gè)容易被忽略的坑fork后的隨機(jī)數(shù)狀態(tài)在Unix-like系統(tǒng)中fork()創(chuàng)建子進(jìn)程后子進(jìn)程會(huì)繼承父進(jìn)程的PRNG狀態(tài)。如果父子進(jìn)程都繼續(xù)使用同一個(gè)PRNG生成隨機(jī)數(shù)它們會(huì)產(chǎn)生相同的序列。這在多進(jìn)程服務(wù)器中是一個(gè)隱蔽的安全漏洞。解決方法是在fork()之后子進(jìn)程重新初始化PRNG或者使用操作系統(tǒng)提供的CSPRNG它內(nèi)部會(huì)處理fork安全問(wèn)題。OpenSSL的RAND_bytes在fork后會(huì)檢測(cè)PID變化并重新播種但自己實(shí)現(xiàn)的PRNG往往沒(méi)有這個(gè)保護(hù)。提示如果你在寫(xiě)多進(jìn)程程序并且自己實(shí)現(xiàn)了PRNG務(wù)必在fork()后的子進(jìn)程中重新播種。最簡(jiǎn)單的方法是混入子進(jìn)程的PID和當(dāng)前時(shí)間戳。7. 公鑰密碼學(xué)與隨機(jī)數(shù)的未來(lái)走向后量子密碼學(xué)是當(dāng)前密碼學(xué)領(lǐng)域的熱點(diǎn)方向。量子計(jì)算機(jī)對(duì)RSA和ECC構(gòu)成實(shí)質(zhì)性威脅因?yàn)镾hor算法可以在多項(xiàng)式時(shí)間內(nèi)解決大整數(shù)分解和離散對(duì)數(shù)問(wèn)題。后量子密碼學(xué)的候選算法如基于格的密碼學(xué)、基于編碼的密碼學(xué)、多變量密碼學(xué)同樣依賴隨機(jī)數(shù)來(lái)生成密鑰。有意思的是后量子密碼學(xué)對(duì)隨機(jī)數(shù)的需求不僅沒(méi)有減少反而增加了。因?yàn)楹芏嗪罅孔铀惴ǖ拿荑€尺寸更大密鑰生成過(guò)程中需要更多的隨機(jī)數(shù)。同時(shí)一些后量子算法對(duì)隨機(jī)數(shù)的質(zhì)量更加敏感弱隨機(jī)數(shù)可能導(dǎo)致密鑰完全暴露。另一個(gè)趨勢(shì)是硬件隨機(jī)數(shù)生成器的普及?,F(xiàn)代CPU如Intel的RdRand指令、AMD的RDRAND指令已經(jīng)集成了硬件隨機(jī)數(shù)生成器可以直接從CPU獲取高質(zhì)量的隨機(jī)數(shù)。操作系統(tǒng)和密碼學(xué)庫(kù)也在逐步適配這些硬件特性減少對(duì)熵池的依賴。對(duì)于開(kāi)發(fā)者來(lái)說(shuō)核心原則沒(méi)有變密鑰生成必須用CSPRNG不要自己造輪子不要用非密碼學(xué)安全的隨機(jī)數(shù)API。這個(gè)原則在過(guò)去二十年沒(méi)有變?cè)谖磥?lái)二十年大概率也不會(huì)變。我在實(shí)際項(xiàng)目中遇到過(guò)幾次因?yàn)殡S機(jī)數(shù)問(wèn)題導(dǎo)致的密鑰安全事件每次追溯到最后都是某個(gè)環(huán)節(jié)用了不該用的隨機(jī)數(shù)API。最離譜的一次是有人用Math.random()生成AES密鑰還覺(jué)得“看起來(lái)挺隨機(jī)的”。隨機(jī)數(shù)這件事看起來(lái)簡(jiǎn)單但真正理解其安全含義的人并不多。希望這篇內(nèi)容能幫你把這塊知識(shí)補(bǔ)上下次寫(xiě)密鑰生成代碼的時(shí)候能多想一步我用的這個(gè)隨機(jī)數(shù)真的夠隨機(jī)嗎