[信息安全] 1.密码工具箱
0. 何謂安全?
對(duì)于信息安全性的重要性,我想大家都不會(huì)否認(rèn)。那么具體來說應(yīng)該具有哪些特性才能稱之為安全呢?舉個(gè)簡單的例子:我給你發(fā)送一條消息“借給我100元”,當(dāng)你收到這條消息并且處理后你的賬戶里面會(huì)少出來100塊,我的賬戶會(huì)多出來100塊。在這個(gè)過程中,你是消息接收方,我是消息發(fā)送方。
作為通信雙方的你我都不希望讓其他人能讀懂這條消息,這是信息的機(jī)密性,即消息在傳遞過程中不被其他人解讀。
作為通信雙方的你我都不希望消息內(nèi)容變成"借老子1000塊!"(操,借錢還這么牛逼,100塊都不給你,還要1000塊!死去...),這是信息的完整性,即可以校驗(yàn)出信息在傳送過程中是否被篡改。
作為消息接收方的你需要確認(rèn)是不是真正的我給你發(fā)的借錢的消息吧,會(huì)不會(huì)是個(gè)詐騙犯要騙我100塊!這是對(duì)信息的認(rèn)證,即接收者要可以驗(yàn)證消息的發(fā)送者確實(shí)是自己希望的發(fā)送者。
作為消息接收方的你肯定不希望在借給了我100塊之后,我耍無賴失口否認(rèn)說沒借過你錢,這是信息的不可否認(rèn)性,即消息發(fā)送者不可以否認(rèn)說這個(gè)信息不是我發(fā)送的。
總結(jié)來說,在通信過程中,滿足這4個(gè)特征:機(jī)密性,完整性,認(rèn)證,不可否認(rèn)性,就可以認(rèn)為信息是安全的。那么接下來的幾個(gè)小節(jié)來介紹一下有那些工具可以使得我們?cè)趥鬟f消息的時(shí)候具有以上4個(gè)特征。
1. 對(duì)稱密碼-對(duì)稱密鑰(Symmetric Cryptography)
對(duì)稱密碼加密可以保障信息的機(jī)密性。舉一個(gè)簡單的例子,一把鎖,兩把相同的鑰匙,就是對(duì)稱密碼;即:使用相同的密鑰來加密和解密。沒有密鑰的其他人是無法解讀信息的真正內(nèi)容是什么的。常見到兩個(gè)對(duì)稱加密標(biāo)準(zhǔn)有DES和AES。
DES是一種對(duì)稱密鑰加密算法,在1976年被美國聯(lián)邦政府的國家標(biāo)準(zhǔn)局確定為聯(lián)邦資料處理標(biāo)準(zhǔn),隨后在國際上廣泛流傳開來。它基于使用56位密鑰的對(duì)稱算法。現(xiàn)在現(xiàn)在已經(jīng)不是一種安全的加密方法,主要因?yàn)樗褂玫?6位密鑰過短。后來又發(fā)展出了3DES(即執(zhí)行三次DES加密)。由于DES已經(jīng)不再安全,后來又推出了新的對(duì)稱加密標(biāo)準(zhǔn)AES,采用的算法為Rijndael。算法的具體實(shí)現(xiàn)邏輯這里不去解釋,這里關(guān)注的是如何利用它們(即,保障信息的機(jī)密性的手段)。看一下簡單的加密解密的數(shù)學(xué)公式:
加密:encrypted_data = encrypt_function(message, key);
解密:message = decrypt_function(encrypted_data, key);
C#使用AES的代碼如下:
/// <summary>/// ?AES加密
/// </summary>
/// <param name="key">128bit,192bit,125bit</param>
/// <returns></returns>
public static byte[] AESEncrypt(this byte[] value, byte[] key) { ? ?//todo 參數(shù)檢查using (var symmetricAlgorithm = Aes.Create()){symmetricAlgorithm.Key = key;symmetricAlgorithm.Mode = CipherMode.ECB;symmetricAlgorithm.Padding = PaddingMode.PKCS7; ? ?
? ?//加密using (var encryptor = symmetricAlgorithm.CreateEncryptor()){ ? ? ? ? ? ?return encryptor.TransformFinalBlock(value, 0, value.Length);}} }
/// <summary>
/// ?AES解密
/// </summary>
/// <param name="key">128bit,192bit,125bit</param>
/// <returns></returns>
public static byte[] AESDecrypt(this byte[] value, byte[] key) { ?
?//todo 參數(shù)檢查using (var symmetricAlgorithm = Aes.Create()){symmetricAlgorithm.Key = key;symmetricAlgorithm.Mode = CipherMode.ECB;symmetricAlgorithm.Padding = PaddingMode.PKCS7; ? ? ? ?
//解密using (var encryptor = symmetricAlgorithm.CreateDecryptor()){ ? ? ? ? ? ?return encryptor.TransformFinalBlock(value, 0, value.Length);}} }static void Main() { ? ?var value = "lnh".ToBytes(Encoding.UTF8); ? ?//構(gòu)造128bit的key,guid正好是128,權(quán)且當(dāng)作key了。var key = Guid.NewGuid().ToByteArray(); ? ?var encryptedData = value.AESEncrypt(key); ? ?var decryptedData = encryptedData.AESDecrypt(key); ? ?var decryptedDataString = Encoding.UTF8.GetString(decryptedData);Console.WriteLine(); }
.Net庫已經(jīng)封裝好了一些對(duì)稱加密的類,開箱即用:
1.1 遺留問題
密鑰配送問題:共享的密鑰如何交到接受消息方的手上呢?雙方可以事先共同約定一個(gè)密鑰,但是這種辦法是無法滿足互聯(lián)網(wǎng)規(guī)模的需要的,互聯(lián)網(wǎng)規(guī)模的環(huán)境是假設(shè)通信雙方事先并不知道對(duì)方的存在,怎么事先約定呢,行不通吧。
下面接下來的公鑰密鑰可以解決這個(gè)問題。
2. 公鑰密碼-非對(duì)稱密鑰(Asymmetric Cryptography)
對(duì)稱密碼加密可以解決信息的機(jī)密性的問題,但是卻無法提供雙方如何才能得到加密所用密鑰的途徑。我們回到最初的目的想一想,我們想要的機(jī)密性的核心在于別人無法取得信息的真實(shí)內(nèi)容,也就是解密;而如何生成這個(gè)機(jī)密的信息,其實(shí)并不是我們關(guān)注的點(diǎn),你能生成,他能生成,都沒區(qū)別,只要我控制住只有我才能解密,那么機(jī)密性的問題就解決了。所以解決密鑰配送的問題的關(guān)鍵就在于,把密鑰分成兩部分,一個(gè)加密用,一個(gè)解密用,它們總是成對(duì)出現(xiàn)的。配送的是加密用的密鑰(也叫公鑰),解密用的叫私鑰,這個(gè)只有我自己知道,不會(huì)在任何地方傳輸,那么也就不存在配送的問題了。
其實(shí)很多計(jì)算機(jī)中的問題都是無解的,往往卻又是有解決辦法的,它的解決辦法其實(shí)并不是直接的解決這個(gè)問題,而是規(guī)避掉這個(gè)問題,使得它不在是一個(gè)問題的。比如密鑰配送的問題,如果說我們有安全的方式解決密鑰配送的問題,直接使用這個(gè)安全的方式配送我們想要傳遞的信息不就是了,我們還繞個(gè)彎配送密鑰干什么呢。公鑰密碼其實(shí)并未解決密鑰配送的問題,而是使得它不再是個(gè)問題,即:公鑰可以公開給任何人,不再需要保密(本質(zhì)上來說,密鑰和待加密的信息同樣重要),而是通過控制解密來達(dá)到我們想要的機(jī)密性,繞過了如何機(jī)密的配送密鑰的問題。
公鑰密碼就是這么一個(gè)簡單的原理:公鑰(=public key)加密,私鑰(=private key)解密,它可以保障信息的機(jī)密性,同時(shí)解決密鑰的配送問題。那么這個(gè)時(shí)候通信雙方的流程就是這樣的:
發(fā)送方向接收方請(qǐng)求一個(gè)??public?key? ;
發(fā)送方使用?public?key?加密消息,?public?key?和加密的消息泄露都沒關(guān)系,因?yàn)橹挥?private?key?才能解密;
接收方用?private?key?解密消息。
至于如何產(chǎn)生出來這樣一對(duì)?public?key?和?private?key?以及相對(duì)于的加密解密算法,這其中涉及到很復(fù)雜的數(shù)學(xué)問題,這里就不展開介紹了(筆者也不懂...)。我們看一下最廣泛使用的公鑰密碼算法RSA在C#里面怎么使用吧:
/// <summary>/// ?RSA加密
/// </summary>
/// <param name="publicKey">公鑰</param>
/// <returns></returns>
public static byte[] RSAEncrypt(this byte[] value, string publicKey) { ? ?//todo 參數(shù)檢查using (var asymmetricAlgorithm = new RSACryptoServiceProvider()){asymmetricAlgorithm.FromXmlString(publicKey);
? ? ? ?return asymmetricAlgorithm.Encrypt(value,false);} }
/// <summary>
/// ?RSA解密
/// </summary>
/// <param name="privateKey">私鑰</param>
/// <returns></returns>
public static byte[] RSADecrypt(this byte[] value, string privateKey) { ? ?//todo 參數(shù)檢查using (var asymmetricAlgorithm = new RSACryptoServiceProvider()){asymmetricAlgorithm.FromXmlString(privateKey); ? ?
?? ?return asymmetricAlgorithm.Decrypt(value,false);} }
static void Main() { ?
?string privateKey; ?
?string publicKey; ?
??using (var asymmetricAlgorithm = RSA.Create()){privateKey = asymmetricAlgorithm.ToXmlString(true);publicKey = asymmetricAlgorithm.ToXmlString(false);} ?
?var value = "lnh".ToBytes(Encoding.UTF8); ? ?//公鑰加密var encryptedData = value.RSAEncrypt(publicKey); ? ?//私鑰解密var decryptedData = encryptedData.RSADecrypt(privateKey); ?
?var decryptedDataString = Encoding.UTF8.GetString(decryptedData);Console.WriteLine(); }
.Net庫中已經(jīng)提供了公鑰密碼相關(guān)的類,開箱即用:
2.1 對(duì)公鑰密鑰的攻擊
中間人攻擊:這鐘類型的攻擊發(fā)生在上述流程中的第一步,即發(fā)送方A向接收方B請(qǐng)求?public?key?的時(shí)候。這時(shí)有一個(gè)攔路打劫的家伙M,截獲了這個(gè)?publickey?,自己據(jù)為己有。然后M把自己的一個(gè)?public?key?給到了A,A是渾然不覺,傻乎乎的用這個(gè)假的?public?key?加密了信息,發(fā)送了出去,這時(shí)候M攔截到了這個(gè)消息,用自己的?private?key?解密了這個(gè)消息,然后篡改一番,用真正的?public?key?進(jìn)行加密,發(fā)給了B。這個(gè)時(shí)候B以為是A發(fā)送的,A也以為自己發(fā)給了B,其實(shí)都被M給玩了...文字可能不是很清晰,看圖:
2.2 遺留問題
公鑰的認(rèn)證問題:公鑰密鑰可以解決規(guī)避掉的配送問題,但是新問題又來了,這個(gè)公鑰真的是你的嗎?針對(duì)上述的中間人攻擊,其實(shí)我們發(fā)現(xiàn),獲取公鑰的這一方并不能確認(rèn)自己收到的公鑰就是自己真正請(qǐng)求的那一方提供的。這個(gè)問題先放一放(后續(xù)會(huì)介紹),下面先看看保障信息的完整性方面有那些工具可用。
3.?密碼散列函數(shù)(Cryptographic hash function)
密碼散列函數(shù)可以保障的信息完整性,用來校驗(yàn)要傳遞的信息是否被篡改過。比如通常在下載文件的時(shí)候,官方的網(wǎng)站上都會(huì)列出來其MD5或者SHA1的值來校驗(yàn)。它的工作原理和要求大致如下:
輸入一組數(shù)據(jù)message,然后得到一組簡短的數(shù)據(jù)hash,只要是采用相同的算法,輸入message就能得到hash:hash = hash_function (message);
其過程是不可逆的,你不能由hash得出message;
滿足message的微小變化會(huì)(比如只改動(dòng)1字節(jié))會(huì)使得hash產(chǎn)生巨大的變化(就好比兩個(gè)雙胞胎,各處都很像,但是他們的指紋卻不是相同的);
兩組不同的消息message1和message2,不能得出相同的hash(理論上可以,只是要盡可能的使這個(gè)過程困難)。
常用的密碼散列函數(shù)(算法)有Message Digest Algorithm以及Secure Hash Algorithm。
3.1 MD5(Message Digest Algorithm 5)
中文明為消息摘要算法第五版,這也說明其實(shí)它也有前面幾個(gè)版本,比如MD4(這里就不介紹了)。MD5算法是輸入任意長度的數(shù)據(jù)(Message),然后算出固定長度的數(shù)據(jù)?16byte=128bits?,用16進(jìn)制表示這16個(gè)byte就是32位。C#使用MD5的代碼如下:
1 /// <summary>2 /// MD5摘要算法
3 /// </summary>
4 /// <param name="value"></param>
5 /// <returns>128 bits,16 byte array</returns>
6 public static byte[] ToMD5(this byte[] value)
7 {
8 if (value == null || value.Length == 0)
9 ? ?{ ? ?
10 throw new ArgumentNullException(nameof(value));
11 ? ?} ? ?
12 using (var hashAlgorithm = MD5.Create())
13 ? ?{ ? ?
14 return hashAlgorithm.ComputeHash(value);
15 ? ?} ? ?
16 }
再一次指出,md5的結(jié)果是固定的?16byte=128bits?,用16進(jìn)制表示是32個(gè)字符。網(wǎng)上由很多的16進(jìn)制16個(gè)字符的md5,其實(shí)這都不是完整的md5,只是截取了32位中的16位而已。
3.2 SHA ( Secure Hash Algorithm )
?從使用者的角度來看,MD5和SHA沒有什么本質(zhì)區(qū)別,差異在于其算法的實(shí)現(xiàn)方式,生成的hash的長度,其抗攻擊破解的難度不一樣。此外由于SHA的強(qiáng)度比MD5要大,所以在計(jì)算SHA的時(shí)候,所消耗的資源(時(shí)間,空間都有)也會(huì)比MD5要多。即使如此,現(xiàn)在MD5(128bit)和SHA-1(160bit)均已遭到了破解:https://news.cnblogs.com/n/563589/。SHA家族現(xiàn)有的以下成員如下有SHA-1(160)、SHA-2(SHA-224,SHA-256,SHA-384,SHA-512)和SHA-3(SHA3-224,SHA3-256,SHA3-384,SHA3-512)。C#中使用SHA256的代碼如下:
/// <summary>/// SHA256哈希算法
/// </summary>
/// <returns>256 bits,32 byte array</returns>
public static byte[] ToSHA256(this byte[] value) { ? ?if (value == null || value.Length == 0){ ? ? ? ?throw new ArgumentNullException(nameof(value));} ? ?using (var hashAlgorithm = SHA256.Create()){ ? ? ? ?return hashAlgorithm.ComputeHash(value);} }
.NET的庫已經(jīng)幫我們封裝好了密碼散列函數(shù)相關(guān)的類,開箱即用。
3.3 密碼散列函數(shù)的實(shí)際應(yīng)用
檢查文件是否被修改:上面一開始舉得例子下載文件的例子。
基于口令的加密:通常我們?cè)诖鎯?chǔ)用戶的密碼的時(shí)候,都會(huì)采用這種方式(除非你是csdn),一般還會(huì)輔助的加上鹽。
消息認(rèn)證碼:后面介紹到到。
數(shù)字簽名:后面會(huì)介紹到。
偽隨機(jī)數(shù)生成器:后面會(huì)介紹到。
3.4 針對(duì)密碼散列函數(shù)的攻擊
強(qiáng)碰撞性攻擊:比如上面提到的Google破解了SHA-1,即使用大量的計(jì)算來找出兩個(gè)數(shù)據(jù)message不一樣,但是hash值卻一樣的過程,如果找到了這樣的兩塊數(shù)據(jù),那么再使用這個(gè)hash作為數(shù)據(jù)的完整性校驗(yàn)的手段,其實(shí)已經(jīng)沒有意義了。解決辦法是升級(jí)SHA-2,增大計(jì)算出這樣兩塊數(shù)據(jù)的難度。
暴力破解:比如網(wǎng)上很多的MD5的解密,其實(shí)原理在于他們有大量的MD5 hash庫,比如?123456?的MD5是?e10adc3949ba59abbe56e057f20f883e?,那么在你給我一個(gè)?e10adc3949ba59abbe56e057f20f883e?的時(shí)候,我就知道你的原文是?123456?。解決辦法是加鹽,增大這種暴力比對(duì)的難度。
針對(duì)上面兩種攻擊方式都是在于增加破解難度,使其在現(xiàn)有的計(jì)算能力下不能輕易的被攻破,沒有絕對(duì)的安全,只是相對(duì)上來說是安全的,當(dāng)破解你帶來的收益要低于其破解成本的時(shí)候,你才是安全的。
3.5 遺留問題
hash被篡改了:比如上面下載文件的時(shí)候官方會(huì)給出MD5或者SHA1的hash值,這里我們假設(shè)一下,官方提供hash值的渠道被黑掉了,給了你一個(gè)篡改過的hash值,然后你下載了一個(gè)被篡改過的文件,你是分辨不出來的。其實(shí)我們下載文件,然后比對(duì)官方給的hash值,這里是假設(shè)官方的hash值是沒有被篡改的。
那么接下來的消息認(rèn)證碼MAC是可以解決這個(gè)問題。
4. 消息認(rèn)證碼(Message Authentication Code)
消息認(rèn)證碼(MAC)的作用就是在保障完整性的基礎(chǔ)上,同時(shí)提供認(rèn)證(認(rèn)證=消息是來自真正的發(fā)送者)的功能,用來解決上述密碼散列函數(shù)遺留的問題。可以簡單的這樣理解,MAC是在密碼散列函數(shù)+共享密鑰后算出的hash值,由于密鑰是只有通信雙方才知道的,那么就可以認(rèn)為通過MAC得到的hash可以保障信息的完整性以及同時(shí)提供認(rèn)證的能力。這里我們假設(shè)雙方不存在密鑰配送的問題(即雙方已經(jīng)持有相同的密鑰,至于是通過什么方式傳遞的,這里先不關(guān)心)。
使用密碼散列函數(shù)可以實(shí)現(xiàn)MAC,這種方式稱為HMAC(Hash Message Authentication Code):https://tools.ietf.org/html/rfc2104?和?https://en.wikipedia.org/wiki/Hash-based_message_authentication_code。計(jì)算公式可以簡單的理解為:mac = mac_function (message,key)。C#中使用HMAC的代碼如下:
/// <summary>/// HMACSHA1算法
/// </summary>
/// <returns>160 bits,20 byte array</returns>
public static byte[] ToHMACSHA1(this byte[] value,byte[] key) { ?
?if (value == null || value.Length == 0){ ? ? ? ?throw new ArgumentNullException(nameof(value));} ? ?if (key == null || key.Length == 0){ ? ? ? ?throw new ArgumentNullException(nameof(key));} ? ?using (var macAlgorithm =new HMACSHA1()){macAlgorithm.Key = key; ? ?
? ?return macAlgorithm.ComputeHash(value);} }
static void Main() { ? ?var value = "lnh".ToBytes(); ?
?var key = "123".ToBytes(); ?
?var mac = value.ToHMACSHA1(key);Console.WriteLine(); }
.Net類庫中開箱即用的MAC相關(guān)的類,開箱即用:
4.1 消息認(rèn)證碼的實(shí)際應(yīng)用
SWIFT:此SWIFT非蘋果的Swift語言,而是Society for Worldwide Interbank Financial(環(huán)球銀行金融電信協(xié)會(huì))的簡寫。在銀行之間進(jìn)行傳遞交易消息時(shí),會(huì)用到MAC來確認(rèn)消息的完整性以及對(duì)消息進(jìn)行認(rèn)證。在沒有使用公鑰密碼進(jìn)行密鑰交換之前,消息認(rèn)證碼使用的共享密鑰時(shí)靠人力通過11路來完成的。
IPsec:增強(qiáng)版Ip協(xié)議,用來認(rèn)證和校驗(yàn)消息的完整性。
SSL/TLS:后續(xù)會(huì)介紹到。
4.2 針對(duì)消息認(rèn)證碼的攻擊
重放攻擊:比如你給我轉(zhuǎn)賬100元,攜帶了mac的消息,其實(shí)我并不用破解你的消息和mac,原封不動(dòng)拿你的消息重復(fù)給我轉(zhuǎn)賬就是了,你信不信我可以把你賬戶里面所有的錢都變成我的...解決辦法是對(duì)消息添加編號(hào)和時(shí)間戳,使得消息接收方針對(duì)這個(gè)消息只處理一次。
密鑰推測(cè)攻擊:和密碼散列碼的暴力攻擊是類似的,不再細(xì)說。
4.3 遺留問題
消息不是我發(fā)送的,是你自己偽造的:基于MAC的原理是在于通信雙方共享密鑰,那么消息接收方可以判斷消息是來自真正的發(fā)送者,但是卻無法向第三者證明這一點(diǎn),為什么呢?因?yàn)橄⒌慕邮辗揭灿忻荑€啊,消息發(fā)送者完全可以說這是消息接收者自己用這個(gè)共享密鑰生成的消息,畢竟密鑰雙方都有。
那么接下來的數(shù)字簽名是可以解決這個(gè)問題。
5. 數(shù)字簽名(Digital Signature)
?上面的MAC可以保障信息的完整性,同時(shí)具有提供消息認(rèn)證的能力,但是又遺留了一個(gè)可以否認(rèn)消息是我發(fā)送的問題。究其原因在于通信雙方使用了同一個(gè)密鑰來生成MAC,你說是他生成的,他說是你生成的。那么怎么解決呢,其實(shí)也簡單,雙方使用不同的密鑰;消息發(fā)送方使用簽名密鑰生成一個(gè)“簽名”(就像簽字畫押按手印一樣的道理,表示我承認(rèn)這些信息是我發(fā)送的),消息接收方使用另外驗(yàn)證密鑰來驗(yàn)證這個(gè)簽名,這其實(shí)就是數(shù)字簽名。
數(shù)字簽名對(duì)簽名密鑰和驗(yàn)證密鑰進(jìn)行了區(qū)分,驗(yàn)證密鑰無法生成簽名;此外簽名密鑰只能由簽名人持有,而驗(yàn)證密鑰則可以由任何想要驗(yàn)證簽名的人持有。回想一下,這個(gè)簽名密鑰和驗(yàn)證密鑰是不是感覺似曾相識(shí),對(duì)了,和上面我們提到的公鑰密碼中的公鑰和私鑰非常類似吧。
公鑰密碼:密鑰分為加密密鑰和解密密鑰,用加密密鑰無法進(jìn)行解密;解密密鑰只有需要解密的人持有,而加密密鑰則是任何需要加密的人都可以持有。
實(shí)際上,數(shù)字簽名和公鑰密鑰有著非常緊密的聯(lián)系,簡單點(diǎn)來說,數(shù)字簽名是通過把公鑰密碼“反過來用”來實(shí)現(xiàn)的:
| 私鑰 / 簽名密鑰 | 公鑰 / 驗(yàn)證密鑰 | |
| 公鑰密碼 | 接收者解密時(shí)使用 | 發(fā)送者加密時(shí)使用 |
| 數(shù)字簽名 | 簽名者生成簽名時(shí)使用 | 驗(yàn)證者驗(yàn)證簽名時(shí)使用 |
| 誰持有密鑰 | 個(gè)人持有 | 只要需要,任何人都可以持有 |
數(shù)字簽名的實(shí)現(xiàn)是:簽名人用私鑰加密{一段信息}來生成簽名,驗(yàn)證者使用公鑰來解密這個(gè)簽名,如果可以解密成功,則說明驗(yàn)證成功。覺得很奇怪是不是?為什么能用公鑰解密就證明簽名驗(yàn)證通過了呢?其實(shí)這是由于私鑰和密鑰是成對(duì)出現(xiàn)的(具有嚴(yán)密的數(shù)學(xué)關(guān)系),只有公鑰才能解密與之配對(duì)的私鑰加密的信息,那么既然能夠解密,那么這個(gè)消息肯定是持有私鑰的這一方生成的。你估計(jì)還會(huì)想到一個(gè)問題,公鑰是公開的呀,你有我由他也有,那么私鑰生成的這個(gè)加密的簽名大家都可以解密,根本沒有機(jī)密性啊。是的,這樣理解是完全正確的,私鑰加密的信息是不具備機(jī)密性的;這是因?yàn)?strong style="font-size: 15px;">數(shù)字簽名是用來提供消息的不可否認(rèn)性的,它并不關(guān)心機(jī)密性的問題。
上面我們說到“簽名人用私鑰加密{一段信息}來生成簽名”。那么問題來了,這{一段信息}是什么信息?關(guān)于這一段信息我們由兩種選擇:1是消息本身,2是消息的hash。
下圖是對(duì)消息本身進(jìn)行簽名的過程:
下圖是對(duì)消息的hash進(jìn)行簽名的過程:
實(shí)際中我們一般采用的是對(duì)消息的hash進(jìn)行簽名的方式,因?yàn)橄⒈旧砜赡芊浅4?#xff0c;加密解密過程會(huì)非常消耗資源。再C#中使用RSA來實(shí)現(xiàn)數(shù)字簽名:
/// <summary>/// 數(shù)字簽名
/// </summary>
/// <returns></returns>
public static byte[] DigitalSignature(this byte[] value, string privateKey) { ?
??using (var asymmetricAlgorithm = new RSACryptoServiceProvider()){asymmetricAlgorithm.FromXmlString(privateKey); ?
?? ? ?return asymmetricAlgorithm.SignData(value, SHA1.Create());} }
/// <summary>
/// 數(shù)字簽名驗(yàn)證
/// </summary>
/// <returns></returns>
public static bool DigitalSignatureVerify(this byte[] value, string publicKey,byte[] digitalSignature) { ? ?using (var asymmetricAlgorithm = new RSACryptoServiceProvider()){asymmetricAlgorithm.FromXmlString(publicKey); ? ? ? ?return asymmetricAlgorithm.VerifyData(value, SHA1.Create(), digitalSignature);} }
static void Main() { ? ?string privateKey; ? ?string publicKey; ?
?using (var asymmetricAlgorithm = RSA.Create()){privateKey = asymmetricAlgorithm.ToXmlString(true);publicKey = asymmetricAlgorithm.ToXmlString(false);} ? ?var value = "lnh".ToBytes(Encoding.UTF8); ?
?//用私鑰生成數(shù)字簽名var digitalSignature = value.DigitalSignature(privateKey); ?
??//用公鑰驗(yàn)證數(shù)字簽名var verified = value.DigitalSignatureVerify(publicKey, digitalSignature);Console.WriteLine(); }
數(shù)字簽名本身的實(shí)現(xiàn)是使用了公鑰密鑰相關(guān)的算法。
6.1 數(shù)字簽名的實(shí)際應(yīng)用
公鑰證書:上面在介紹公鑰密碼的時(shí)候,遺留的一個(gè)公鑰認(rèn)證的問題,即我們?cè)趺床拍苤雷约耗玫降墓€是不是真正的公鑰,而不是被第三方偽造的呢?可以把公鑰當(dāng)作消息,然后施加數(shù)字簽名,所得到的就是公鑰證書,關(guān)于證書的知識(shí)后續(xù)博客會(huì)介紹。
SSL/TLS:SSL/TLS在認(rèn)證服務(wù)器是否合法的時(shí)候會(huì)使用服務(wù)器證書,就是上面提到的公鑰證書;于此相對(duì),服務(wù)器在對(duì)客戶端進(jìn)行認(rèn)證的時(shí)候,也會(huì)使用客戶端證書。關(guān)于SSL/TLS后續(xù)博客會(huì)介紹。
6.2 針對(duì)數(shù)字簽名的攻擊
中間人攻擊:在公鑰密碼這一小節(jié)中提到了中間人攻擊,因?yàn)閿?shù)字簽名其實(shí)就是使用它來實(shí)現(xiàn)的,那么對(duì)于數(shù)字簽名來說,中間人攻擊也是具有相同的威脅。
對(duì)密碼散列函數(shù)的攻擊:數(shù)字簽名使用了密碼散列函數(shù),那么數(shù)字簽名也面臨同樣的威脅。
利用數(shù)字簽名攻擊公鑰密鑰:這塊好復(fù)雜,筆者研究明白再補(bǔ)充( ╯□╰ )。。。
6.3 遺留問題
數(shù)字簽名可以識(shí)別出篡改和偽裝,還可以防止否認(rèn),也就是說數(shù)字簽名可以提供信息安全中的完整性、認(rèn)證和不可否認(rèn)性這3點(diǎn)的保障(很強(qiáng)大有木有)。然而這一切都基于一個(gè)假設(shè)“公鑰必須是真正的發(fā)送者提供的”,和公鑰密鑰陷入了同一個(gè)問題。我們發(fā)現(xiàn)自己陷入了一個(gè)死循環(huán):數(shù)字簽名可以用來識(shí)別篡改、偽裝以及否認(rèn)的,但是為此我們又需要從一個(gè)沒有被偽裝的真正的發(fā)送者那里得到一個(gè)沒有被篡改的密鑰......這是一個(gè)雞生蛋蛋生雞的問題。
細(xì)心的讀者或許可以看出來,上面我們的加密、散列、mac,簽名也好,消費(fèi)的數(shù)據(jù)都是byte[],而byte[]是不方便書寫、打印、復(fù)制和粘貼的,下面看一看byte[]編碼的問題。換換腦子,雞生蛋還是蛋生雞的問題放一放先。
7. 編碼(Encoding)
我們知道計(jì)算機(jī)的任何數(shù)據(jù)底層都是由0和1這樣的二進(jìn)制表示的,不管你是文本,圖片,音頻或者視頻還是exe等等,都是01這樣的二進(jìn)制。比如我們上面列舉的各種算法,其實(shí)它們都是以byte(=8bit)作為輸入的,輸出也是如此。很多場(chǎng)景下,傳輸?shù)臄?shù)據(jù)被限制在ASCII碼表(https://tools.ietf.org/html/rfc20)以內(nèi),比如url中的字符是ASCII中很小的一部分。在https://tools.ietf.org/html/rfc4648中定義了base16,base32,base64這幾種編碼方式,最常用的方式由16進(jìn)制(也叫base16)和base64編碼。
7.1 16進(jìn)制(base16)
16進(jìn)制的核心在于把一個(gè)byte(=8bit)分割成兩組4個(gè)bit。那么這四個(gè)bit組合起來最小的數(shù)字是0(2?),最大是16(2?)。編碼后每一組(4個(gè)bit)都轉(zhuǎn)成十進(jìn)制,對(duì)應(yīng)一個(gè)字母(使用1個(gè)byte表示),也就是相當(dāng)于對(duì)原始的數(shù)據(jù)放大了2倍,其字母表如下:
舉個(gè)簡單的例子如下,比如“李”這個(gè)原始字符,我把它先用UTF8取得byte數(shù)組,然后把byte數(shù)組轉(zhuǎn)成16進(jìn)制:
1 public static string ToHexString(this byte[] value) 2 { 3 return BitConverter.ToString(value).Replace("-",""); 4 } 5 6 public static byte[] ToBytes(this string value, Encoding encoding = null) 7 { 8 if (value==null) 9 ? ?{10 throw new ArgumentNullException(nameof(value));11 ? ?}12 if (encoding==null)13 ? ?{14 encoding = Encoding.UTF8;15 ? ?}16 return encoding.GetBytes(value);17 }18 19 static void Main()20 {21 var hex = "李".ToBytes().ToHexString();22 //hex=E69D8E23 ? ?Console.WriteLine(hex);24 }具體的編碼流程:
| 原始數(shù)據(jù) | 李 | |||||||||||||||||||||||
| (1).轉(zhuǎn)成byte數(shù)組(UTF8) | 230 | 157 | 142 | |||||||||||||||||||||
| (2).二進(jìn)制形式 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
| (3).按照4bit一組分割 | 1110 | 0110 | 1001 | 1101 | 1000 | 1110 | ||||||||||||||||||
| (4).對(duì)應(yīng)的10進(jìn)制數(shù)值 | 14 | 6 | 9 | 13 | 8 | 14 | ||||||||||||||||||
| (5).對(duì)應(yīng)的16進(jìn)制字母 | E | 6 | 9 | D | 8 | E | ||||||||||||||||||
| (6).16進(jìn)制編碼 | E69D8E | |||||||||||||||||||||||
?其中核心步驟在(2)-(5),下面詳細(xì)解釋以下:
(1)把原始數(shù)據(jù)轉(zhuǎn)成byte數(shù)組,我這里用的是UTF8編碼,轉(zhuǎn)成了3個(gè)byte(如果你用GB2312,“李”這個(gè)字符對(duì)應(yīng)的就不是這三個(gè)byte了,而是另外的兩個(gè)byte:?192?238?);如果你的數(shù)據(jù)原本就一個(gè)音頻文件或者其他二進(jìn)制文件,那么你得到的就直接是一個(gè)byte的數(shù)組。這一步的目的在于準(zhǔn)備數(shù)據(jù)。還并未進(jìn)入到16進(jìn)制編碼的環(huán)節(jié)。
(2)把第一步得到的byte數(shù)組以二進(jìn)制形式展開。
(3)依次把(2)按照4個(gè)bit為一組進(jìn)行分割。
(4)把(3)的每一組都轉(zhuǎn)成10進(jìn)制的數(shù)值。
(5)根據(jù)(4)得到的數(shù)值查找16進(jìn)制的編碼表,得到對(duì)應(yīng)的字母。
(6)把(5)中得到的字母依次組合在一起,就是最終的結(jié)果。
7.2 base64編碼
base64也可以說是64進(jìn)制,它是用6個(gè)bit表示一個(gè)字符,也就是2?。其實(shí)核心原理和的16進(jìn)制是一模一樣的,但是有點(diǎn)不同的是,當(dāng)一組byte(8bit)拆成4bit一組的的時(shí)候,是永遠(yuǎn)都可以成對(duì)的拆分的(8÷4=2);但是當(dāng)一組byte想要拆成6bit一組的時(shí)候,可能就無法正好拆分了(8÷6=1.333333.....),只有數(shù)據(jù)的bit數(shù)是8和6的最小公約數(shù)24的整數(shù)倍的情況下,才可以正好拆分,簡化點(diǎn)就是byte數(shù)÷3。不能整除的時(shí)候,這個(gè)時(shí)候就需要補(bǔ)上一些0湊夠6位。
標(biāo)準(zhǔn)的base64的碼表是由:[A-Z]、[a-z]、[0-9]、“+”和“/”構(gòu)成的(26+26+10+2=64),再附加一個(gè)對(duì)齊用的“=”(個(gè)人理解這個(gè)“=”完全是多余,就像人的闌尾似的...),一共65個(gè)字符。
除了這個(gè)標(biāo)準(zhǔn)的碼表之外,還有一些其他的碼表,主要是因?yàn)椤?+=”這三個(gè)字符再一些特殊的場(chǎng)景下術(shù)語特殊字符,比如在url傳遞的時(shí)候,這三個(gè)字符都是特殊字符,需要替換掉,比如把“/+”換成“-_”這2個(gè)字符。還拿上面的“李”舉例子(這次我們用GB2312,故意使它無法整除:李字在GB2312中使用2個(gè)byte表示,不能整除3):
public static string ToBase64String(this byte[] value) { ? ?return Convert.ToBase64String(value); }static void Main() { ? ?var base64 = "李".ToBytes(Encoding.GetEncoding("GB2312")).ToBase64String(); ? ?//base64 wO4= ? ?Console.WriteLine(base64); }具體的編碼流程如下:
| 原始數(shù)據(jù) | 李 | |||||||||||||||||||||||
| (1).轉(zhuǎn)成byte數(shù)組(GB2312) | 192 | 238 | ||||||||||||||||||||||
| (2).二進(jìn)制形式 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 補(bǔ)0 | 補(bǔ)0 | ||||||
| (3).按照6bit一組分割 | 110000 | 001110 | 111000 | |||||||||||||||||||||
| (4).對(duì)應(yīng)的10進(jìn)制數(shù)值 | 48 | 14 | 56 | |||||||||||||||||||||
| (5).對(duì)應(yīng)的base64字母 | w | O | 4 | 補(bǔ)= | ||||||||||||||||||||
| (6).base64編碼 | wO4= | |||||||||||||||||||||||
?
?這個(gè)過程就不再詳細(xì)解釋了,和上面的16進(jìn)制是一樣的,不同之處在于對(duì)齊補(bǔ)上“=”。
8. 總結(jié)一下
以上簡單的介紹了一下信息安全方面的一些特征,以及又哪些工具可以提供這些特征的保障(均是筆者從資料中翻出來自己解讀了以下,如有錯(cuò)誤之處,歡迎指正!)。具體每個(gè)工具只是從使用者的角度簡單的介紹了一下,對(duì)實(shí)現(xiàn)細(xì)節(jié)并未深入研究,感興趣的需要自己單獨(dú)再去深入了解了,這篇博客主要是個(gè)入門的科普介紹而已,主要參考資料是《圖解密碼技術(shù)》,以及維基百科的一些解釋(為啥給的是英文鏈接,其實(shí)我發(fā)現(xiàn)中文的維基百科的信息比英文的少的不是一點(diǎn)半點(diǎn)的)。還有一本不錯(cuò)的圖書《編碼 - 隱匿在計(jì)算機(jī)軟硬件背后的語言》,個(gè)人認(rèn)為是非常棒的一本計(jì)算機(jī)科學(xué)的科普讀物。最后還留了一個(gè)雞生蛋蛋生雞的問題和公鑰密碼的認(rèn)證問題,且聽下回分解。
9. 參考 & 引用
代碼:https://gist.github.com/linianhui/447c23fd3f3834b6533ecd93424a3906
書籍:
圖解密碼技術(shù):https://book.douban.com/subject/26822106/
編碼 - 隱匿在計(jì)算機(jī)軟硬件背后的語言 :https://book.douban.com/subject/4822685/
鏈接:
對(duì)稱密鑰 - DES:https://en.wikipedia.org/wiki/Data_Encryption_Standard
對(duì)稱密鑰 - AES:https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
公鑰密鑰 - 非對(duì)稱密鑰 - Public-key cryptography:https://en.wikipedia.org/wiki/Public-key_cryptography
公鑰密鑰 - 非對(duì)稱密鑰 - RSA:https://en.wikipedia.org/wiki/RSA
密碼散列函數(shù):https://en.wikipedia.org/wiki/Cryptographic_hash_function
密碼散列函數(shù) - MD5:https://en.wikipedia.org/wiki/MD5
密碼散列函數(shù) - SHA-1:https://en.wikipedia.org/wiki/SHA-1
密碼散列函數(shù) - SHA-2:https://en.wikipedia.org/wiki/SHA-2
密碼散列函數(shù) - SHA-3:https://en.wikipedia.org/wiki/SHA-3
消息認(rèn)證碼 - HMAC: Keyed-Hashing for Message Authentication:https://tools.ietf.org/html/rfc2104
消息認(rèn)證碼 - Message Authentication Code:https://en.wikipedia.org/wiki/Message_authentication_code
消息認(rèn)證碼 - Hash-based message authentication code :https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
數(shù)字簽名 -?https://en.wikipedia.org/wiki/Digital_signature
編碼 - ASCII format for Network Interchange:https://tools.ietf.org/html/rfc20
編碼 - The Base16, Base32, and Base64 Data Encodings:https://tools.ietf.org/html/rfc4648
?
原文地址:http://www.cnblogs.com/linianhui/p/security-based-toolbox.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的[信息安全] 1.密码工具箱的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于.NET CORE微服务框架 -谈谈
- 下一篇: 在Docker中运行asp.net co