1. 程式人生 > >密碼學基礎下篇

密碼學基礎下篇

本文首發自蟲洞社群,轉載請註明出處。

祕鑰管理

祕鑰長度與安全性

所謂安全密碼,不是說不能被破解出來,而是說不能在安全期限的時間內破解出來或者破解出來的所需代價遠大於得到的破解結果。
下面以祕鑰強度112位的3DES破解為例計算所需的代價:
1百億億次/秒需要100w臺100年才能破解出。新一代超級計算機:百億億次級運算,也需要100w臺破解1年。
數量級概念:以256位祕鑰為例
2^256>2^(10*25)>10^(3*25)=10^75>>>3×10^23
3×10^23等於宇宙中恆星的數量,大於地球中沙子的數量。
如果是1秒鐘嘗試20億億次不同的組合:
10^75/(20*10^8*10^8*86400*365)=10^58/86400/365/2>10^50

年。這麼長時間遠遠大於太陽的壽命,6*10^9億年。
祕鑰安全長度:

祕鑰管理系統

金鑰管理指的是自金鑰的產生到金鑰的銷燬,整個過程大致包括金鑰的生成,儲存,分配,啟用,停用,控制,更新,撤銷和銷燬,其中金鑰的分配和儲存最為關鍵。
從上一節我們可以知道無論對稱密碼體制內還是非對稱密碼體制,其安全性實際取決於對金鑰的長度和祕鑰的安全保護。現代密碼學把資料加密保護全部繫於金鑰體質上,因此金鑰的安全管理是保證密碼系統安全性的重要因素。金鑰的管理目的是維護系統與各個實體之間的金鑰關係,以抵抗各種可能的威脅,金鑰管理要藉助加密,認證,簽名,協議和公正技術。對於對稱密碼而言,在通訊的過程中應當遵循一次一密的加密方式。

祕鑰分類

基本金鑰或初始金鑰:基本金鑰是使用者選出的由系統分配給使用者的可能在較長時間內使用者所專用的金鑰,又稱使用者金鑰。基本金鑰和會話金鑰一起啟動和控制由某種演算法所構成的金鑰產生器,依次產生用於加密資料的金鑰流。
會話金鑰:會話金鑰是兩個通訊終端使用者在一次交換資料時所採用的金鑰,當用其保護傳輸金鑰時稱為資料加密金鑰,當用其保護檔案時,稱之為檔案金鑰。會話金鑰可以由通訊雙方互相約定,也可由系統動態地賦予通訊雙方,故又稱專用金鑰。
金鑰加密金鑰:金鑰加密金鑰是對通訊中傳送的會話或檔案金鑰進行加密時所採用的金鑰,通訊網中每一個結點都有這樣的一個金鑰。
主機主金鑰:主機主金鑰是對金鑰加密金鑰進行加密的金鑰,儲存於主處理器中。

祕鑰產生到銷燬

祕鑰的產生:應當採用足夠安全的方法來產生金鑰,對一個金鑰的基本要求是隨機性,而真正的隨機性是不可再現的,任何人都不會再次產生它。關於隨意數將在下一章節講解。

祕鑰分配:金鑰的分配是金鑰管理系統中最為複雜的問題,依據分配手段可以分為人工分發和金鑰交換協議的動態分發,從金鑰分配技術來看,有基於對稱密碼體制的金鑰分配與基於公鑰密碼體制的金鑰分配,從金鑰的交換方式來說,可以分為人工金鑰分發,基於中心的金鑰分發和基於認證的金鑰分發。

祕鑰交換:本質上也是一種加密演算法,一般有兩種實現方式。
a) 公鑰加密實現:傳送方用接收方的公鑰加密自己的金鑰,接收方用自己的私鑰解密得到傳送方的金鑰,逆過來亦然,從而實現金鑰交換。
b) 使用DH演算法:前提傳送方和接受方協商使用同一個大素數P和生成數g,各自產生的隨機數X和Y。傳送方將g的X次方mod P產生的數值傳送給接收方,接受方將g的Y次方mod P產生的數值傳送給傳送方,傳送方再對接收的結果做X次方運算,接受方對接收的結果做Y次方運算,最終密碼形成,金鑰交換完成。

