原創:打碼日記(微信公眾號ID:codelogs),歡迎分享,轉載請保留出處。
簡介
在資訊保安領域,一般會遇到"竊聽"、"篡改"、"偽裝"、"否認"這些威脅,而密碼學家們提供了相應的密碼學演算法來解決這些問題,如下:
- 竊聽:攻擊者可以在網路上安置了一個路由器,偵聽所有經過的資料包,這樣資料就被洩密了,密碼學提供了對稱密碼與公鑰密碼演算法對資料加密,保證機密性。
- 篡改:攻擊者對經過的資料包進行修改,使得接收方獲取到錯誤的資訊,密碼學提供了單向雜湊函式生成“資料指紋”,保證資料完整性。
- 偽裝:攻擊者偽裝成傳送方來傳送資料,使得接收方獲取到虛假的資訊,密碼學提供訊息認證碼生成"認證碼",保證資料來源的正確性。
- 否認:傳送方本身是攻擊者,傳送了惡意請求後,謊稱自己沒有發此請求,密碼學提供了數字簽名演算法,使其不可否認。
像對稱加密、公鑰加密、訊息雜湊、訊息認證碼、數字簽名這些演算法,也被稱為密碼學家的工具箱。
加密分類
現如今的加密演算法,主要分為兩大類,一類是對稱加密,而另一類是非對稱加密,而對稱加密在實現方式上又可以分為兩類,一類是分組加密演算法,另一類是流加密演算法。
分組加密
分組加密(block cipher),每次加密或解密資料中特定長度的一小塊,前一塊處理完了再處理下一塊,直到所有資料都處理完,這裡的“一塊”就稱為分組,而一個分組的bit數就是分組長度。
常見的分組加密演算法有DES與AES,比如DES的分組長度是64bit,而AES的分組長度可以是128bit或192bit或256bit,由於DES或AES的演算法過程比較複雜,暫不介紹,但我們有必要了解一下它們經常使用的基礎運算XOR。
XOR加密
在位運算中,一般有AND(與)、OR(或)、NOT(非)運算,然而還有一種位運算也非常常用,即XOR(異或),它的運算規則如下:
簡單來說,就是兩邊相同結果是0,兩邊不同結果是1,所以才稱為“異或”嘛!
而如果將XOR運算應用在多bit的資料上,我們會發現XOR有非常好的自反特性,如下:
- 資料A與B異或之後,變成了一個新資料
- 而當我們將新資料再與B異或運算後,發現它又變成了A!
- 從而,我們驚奇的發現,如果把B看成是金鑰的話,XOR可以用來實現加密、解密演算法,而加密、解密過程,都只需要資料與金鑰做XOR運算即可。
Linux命令做XOR加密
$ pip install xortool
# 明文檔案,內容是hello
$ echo -n hello > plain.txt
$ xxd plain.txt
00000000: 6865 6c6c 6f hello
# 用xor演算法加密,密碼為pass,生成encrypt.bin
$ cat plain.txt | xortool-xor -n -r pwd -f - > encrypt.bin
$ xxd encrypt.bin
00000000: 1812 081c 18 .....
# 用XOR演算法解密,可還原成明文
$ xortool-xor -n -r pwd -f encrypt.bin
hello
雖然對稱加密演算法的核心是XOR,但如果加密演算法只使用XOR的話,其加密的強度並不夠高,很容易被破譯或洩密。
下面會以一個故事展開密碼學的各種演算法概念及用途,背景如下:
男主:小李 ,女主:小紅,男配:大李,小李的哥哥,女配:小美,小紅的閨蜜
小李與小紅是同班同學,專業計算機,小李對小紅傾慕已久!
戀愛日記:小李寫情書
為了表達愛慕之情,小李寫了一封情書,寫了好長時間,決定發給小紅,但又不想被網路上的其它人看到!如下:
由於小李最近學習了XOR加密演算法,於是使用XOR加密了情書,如下:
cat plain.txt | xortool-xor -n -r pwd -f - > encrypt.bin
可以看到,加密後的資料都是亂碼了,效果很好!
小李喜出望外,將自己的成果展示給哥哥大李看!大李指出,這樣的加密演算法強度不夠高,密碼學家們可以輕易破解,開始給小李展示破譯過程!
# 統計原文詞頻
$ grep -oP . plain.txt |sort|uniq -c|sort -nr|head
66 ,
37 你
31 的
25 我
15 是
15 不
13 一
12 想
10 會
9 都
# 統計密文詞頻
$ xxd -g3 -c3 -ps encrypt.bin |sort|uniq -c|sort -nr|head
66 9fcbe8
37 94cac4
31 97ede0
25 96fff5
15 96efcb
15 94cfe9
13 94cfe4
12 96f4d7
10 94cbfe
9 99f4d9
# 按詞頻構造替換表
$ paste <(xxd -g3 -c3 -ps encrypt.bin |sort|uniq -c|sort -nr|head|awk '{print $2}'|sed -E 's/../\\x&/g') <(grep -oP . plain.txt |sort|uniq -c|sort -nr|head|awk '{print $2}')
\x9f\xcb\xe8 ,
\x94\xca\xc4 你
\x97\xed\xe0 的
\x96\xff\xf5 我
\x96\xef\xcb 是
\x94\xcf\xe9 不
\x94\xcf\xe4 一
\x96\xf4\xd7 想
\x94\xcb\xfe 會
\x99\xf4\xd9 都
# 變成sed替換指令碼
$ paste <(xxd -g3 -c3 -ps encrypt.bin |sort|uniq -c|sort -nr|head|awk '{print $2}'|sed -E 's/../\\x&/g') <(grep -oP . plain.txt |sort|uniq -c|sort -nr|head|awk '{print $2}') | awk '{printf "s/%s/%s/g; ",$1,$2}'
s/\x9f\xcb\xe8/,/g; s/\x94\xca\xc4/你/g; s/\x97\xed\xe0/的/g; s/\x96\xff\xf5/我/g; s/\x96\xef\xcb/是/g; s/\x94\xcf\xe9/不/g; s/\x94\xcf\xe4/一/g; s/\x96\xf4\xd7/想/g; s/\x94\xcb\xfe/會/g; s/\x99\xf4\xd9/都/g;
使用sed將密文按照替換表進行替換,如下:
可以發現,一部分資訊被還原回來了!
大李說,雖然XOR使用密碼對資料每位元組進行異或後,實現了加密,但它相當於是一種替換表,每組確定的明文被替換為確定的密文,然而人類的文章大多是有統計特徵的,比如在中文裡"你","我"之類的字出現頻率就很高,黑客收集了大量人類文字且統計出字頻表,因而只要將密文中出現頻率高的密文替換為字頻表中的明文,然後連懵帶猜就可以破譯出密文資訊了。
為了保守小李的祕密,大李幫小李使用AES演算法將情書加密,然後發給了小紅...
戀愛日記:小紅回應心意
小紅收到情書後,非常感動,為表達自己的心意,小紅自拍了一張照片,修了好長時間,想發給小李,但同時又不想被其它人看到!
小紅也瞭解到XOR可以進行加密,於是使用XOR演算法對照片進行了加密,如下:
# bmp圖片前54位元組是圖片後設資料,這部分要保持不變
(head -c 54 image.bmp;tail -c +55 image.bmp| xortool-xor -n -r password -f -) > image2.bmp
當加密完成後,小紅開啟照片,發現照片確實不一樣了,但還是能看出自己的大致模樣。
小紅心想,不行,加密演算法還得再改改,這樣熟人還是能認出我來!為了解決這個問題,小紅同樣打算使用密碼學界廣為流傳的AES演算法試試!
AES-ECB加密
小紅找到了如下的程式碼,然後對圖片加了密,如下:
(head -c 54 image.bmp;tail -c +55 image.bmp| openssl enc -aes-128-ecb -e -in - -out - -k password ) > image3.bmp
可以看到,AES演算法很優秀,加密後的圖片,已經完全看不清模樣了,只能看到大致輪廓!
雖然這張照片已完全認不出是小紅了,但小紅心裡還是有點疙瘩,這圖片還是有點暴露了我的身材啊!
AES-CBC加密
經過一段時間搜尋,小紅發現AES還有其它的加密模式,如CBC,於是使用CBC模式試了下,如下:
(head -c 54 image.bmp;tail -c +55 image.bmp| openssl enc -aes-128-cbc -e -in - -out - -k password -iv 0102030405060708) > image4.bmp
太棒了!小紅差點叫出聲來,加密後的照片像收不到訊號的黑白電視機一樣,小紅自已都不認識自已了,小紅滿意的笑了笑,將照片發給了小李。
加密模式
如上過程一樣,為了使對稱加密演算法更可靠,密碼專家們玩出了一些新花樣,我們稱其為加密模式,常見的加密模式有ECB、CBC、CFB、CTR、OFB等,這裡僅挑選ECB與CBC介紹一下,如下:
ECB模式
ECB模式的全稱是電子密碼本模式(Electronic CodeBook),在ECB模式中,每個明文分組加密後,直接作為密文分組輸出,如下:
這種模式其實和上面的XOR加密演算法一樣,每一小塊資料,都使用相同的金鑰和方式進行加密,所以這也解釋了為啥圖片加密後還能看到輪廓,因為金鑰在每一小塊上的擾亂效果是整齊劃一的,當圖片很大時,從巨集觀上會透出少量資訊出來。
一般來說,現在已不建議再使用ECB模式加密資料了。
CBC模式
CBC模式全稱是密文分組連結模式(Cipher Block Chaining),之所以叫這個名字,是因為密文分組像鏈條一樣相互連線在一起。
可以發現,CBC模式很巧妙,它將前一組加密產生的密文分組,參與到下一組的加密過程中去了,用前面的資料擾亂後面的資料!
實際上分組加密的過程,可以想像成在圖片上撒沙,其中圖片就是等待加密的資料,而金鑰則是沙。
對於ECB模式,就是每個區域撒上相同分佈的沙,而對於CBC模式,則是每次撒的沙會變花樣。
初始化向量
可以發現,在使用CBC模式時,需要一個初始化向量IV,其實它就是一個分組長度的隨機bit序列,用來增強第一個分組的加密效果,那它有什麼作用呢?
我們有時候也稱初始化向量為鹽(Salt),它的作用就像鹽一樣,擾亂加密後的資料,假如你使用的128bit金鑰很簡單,這時明文的前128bit(第一個分組)加密效果就會很差,這個分組很容易被破譯,而有了初始化向量後,由於初始化向量是隨機的,這會導致第一個分組也會被干擾得面目全非。
使用初始化向量還有一個好處,二次加密過程,就算是使用相同的金鑰,加密出來的密文也不一樣。
有個疑問是,如果解密方沒有初始化向量,根本無法解密啊!
是的,所以初始化向量需要告訴解密方,一種方法是每一次加密時生成初始化向量,並將初始化向量拼接在密文開頭,是的,初始化向量並不是金鑰,沒有保密需求,直接附在密文中也沒有關係。
填充模式
分組加密演算法中還有一部分就是填充演算法,如NoPadding, PKCS#5, PKCS#7, ISO 10126, ANSI X9.23和ZerosPadding等。
為什麼會需要填充演算法呢?這是因為分組加密演算法是一組一組加密的,比如分組大小是128bit的話,如果明文資料最後一組不足128bit,該怎麼辦呢?這時就需要填充演算法了,而PKCS#7演算法是目前使用得最廣泛的填充演算法了。
所以,在Java中要使用AES對資料進行加密時,應傳遞的演算法名稱為AES/CBC/PKCS5Padding
,其中AES是演算法,CBC是加密模式,而PKCS5Padding
就是填充模式,而PKCS5Padding
實際上就是blockSize=8bytes
的PKCS#7
。
pkcs5與pkcs7區別:https://www.cnblogs.com/midea0978/articles/1437257.html
流加密
流加密(stream cipher),是對資料流進行連續處理的一類密碼演算法。流密碼中一般以1bit、8bit或32bit等為單位進行加密和解密。
常見的流加密演算法有RC4、A5等,雖然它們也是對稱加密演算法的一類,但目前來說,分組加密演算法使用得更為廣泛一些,因此不做過多介紹。
非對稱加密
非對稱加密,又稱公鑰加密,使用非對稱加密演算法,需要生成一對金鑰,公鑰與私鑰,然後加密方使用公鑰進行加密,而解密方使用私鑰進行解密。
- 私鑰:是需要自己保留的私密金鑰,不能讓別人知道的。
- 公鑰:可以公開給大眾使用,以使他們可以和自己通訊。
就像客戶端與伺服器模式一樣,多個客戶端擁有公鑰,客戶端使用公鑰加密資料後傳送給服務端,而加密後的資料只有持有私鑰的伺服器才可以解密出來。
注:實際上也可以使用私鑰來加密,使用公鑰來解密,後面介紹的數字簽名技術,會使用這種方法。
RSA
RSA是使用最廣泛的公鑰演算法,在1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成。
RAS實際加解密過程很簡單,都是對資料進行乘方運算後取模,如下:
加密:密文 = 明文 ^ E % N,其中E、N就是兩個數字
解密:明文 = 密文 ^ D % N,其中D、N就是兩個數字
其中(E,N)組合在一起為公鑰,(D,N)組合在一起為私鑰,如下,假設E=5,N=323,D=29,可以發現234經過RSA加密後變成81、RSA解密後還原成了234:
$ bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
234^5%323
81
81^29%323
234
當然E、N、D不是隨便得來的數字,它們是根據一系列數學過程計算而來的,有著隱含的數學關係,因此RSA還設計了一套金鑰對生成演算法,用以生成E、N、D這3個數字。
關於金鑰對生成細節,以及RSA加解密在數學上的證明,這裡不再贅述,可以參考阮一峰的解析:
https://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
https://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html
非對稱加密演算法,除了RSA以外,還有如ElGamal、Rabin、ECC等,一般來說,非對稱加密演算法都是基於數學難題來實現的,比如RSA基於大數質因數分解難題。
而且非對稱加密演算法還有一個特點,就是加密後的資料,自己都無法解密,只有持另一個金鑰的人才能解密。
模運算
RSA的數學原理比較複雜,但我們可以簡單的認為,RSA加密後的密文,之所以能夠解密,主要貢獻是模運算,可以通過簡單的同餘定理來理解,如下:
同餘:如果 p , c 對 m 求模後得到的餘數相同,則稱模m與p、c同餘。
在同餘運算規則中,有一個同餘式相加運算,如下:
(A+B)%C = (A%C + B%C)%C
假設C=256(1位元組大小),利用此規則,可以得到如下演算過程:
- 將x看作A,127+129作為整體看作B,得到:
(x + 127 + 129) % 256 = (x % 256 + (127 + 129) % 256) % 256 = x % 256 - 將x+127作為整體看作A,將129看作B,得到:
(x + 127 + 129) % 256 = ((x + 127 ) % 256 + 129 % 256) % 256 = ((x + 127 ) % 256 + 129) % 256 - 由1, 2可得到:
x % 256 = ((x + 127 ) % 256 + 129) % 256 - 當x小於256時,可得到:
x % 256 = x,所以 x = ((x + 127 ) % 256 + 129) % 256 - 因此,可得到一個非對稱加密演算法如下:
加密:(x + 127) % 256 = S (密文)
解密:(S + 129) % 256 = ((x + 127) % 256 + 129) % 256 = x (明文)
其中,公鑰是(127,256),私鑰是(129, 256),這個加解密過程看起來是不是和RSA很像?只不過RSA用的是模冪運算,而這裡使用的是簡單的相加求模運算!
可以把基於模運算的加解密,看作是在轉圈圈,起始明文在如下位置:
經過加密之後,跑到快半圈,到了160(密文)的位置,如下:
再經過解密之後,跑完了1整圈,又回到了33(明文)的位置,如下:
當然,這個非對稱加密演算法並不安全,如下:
- 這個演算法只跑1圈就回到明文了,很容易根據密文規律推算出明文,而RSA演算法加解密都是用的模冪運算,會跑很多圈,所以破解難度很大,數學上稱為離散對數難題。
- 這個演算法公鑰與私鑰之和就是一圈,根據公鑰很容易推算私鑰,但RSA演算法公鑰與私鑰的隱含數學關係更復雜,要想通過公鑰推算私鑰,需要解決大數質因數分解難題。
Linux工具命令
# 生成私鑰與公鑰
openssl genrsa -out rsa_private_key.pem 2048
openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
私鑰與公鑰是由數字E、N、D組成的,如下可檢視私鑰、公鑰檔案中這些數字的值:
# 讀取私鑰資訊
$ openssl rsa -in rsa_private_key.pem -text -noout
RSA Private-Key: (2048 bit, 2 primes)
modulus:
00:d1:ca:47:3a:c5:34:b1:2f:4e:f0:a7:29:0d:99:
de:51:89:ae:1c:1f:81:22:53:7c:fe:c1:68:62:80:
...
publicExponent: 65537 (0x10001)
privateExponent:
00:cb:f3:8b:d5:fd:dc:61:19:2d:f4:55:7e:5a:c3:
a8:d7:ba:32:f3:12:49:b7:76:55:01:52:43:c9:e7:
...
prime1:
00:f3:e7:8f:7a:00:fc:bf:72:12:8f:c1:85:81:4b:
75:a8:88:7c:5e:a6:3b:9e:d1:3c:02:53:e0:2e:f2:
...
prime2:
00:dc:31:a0:2c:da:df:c5:1e:e3:20:ca:44:1b:9e:
33:2a:69:8d:2d:7d:a4:d6:7d:eb:c1:75:cb:1f:d0:
...
exponent1:
00:a1:e0:45:b1:4b:86:73:e9:59:b8:5f:50:24:07:
d9:07:09:ce:c1:62:c2:9f:1d:6f:1e:7c:5c:85:cc:
...
exponent2:
6e:70:f9:9c:e5:df:0c:b8:b4:45:13:0e:5c:27:da:
13:f0:c3:1d:c9:02:2f:8f:12:fb:82:c0:71:e1:b9:
...
coefficient:
5e:94:e2:3f:86:97:c4:24:8d:4b:e7:e9:ed:10:bc:
e3:d5:79:3c:f8:bd:c5:33:5f:52:14:34:a0:72:83:
...
# 讀取公鑰資訊
$ openssl rsa -pubin -in rsa_public_key.pem -text -noout
RSA Public-Key: (2048 bit)
Modulus:
00:d1:ca:47:3a:c5:34:b1:2f:4e:f0:a7:29:0d:99:
de:51:89:ae:1c:1f:81:22:53:7c:fe:c1:68:62:80:
...
Exponent: 65537 (0x10001)
使用公私鑰加解密,如下:
# 加密
$ echo -n hello | openssl rsautl -encrypt -in - -inkey rsa_public_key.pem -pubin -out encrypt.bin
# 解密
$ openssl rsautl -decrypt -in encrypt.bin -inkey rsa_private_key.pem
hello
加密演算法總結:
- 對稱加密演算法由於大多數操作都是類似XOR的位運算,加密速度很快,但像客戶端-伺服器這種模式,需要給每個客戶端分配一個對稱金鑰,金鑰管理難度較大。
- 非對稱加密演算法可以將公鑰告知給所有客戶端,服務端使用私鑰解密,能很好的應對客戶端-伺服器模式,但由於需要做數學運算,加密速度較慢。
戀愛日記:提升溝通效率
小李與小紅了解到了對稱與非對稱加密演算法的優缺點,決定結合使用兩種密碼學演算法,如下:
- 小紅開始時隨機生成一個對稱金鑰,然後使用公鑰演算法加密,將加密後的對稱金鑰傳送給小李。
- 小李使用私鑰將對稱金鑰解密出來。
- 然後小紅和小李就使用對稱金鑰加解密資料,以完成資訊互通。
- 並且,小紅和小李決定每隔一天,重新生成一個新的對稱金鑰。
混合密碼系統
嗯,他倆這辦法還真不錯,這種結合各種密碼演算法優缺點的方法,在密碼學裡叫混合密碼系統,像SSL/TLS就是典型的混合密碼系統,看看SSL/TLS中的一個密碼套件TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
,它混合使用了多種密碼演算法,包括我們後面會介紹的ECDHE
與SHA256
。
金鑰輪換
小李與小紅每隔一天就更換一次對稱金鑰,這種定期更改金鑰的方式叫金鑰輪換,金鑰輪換在機密通訊場景也經常使用,這樣就算某個金鑰被洩露,也不至於所有加密資料都失去保護。
然而當大李瞭解到他們的這個方案後,笑了笑說:你們這個方案不滿足前向安全性啊!
前向安全性
簡而言之,前向安全性就是,當你的主金鑰被洩露之後,在洩露時間點之前通訊的資料依然是安全的,即使攻擊者在網路中將你們之前的通訊資料全都監聽儲存下來了。
像小李與小紅其實發明瞭一種最簡單的金鑰協商方案,這種方案是小紅使用RSA公鑰將對稱金鑰加密後,直接通過網路傳送給對方,但如果小李的RSA私鑰不小心洩露了,那麼他們之前每次協商的對稱金鑰都能被解密出來,從而通訊資料也會被解密出來!
所以,這種通過加密協商金鑰的方案,就不滿足前向安全性。
金鑰協商演算法
因而,密碼學家們發明了一類非常巧妙的密碼學演算法,不需要在網路中直接傳送金鑰,通訊雙方就能計算出同樣的金鑰出來,這類演算法統稱為金鑰協商演算法,如DH與ECDH。
DH(Diffie-Hellman)演算法原理如下:
假設Ali和Bob需要互相通訊並共享祕鑰
- Ali先給Bob一個明文共享引數P,G,此資訊可以被任何人識別
- Ali自己生成一個隨機數A(Ali的私鑰) ,並不將A告訴包括Bob在內的任何人
- Bob自己生成一個隨機數B(Bob的私鑰) ,並不將B告訴包括Ali在內的任何人
- Ali計算(G^A % P),並傳送給Bob
- Bob計算(G^B % P),並傳送給Ali,通過計算 (G^A % P)^B % P = G^(A*B) % P = S 得到共享金鑰
- Ali同樣通過計算 (G^B % P)^A % P = G^(A*B) % P = S 得到共享金鑰
DH演算法安全性基於離散對數難題,當P是一個非常大的質數時,Ya=G^A % P通過Ya很難推算出A。
而ECDH是結合ECC與DH的金鑰協商演算法,其安全性基於橢圓曲線上的離散對數難題。
戀愛日記:殘缺的照片
本來小紅與閨蜜小美是形影不離的,但現在小紅很少和她玩了,並且小紅每天在她面前炫耀男友,惹得小美醋心大發,心中不樂,決定棒打鴛鴦,於是她隱藏在網路裡,將小紅髮給小李的密文胡亂修改後,再轉發給小李,如下:
# 小美將圖片中第90000個分組(16位元組)後的160000位元組,篡改成隨機位元組
(head -c $((54+16*90000)) image4.bmp; head -c 160000 /dev/urandom; tail -c +$((54+16*90000+160000+1)) image4.bmp) > image5.bmp
然後,小李解密圖片,發現自己收到的圖片被解密後,女友照片中有些區域亂七八遭的,如下:
(head -c 54 image5.bmp;tail -c +55 image5.bmp | openssl enc -aes-128-cbc -d -in - -out - -k password -iv 0102030405060708) > image6.bmp
小李馬上和大李溝通了此事,大李覺得,這應該是有人在網路中篡改了密文導致!
是的,AES演算法雖然能保證機密性,但不法防止別人篡改密文,要想解決這個問題,有兩種辦法,如下:
- 使用認證加密演算法可以防篡改,如AES-GCM演算法,這類演算法統稱為AEAD演算法,這裡暫不做過多討論。
- 使用單向雜湊演算法,如MD5、SHA1、SHA256等,這是我們接下來會介紹的...
單向雜湊演算法
單向雜湊演算法,有時也稱為(密碼學)雜湊演算法,主要作用是生成資料的一個雜湊值,也稱訊息摘要或“指紋”,用於驗證資料在傳輸過程中是否發生了篡改,常見的雜湊演算法有MD5、SHA1、SHA256等等。
雖然像CRC32、CRC64這樣的雜湊演算法,也能生成32位或64位的資料摘要,但由於它們不是為密碼學領域而設計的,所以它們生成的資料雜湊值非常容易衝突碰撞,即不同資料生成的雜湊值相同。
單向雜湊演算法有如下特點:
- 強抗碰撞性,哪怕原資料只改一bit,雜湊值也會發生很大的變化,要想找到兩個雜湊值一樣的對於人類有意義的資料,是非常困難的。
- 單向性,通過資料可以計算出雜湊值,但通過雜湊值無法反算出資料。
單向雜湊演算法的用途:
- 由於原資料修改之後,雜湊值會發生改變,所以MD5等密碼學雜湊演算法可用來檢測資料是否被篡改。
- 由於像MD5等密碼學雜湊演算法,它們通常都具有非常強的抗碰撞性,對於人類產生的有意義的不同資料,幾乎不可能生成相同的雜湊值,所以MD5等演算法有時也會用來生成資料的唯一標識。
Linux工具命令
在Linux中,可用如下命令使用單向雜湊演算法:
# md5生成128位的雜湊位元組
$ echo -n hello | md5sum
5d41402abc4b2a76b9719d911017c592 -
# sha1生成160位的雜湊位元組
$ echo -n hello | sha1sum
aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d -
# sha256生成256位的雜湊位元組
$ echo -n hello | sha256sum
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 -
一般來說,生成的雜湊值長度越長,抗碰撞性越好,所以目前來說,一般推薦使用SHA256演算法。
戀愛日記:我討厭你!
瞭解了單向雜湊演算法的作用後,小紅與小李在後面的通訊中使用了SHA256,如果小李發現圖片的雜湊值與小紅給的不對應,就讓小紅重發一遍即可。
很快小美也發現了這個情況,心裡更加不愉快了,經過她一翻思考,想成小紅能生成雜湊值,我也可以啊,於是小美給小李傳送了一條“我討厭你”的訊息,並且也發了這條訊息的SHA256雜湊值。
小李收到這條訊息後,大為震驚,不知是哪裡惹小紅不開心了,但與小紅溝通後,發現小紅根本就沒有發這條訊息過來,有人冒充小紅給小李發訊息,還帶有雜湊值,不知怎麼辦的小李,又找到了大李。
大李笑了笑,你們是時候使用訊息認證碼技術了...
訊息認證碼
訊息認證碼(message authentication code)是一種確認完整性並進行認證的技術,取三個單詞的首字母,簡稱為MAC.
訊息認證碼的輸入包括任意長度的訊息和一個傳送者與接收者之間共享的金鑰,它可以輸出固定長度的資料,這個資料稱為MAC值。
密碼學家們發明了很多訊息認證碼演算法,最常見有兩類,一類是HMAC,全稱Hash MAC,顧名思義就是基於密碼學雜湊演算法實現的訊息認證碼演算法,另一類是CMAC,全稱Ciper MAC,顧名思義就是基於加密演算法實現的訊息認證碼演算法。
HMAC
而一般而言的HMAC演算法,是指下面這樣的標準HMAC演算法。
HMAC演算法特點是,可以隨意搭配單向雜湊演算法,如MD5、SHA1、SHA256等,比如在Java中的hmacWithSha256演算法,就是HMAC搭配SHA256。
Linux工具命令
在Linux中,hmac256命令就是hmacWithSha256,如下:
$ echo -n 'hello'|hmac256 'secret'
88aab3ede8d3adf94d26ab90d3bafd4a2083070c3bcce9c014ee04a443847c0b
另外,我們常常見到開放平臺指定簽名演算法時,會使用如下的方法:
sign = md5(appid + text + appsecret)
其實這就是自行發明了一種簡單的HMAC演算法。
另外,如果使用這種拼接方式實現MAC演算法,最好在text兩側都拼接上東西,具體可見:雜湊長度擴充套件攻擊:https://www.cnblogs.com/dre0m1/p/15858202.html
戀愛日記:我喜歡你!
小紅與小李瞭解了訊息認證碼技術後,立馬決定使用HMAC演算法,這也很快被小美發現了,小美發現好像無法冒充小紅髮訊息了,因為自己不知道他們使用HMAC時的金鑰,苦苦思索中...
有一次,小紅被撩嗨了,頭腦發熱給小李發一句“我喜歡你”,但隨後小紅又後悔了,覺得自己如此主動表達心意,後面戀愛過程會被小李拿捏的。
小李喜出望外,立馬去找小紅,結果小紅否認這訊息是她發的,小李與小紅爭執起來,說金鑰只有你知我知,不是你發的,難道是我發的?
小紅故意淘氣的說,誰知道你是不是自戀狂啊!
小李傷心回到家後,與大李說了此事,大李安慰了一下小李,說是時候使用數字簽名技術了...
數字簽名
從上面的問題,我們可以發現,訊息認證碼無法防否認,原因是通訊雙方擁有相同的金鑰,為了解決這個問題,需要傳送方與接收方使用不同的金鑰,傳送方用一個金鑰簽名,接收方用另一個金鑰驗籤,這就是數字簽名技術。
而非對稱加密技術就是使用不同金鑰加解密的技術,剛好可以滿足數字簽名的需求,如下:
數字簽名的生成與驗簽過程,和公鑰加密的過程剛好是相反的,如下:
- 公鑰加密的過程是,傳送方使用公鑰進行加密,然後接收方使用私鑰進行解密。
- 公鑰簽名的過程是,簽名方使用私鑰生成簽名,然後驗籤方使用公鑰驗籤。
對於數字簽名過程,需要注意兩點:
- 所謂的簽名其實就是私鑰加密,而驗籤即是將簽名值(即密文)使用公鑰解密,然後與明文進行對比。
- 為了提升簽名、驗籤的速度,在簽名(加密)前給明文資料做了雜湊,而解密後與明文雜湊值做對比。
注:理論上,用公鑰簽名,私鑰驗籤也是可以的,但由於在定義上公鑰是公開給別人的金鑰,而如果多方都可以使用公鑰簽名的話,就不符合簽名的需求了。
Linux工具命令
# 生成私鑰與公鑰
openssl genrsa -out rsa_private_key.pem 2048
openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
# 簽名
echo -n hello | openssl rsautl -sign -in - -inkey rsa_private_key.pem -out sign
# 驗籤
openssl rsautl -verify -in sign -inkey rsa_public_key.pem -pubin
為什麼可以防否認?
因為私鑰只有簽名方才有啊,除了它,其它人都無法生成簽名了,包括公鑰持有方。
戀愛日記:分手吧!
在大李介紹了數字簽名技術之後,小李與小紅立馬使用RSAWithSha256簽名演算法,當然這需要兩人各生成一對金鑰,如下:
- 小紅自己生成一對公私鑰後,將公鑰發給小李,私鑰則自己保留,誰都不告訴。
- 小李自己也生成一對公私鑰後,將公鑰發給小紅,私鑰也自己保留,誰都不告訴。
- 小紅髮訊息時,同時使用自己私鑰為訊息計算一個簽名發給小李,小李則拿小紅公鑰驗籤。
- 小李發訊息時,也使用自己私鑰為訊息計算一個簽名發給小紅,小紅則拿小李公鑰驗籤。
看起來完美了,小紅與小李都無法否認訊息了!
可好景不長,小美為了破壞戀愛過程,發現了一種叫做中間人攻擊的手段,如下:
中間人攻擊
比如Alice(小紅)與Bob(小李)通訊,需要把Bob的公鑰傳送給Alice,但Alice與Bob之間有個Mallory(小美),Mallory將Bob傳送的公鑰截獲後,將自己的公鑰發給了Alice,而之後Alice實際在與Mallory通訊,Mallory與Bob在通訊,Mallory可以解密出所有通訊內容!
於是,小美在小紅與小李互發公鑰時,將他們的公鑰截獲儲存起來,然後將自己的公鑰發給雙方!於是,小美在中間可以為所欲為了,給雙方發了一句“分手吧”,如下:
- 當小紅用私鑰給訊息簽名時,小美截獲了訊息,然後自己發一條訊息“分手吧”給小李,同時用自己的私鑰簽名訊息,並把簽名也發給小李。
- 當小李收到訊息後,使用“小紅的公鑰”驗籤,而這個公鑰實際是小美的公鑰,於是驗籤通過,小李篤定訊息是小紅髮的。
- 小紅也同理,收到“分手吧”訊息,且篤定訊息是小李發的。
好傢伙,小李與小紅傷心欲絕,差點真分手了!好在大李知道這個訊息後,認真考察了小李與小紅,覺得他們確實沒有發這個訊息,猜測可能發生了中間人攻擊!
大李在安慰了小紅與小李後,說:我來給你倆做擔保,將你們的公鑰變成證照吧!
證照
信任問題與PKI
上面之所以出現中間人攻擊,是因為小紅在收到小李的公鑰時,並不知道這個公鑰是不是小李的,反之亦然,那怎麼證明這個公鑰就是小李的呢?
- 一是需要在公鑰上寫上小李的名字,但小李可以在公鑰上寫"小李",小美也可以在公鑰上寫"小李",所以僅僅有名字還不夠
- 二是需要在公鑰上做數字簽名,類似蓋章一樣,那誰來蓋這個章?
回想一下,我們一生中會辦各種證照,學生證、身份證、結婚證,房產證,為什麼你出示結婚證,別人就會相信你結婚了?因為結婚證是國家發的啊,上面有國家機構蓋的章呢,我們普通人有理由相信國家蓋章的證照是真實的證照,畢竟它的章不好偽造,也沒人敢偽造。
在計算機世界也一樣,為了解決公鑰認證問題,人們構建了公鑰基礎設施PKI和認證機構CA,比如小李在自己的公鑰寫上名字,然後由大李(CA認證機構)使用自己的私鑰為其生成數字簽名,然後將公鑰、名字、數字簽名打包成一個檔案,這個檔案就是小李的數字證照,就像由CA機構(大李)蓋過章一樣。
而小紅在收到小李的數字證照後,它首先會使用大李(CA機構)的公鑰來驗證數字簽名是否正確,然後又會確認證照上的名字是不是小李,數字簽名與名字都正確後,取出證照中的公鑰與小李通訊。
這裡面大李就類似於CA機構,大李可以私底下將自己的公鑰給小李與小紅。
那對於網際網路上千千萬萬的網民,怎麼會有CA機構的公鑰的?這是因為CA機構聯合了作業系統、瀏覽器廠商,預先將它們的公鑰預設內建到了作業系統或瀏覽器中。
證照例子:
如下,是百度網站的證照
證照上可以看到這樣的證照路徑,這是一個遞迴的過程,大的CA機構可以給中等的CA機構的證照蓋章,中等的CA機構又可以小的CA機構的證照蓋章,如此下去會形成一棵樹,所以我們的證照,一般都被根CA機構委託幾次後的CA機構蓋章的。
curl與openssl也可以看到部分證照資訊,如下:
curl -v https://www.baidu.com
openssl s_client -connect www.baidu.com:443 -prexit
注:這裡小紅與小李雙方都生成了證照,而在CS架構裡,一般只需要服務端生成證照即可,由於客戶端不需要提供證照,這也導致服務端收到的請求是不可信的,需要附加Cookie等認證機制並校驗請求資料合法性。另外,SSL/TLS其實是可以配置成雙向認證的。
CA機構職責
你可能還會有疑問,我自己生成一個公鑰,然後在公鑰上寫上名字baidu.com,然後給CA機構簽名,那不一樣可以實施中間人攻擊?
確實有這樣的可能性,但CA機構並不是隨便給證照籤名的,它會讓你出具各種證明材料並嚴格審查,只有認定你確實是baidu才會在證照上簽名。
並且,只有CA機構的私鑰不洩露,簽名才可靠,為了保證自己的私鑰不被洩露,需要對私鑰進行嚴格的訪問控制,甚至派專人值守在私鑰放置處,因此,CA機構簽發證照是需要收錢的,就好像國家保證你的安全需要收稅一樣。
戀愛日記:歡樂時光
終於,使用了證照後,小紅與小李迎來了他們的歡樂時光!然而,小美在瞭解到了社會工程學攻擊後,竟然也偷偷的與大李約會了...
密碼演算法建議
- 加密演算法,建議使用AES,加密模式使用CBC或GCM,普通安全級別AES128足夠了,但考慮到量子計算機的威脅,絕密場景建議使用AES256。
- RSA演算法,要保證安全性,至少需要使用2048位的金鑰長度。
- 金鑰協商演算法,為保證前向安全性,建議使用ECDH金鑰協商演算法,且金鑰長度至少384位。
- 雜湊認證演算法,目前不建議使用MD5、SHA1,HMAC演算法目前還是非常安全的。