祕鑰分配:金鑰的分配技術解決的是在網路環境中需要進行安全通訊的端實體之間建立共享的對稱金鑰問題。目前最為流行的方案是使用金鑰分配中心方式KDC(Key Distribution Center)。每個節點或使用者只需保管與KDC之間使用的金鑰加密金鑰,而KDC為每個使用者保管一個互不相同的金鑰加密金鑰。當兩個使用者需要通訊時,需向KDC申請,KDC將工作金鑰(也稱會話金鑰)用這兩個使用者的金鑰加密金鑰分別進行加密後送給這兩個使用者。在這種方式下,使用者不用儲存大量的工作金鑰,而且可以實現一報一密,但缺點是通訊量大,而且需要有較好的鑑別功能,以識別KDC和使用者。KDC方式還可以變形為電話號碼本方式,適用於非對稱密碼體制。通過建立使用者的公開密碼錶,在金鑰的連通範圍內進行散發,也可以採用目錄方式進行動態查詢,使用者在進行保密通訊前,首先產生一個工作金鑰並使用對方的公開金鑰加密傳輸,對方獲悉這個工作金鑰後,使用對稱密碼體制與其進行保密通訊。

祕鑰儲存:有關公私鑰的儲存方法如下。
a) 公鑰儲存:公鑰通常被分發或提供給其他使用者,但還必需經過中央權威機構(通常是證書管理機構CA)認證以便證明它屬於金鑰對的擁有者
b) 私鑰儲存:比較高階的儲存方法例如有銀行的U盾,U盾內自帶CPU,可以使私鑰不出卡,所有的運算都是在硬體內完成,從根本上保證安全,杜絕了金鑰被使用者擷取的可能性。

祕鑰的更換與撤銷:金鑰是有壽命的,一旦到了有效期就必需消除原密碼儲存區,用隨機產生的噪聲重寫,為了保證加密裝置能夠連續正常工作,也可以在新金鑰產生之後,舊金鑰仍然保留一段時間,以防止金鑰更換期間出現不能解密的問題。

隨機數

隨機數是專門的隨機試驗的結果。例如以下的摸球案例就可以生成一個真正的隨機數。

真隨機數

要產生取值為 0-9 的離散型均勻分佈的隨機數:通常的操作方法是把 10 個完全相同的乒乓球分別標上 0,1,2,….,9 然後放在一個不透明的袋中,攪攔均勻後從中摸出一球記號碼後放回袋中, 接著仍將袋中的球攪拌均勻後從袋中再摸出一球記下號碼後再放回袋中,依次放下去,就得到隨機序列.通常稱類似這種摸球的方法產生的隨機數為真正的隨機數。真隨機數是不可重複產生的,即:用完全相同的輸入對序列發生器操作兩次(至少與人所能做到的最精確的一樣),得到兩個不相關的隨機序列

偽隨機數

計算機是用某種數學方法產生的隨機數,實際上是按照一定的計算方法得到的一串數,它們具有類似隨機數的性質,但是它們是依照確定演算法產生的, 便不可能是真正的隨機數,所以稱計算機產生的隨機數為偽隨機數.

隨機數週期

用某種遞推公式按一定程式產生偽隨機數,一般從某一初始值起步產生偽隨機數列X1,X2…..;若存在m對任意n,有Xn+m=Xn(n=1,2…..),則滿足上述關係的最小m稱做偽隨機數的週期。週期大於2256位的隨機數序列,就能大量應用。

偽隨機數要求

在計算機上用數學方法產生隨機數的一般要求如下:
1) 產生的隨機數列要有均勻性、抽樣的隨機性、試驗的獨立性和前後的一致性。
2) 產生的隨機數列要有足夠長的週期,以滿足模擬實際問題的要求。
3) 產生隨機數的速度要快,佔用的記憶體少。

偽隨機數演算法

偽隨機數產生的方法叫做偽隨機數發生器。偽隨機數產生器中最基礎的思想是均勻分佈(不是唯一的思路)。一般來說,如今主流的程式語言中使用的隨機數函式基本採用這種均勻分佈思想,而其中最常用的演算法就是”線性同餘法”。
線性同餘法基於如下線性同餘方程組

ax + by = m

用於產生均勻型偽隨機數的線性同餘產生器(與上面的方程符號沒有對應關係)

Xn = (an-1+b)mod(n)

其中,a為”乘數”,b為”增量”,m為”模數”,x0為”種子數”。
如果產生的是區間實在(0,1)之間的,則只需要每個數都除以m即可,即取

£n = Xn/m

物理裝置產生的隨機數

Linux作業系統的/dev/random和/dev/urandom是提供的隨機偽裝置,這兩個裝置的任務,是提供永不為空的隨機位元組資料流。為了獲得真正意義上的隨機數,需要一個外部的噪聲源。Linux核心找到了一個完美的噪聲源產生者—就是使用計算機的人。我們在使用計算機時敲擊鍵盤的時間間隔,移動滑鼠的距離與間隔,特定中斷的時間間隔等等,這些對於計算機來講都是屬於非確定的和不可預測的。雖然計算機本身的行為完全由程式設計所控制,但人對外設硬體的操作具有很大的不確定性,而這些不確定性可以通過驅動程式中註冊的中斷處理例程(ISR)獲取。核心根據這些非確定性的裝置事件維護著一個熵池,池中的資料是完全隨機的。當有新的裝置事件到來,核心會估計新加入的資料的隨機性,當我們從熵池中取出資料時,核心會減少熵的估計值。

熵來源:開機起時鐘tick數,當前時間,使用者名稱計算機名等MD4的雜湊值,cpu執行情況,系統io情況,系統中斷資訊……

/dev/random和/dev/urandom這兩個特殊裝置都是字元型裝置。dev/random的random pool依賴於系統中斷,因此在系統的中斷數不足時,/dev/random裝置會一直封鎖,嘗試讀取的程序就會進入等待狀態,直到系統的中斷數充分夠用, /dev/random裝置可以保證資料的隨機性。/dev/urandom不依賴系統的中斷,也就不會造成程序忙等待,但是資料的隨機性也不高。

rand()和srand()函式

各類語言中常用生成隨機數的兩個函式rand()和srand()。因為rand的內部是用線性同餘法做的,不是真的隨機數,只不過因為其週期特別長,所以在一定範圍內可以看成是隨機的,rand()會返回一隨機值,範圍在0到RAND_MAX間,在呼叫此函式產生隨機數前,必須利用srand()設好隨機數種子,若沒有設隨機數種子,rand()在呼叫時會自動設隨機數種子為1,也就是可以算出初始值,下一次生成的隨機數也就可以預測了。所以rand()函式的安全性依賴srand()函式生成的初始值了。srand()函式一般是用機器中的實時時間來啟動的,因為實時時間的值每時每刻都在變化,這樣啟動的rand()函式產生的偽隨機數序列就能達到以假亂真的效果。
c 語言生成100以內隨機數的程式碼案例:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
main() {
    int i,k;
    srand( (unsigned)time( NULL ) ); //一般是使用時間初始化種子
    for( i = 0; i < 10;i++ ) {
        k=rand()%100+1; //rand()%100表示取100以內的隨機數,即取了隨機數後再對100取餘         
        x=rand()%(Y-X+1)+X
        printf( " k=%d\n", k );
    }
}    

現在總結下,rand()函式的安全問題如下:

  • 線性同餘方法是可預測的(最主要原因)
  • 通過時間設定隨機種子
    a) 不同程序在同一時間呼叫,生成的隨機數序列是相同的
    b) 同一秒內呼叫兩次或以上rand函式,生成的隨機數序列仍然是相同的

零知識證明

零知識的證明過程晦澀難懂,所以我這邊將以最簡潔的語言概括下零知識證明的核心思想。

什麼是零知識證明

零知識證明滿足三個屬性:

  1. 如果語句為真,誠實的驗證者(即,正確遵循協議的驗證者)將由誠實的證明者確信這一事實。
  2. 如果語句為假,不排除有概率欺騙者可以說服誠實的驗證者它是真的。
  3. 如果語句為真,證明者的目的就是向驗證者證明並使驗證者相信自己知道或擁有某一訊息,而在證明過程中不可向驗證者洩漏任何有關被證明訊息的內容。

零知識證明實質上是一種涉及兩方或更多方的協議,即兩方或更多方完成一項任務所需採取的一系列步驟。 實際上這是一種概率遊戲,對方不直接告訴你答案,而是採用另一種表達方式來讓向你證明,直到你認為對方確實知道答案為止。

下面用一個數獨案例簡單說明零知識證明(參考自知乎,侵刪)。

背景

小明,小紅,小剛三個好朋友很喜歡玩數獨。平日裡他們三個也會互相出題給對方做。有時候他們會出一些非常變態的數獨題互相挑戰。他們會挑一個人在紙上畫出一個9x9的格子,填上謎面(Constraint),然後交給另外兩人去解。

難題

有一天,小明出了一道非常難的數獨題,小紅花了很長時間嘗試去解開這個數獨,但是怎麼都解不出結果。

證明過程

小明拿出81(9x9)張空白的卡片放在桌上,在每張紙上寫上1-9中的一個數字,他讓小紅轉過身閉上眼,然後把這81張卡片小心翼翼地按照解的排列放在桌上,代表謎底的卡片,數字面朝下放在桌上;代表謎面的卡片,則數字面朝上放在桌上。

小紅很困惑,然後告訴小明她決定選擇按照行的方法來驗證,小明接著把每一行的9張卡片收起來單獨放到一個麻布袋裡。所有卡片都被收完放在了9個麻布袋裡。小明接著搖了搖每個麻布袋,把裡面的卡片順序都打散。最後把這9個麻布袋交給小紅。

重複過程:

小紅還是不服氣。覺得小明仍然有可能在騙她,所以要求小明再把卡片復原,按照原來的方法,重新選。這樣接連試了幾次,小紅每次都選一個不一樣的試驗方法。試了好多次都是一樣的結果。小紅這下不得不承認,小明要麼運氣非常非常好,每次都能押中小紅會選擇哪種試驗方式,要麼就是他確實知道題解,(或者小明會讀心術能預先知道小紅會選什麼試驗方式)。小紅很失望,這麼多次試驗下來,她還是不知道真正的題解,她只知道每次小明放置卡片的排列裡很大機率每行每列每個九宮格確實都是沒有重複的1-9,這就說明很大機率這題是有解的,而且小明很大機率確實知道這題的解。

總結

  • 零知識證明的本質就是在不揭曉我所知道或擁有的某樣東西的前提下,向別人證明我有很大機率(這點很重要,零知識證明說到底是一個概率上的證明)確實知道或擁有這個東西。

  • 小明和小紅之間最開始那種互動式的證明方法暗指的是互動式零知識證明(interactive zero-knowledge proof)。互動式零知識證明需要驗證方(小紅)在證明方(小明)放好答案(commitment)後,不斷的傳送隨機試驗。如果驗證和證明雙方事先串通好,那麼他們就可以在不知道真實答案的情況下開掛(simulate/forge a proof)。

  • 非互動式的證明則不需要這種互動。但是會額外需要一些機器或者程式,並且需要一串試驗序列,這個試驗序列不能被任何人知道。有了這麼一個程式和試驗序列,證明機就能自動算出一個證明,並且能防止任何一方作假。

zcash中的零知識證明

Zcash是一種去中心化、開源的加密網際網路貨幣。與比特幣相比,其更注重於隱私,以及對交易透明的可控性。具體體現為:公有區塊鏈加密了交易記錄中的傳送人、接收人、交易量;使用者可裁量選擇是否向其他人提供檢視金鑰,僅擁有此金鑰的人才能看到交易的內容。

Zcash代幣ZEC的匿名交易過程

假設alice要給bob轉1個ZEC,流程大概如下:

  1. alice用自己創私鑰對這個ZEC的交易單子進行簽名,證明自己有對這個ZEC的使用權;
  2. 此時這個ZEC的交易單子上多了一串隨機字串,該字串具有唯一性,也就是單號;
  3. 系統為bob也建立1個ZEC的交易單子,且該單子也有一個單號,也是唯一的;
    要確認bob的單子上有一個ZEC的使用權時,需要將alice的單子上的ZEC銷燬,但是ZCash不是直接銷燬的,而是將alice的交易單號放到一個作廢列表中,這時bob才能擁有這一個ZEC的使用權。
  4. 在確認bob資產所有權的時候,需要讀取這筆資產的單號是否在作廢列表中,如果不在,bob才有這個ZEC的使用權。在這個交易過程中,bob是不需要看到alice對這個ZEC的使用權的,但是交易卻達成了。因為alice只需要向礦工們提供自己的單號,礦工把這個單號放入作廢列表中即可。

同態加密

什麼是同態加密

同態加密也是個很有趣的密碼學演算法,下面也要案例通俗易懂解釋下什麼是同態加密(依然參考自知乎,侵刪)

一般的加密方案關注的都是資料儲存安全。即,我要給其他人發個加密的東西,或者要在計算機或者其他伺服器上存一個東西,我要對資料進行加密後在傳送或者儲存。沒有金鑰的使用者,不可能從加密結果中得到有關原始資料的任何資訊。只有擁有金鑰的使用者才能夠正確解密,得到原始的內容。我們注意到,這個過程中使用者是不能對加密結果做任何操作的,只能進行儲存、傳輸。對加密結果做任何操作,都將會導致錯誤的解密,甚至解密失敗。同態加密方案最有趣的地方在於,其關注的是資料處理安全。同態加密提供了一種對加密資料進行處理的功能。也就是說,其他人可以對加密資料進行處理,但是處理過程不會洩露任何原始內容。同時,擁有金鑰的使用者對處理過的資料進行解密後,得到的正好是處理後的結果。我們舉個實際生活中的例子。有個叫Alice的使用者買到了一大塊金子,她想讓工人把這塊金子打造成一個項鍊。但是工人在打造的過程中有可能會偷金子啊,畢竟就是一克金子也值很多錢的說… 因此能不能有一種方法,讓工人可以對金塊進行加工,但是不能得到任何金子?當然有辦法啦,Alice可以這麼做:Alice將金子鎖在一個密閉的盒子裡面,這個盒子安裝了一個手套。工人可以帶著這個手套,對盒子內部的金子進行處理。但是盒子是鎖著的,所以工人不僅拿不到金塊,連處理過程中掉下的任何金子都拿不到。加工完成後。Alice拿回這個盒子,把鎖開啟,就得到了金子。這個盒子的樣子大概是這樣的:

這裡面的對應關係是:

  • 盒子:加密演算法
  • 盒子上的鎖:使用者金鑰
  • 將金塊放在盒子裡面並且用鎖鎖上:將資料用同態加密方案進行加密
  • 加工:應用同態特性,在無法取得資料的條件下直接對加密結果進行處理
  • 開鎖:對結果進行解密,直接得到處理後的結果

應用場景

近幾年雲端計算越來越火熱,而同態加密簡直是雲端計算的完美搭檔。我們考慮以下的情景:一個使用者想要處理一個數據,但是他的計算機計算能力較弱。這個使用者可以使用雲端計算的概念,讓雲來幫助他進行處理而得到結果。但是如果直接將資料交給雲,無法保證安全性啊!於是,他可以使用同態加密,然後讓雲來對加密資料進行直接處理,並將處理結果返回給他。

  • 這樣一來:使用者向雲服務商付款,得到了處理的結果;
  • 雲服務商掙到了費用,並在不知道使用者資料的前提下正確處理了資料;

這方法簡直完美啊!但是,這麼好的特性肯定會帶來一些缺點。同態加密現在最需要解決的問題在於:效率。效率一詞包含兩個方面,一個是加密資料的處理速度,一個是這個加密方案的資料儲存量。我們可以直觀地想一想這個問題:

  • 工人戴著手套加工金子,肯定沒有直接加工來得快。也就是說,隔著手套處理,精準度會變差(現有構造會有誤差傳遞問題),加工的時間也會變得更長(密文的操作花費更長的時間),工人需要隔著操作,因此也需要更專業(會正確呼叫演算法)。
  • 金子放在盒子裡面,為了操作,總得做一個稍微大一點的盒子吧,要不然手操作不開啊(儲存空間問題)。裡面也要放各種工具吧,什麼電鑽啦,銼刀啦,也需要空間吧?

上面說了同態加密的基本概念,讓大家有基本的瞭解,事實上同態加密目前依然是業內的難題,哪家雲端計算公司如果能攻克這個難題,那這家公司目前肯定是一家獨大的。有興趣的同學可以嘗試著去研究更深層的演算法原理,號稱入門都要3個月(加油,有志之士的青年們)。