對於信息安全性的重要性,我想你們都不會否定。那麼具體來講應該具備哪些特性才能稱之爲安全呢?舉個簡單的例子:我給你發送一條消息「借給我100元」,當你收到這條消息而且處理後你的帳戶裏面會少出來100塊,個人帳戶會多出來100塊。在這個過程當中,你是消息接收方,我是消息發送方。html
1.做爲通訊雙方的你我都不但願讓其餘人能讀懂這條消息,這是信息的機密性,即消息在傳遞過程當中不被其餘人解讀。git
2.做爲通訊雙方的你我都不但願消息內容變成"借老子1000塊!"(操,借錢還這麼牛逼,100塊都不給你,還要1000塊!死去...),這是信息的完整性,便可以校驗出信息在傳送過程當中是否被篡改。算法
3.做爲消息接收方的你須要確認是否是真正的我給你發的借錢的消息吧,會不會是個詐騙犯要騙我100塊!這是對信息的認證,即接收者要能夠驗證消息的發送者確實是本身但願的發送者。安全
4.做爲消息接收方的你確定不但願在借給了我100塊以後,我耍無賴失口否定說沒借過你錢,這是信息的不能否認性,即消息發送者不能夠否定說這個信息不是我發送的。服務器
總結來講,在通訊過程當中,知足這4個特徵:機密性,完整性,認證,不能否認性,就能夠認爲信息是安全的。那麼接下來的幾個小節來介紹一下有那些工具可使得咱們在傳遞消息的時候具備以上4個特徵。session
英文:Symmetric Cryptographydom
對稱密碼加密能夠保障信息的機密性。舉一個簡單的例子,一把鎖,兩把相同的鑰匙,就是對稱密碼;即:使用相同的密鑰來加密和解密。沒有密鑰的其餘人是沒法解讀信息的真正內容是什麼的。常見的兩個對稱加密標準有DES
和AES
。ide
DES
是一種對稱密鑰加密算法,在1976年被美國聯邦政府的國家標準局肯定爲聯邦資料處理標準,隨後在國際上普遍流傳開來。它基於使用56位密鑰的對稱算法。如今如今已經不是一種安全的加密方法,主要由於它使用的56位密鑰太短。後來又發展出了3DES(即執行三次DES加密)。因爲DES已經再也不安全,後來又推出了新的對稱加密標準AES
,採用的算法爲Rijndael
。算法的具體實現邏輯這裏不去解釋,這裏關注的是如何利用它們(即,保障信息的機密性的手段)。看一下簡單的加密解密的數學公式:函數
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 參數檢查
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 參數檢查
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);
//構造128bit的key,guid正好是128,權且看成key了。
var key = Guid.NewGuid().ToByteArray();
var encryptedData = value.AESEncrypt(key);
var decryptedData = encryptedData.AESDecrypt(key);
var decryptedDataString = Encoding.UTF8.GetString(decryptedData);
Console.WriteLine();
}
複製代碼
密鑰配送問題:共享的密鑰如何交到接受消息方的手上呢?雙方能夠事先共同約定一個密鑰,可是這種辦法是沒法知足互聯網規模的須要的,互聯網規模的環境是假設通訊雙方事先並不知道對方的存在,怎麼事先約定呢,行不通吧。
下面接下來的公鑰密鑰能夠解決這個問題。
英文:Asymmetric Cryptography
對稱密碼加密能夠解決信息的機密性的問題,可是卻沒法提供雙方如何才能獲得加密所用密鑰的途徑。咱們回到最初的目的想想,咱們想要的機密性的核心在於別人沒法取得信息的真實內容,也就是解密;而如何生成這個機密的信息,其實並非咱們關注的點,你能生成,他能生成,都沒區別,只要我控制住只有我才能解密,那麼機密性的問題就解決了。因此解決密鑰配送的問題的關鍵就在於,把密鑰分紅兩部分,一個加密用,一個解密用,它們老是成對出現的。配送的是加密用的密鑰(也叫公鑰),解密用的叫私鑰,這個只有我本身知道,不會在任何地方傳輸,那麼也就不存在配送的問題了。
其實不少計算機中的問題都是無解的,每每卻又是有解決辦法的,它的解決辦法其實並非直接的解決這個問題,而是規避掉這個問題,使得它不在是一個問題的。好比密鑰配送的問題,若是說咱們有安全的方式解決密鑰配送的問題,直接使用這個安全的方式配送咱們想要傳遞的信息不就是了,咱們還繞個彎配送密鑰幹什麼呢。公鑰密碼其實並未解決密鑰配送的問題,而是使得它再也不是個問題,即:公鑰能夠公開給任何人,再也不須要保密(本質上來講,密鑰和待加密的信息一樣重要),而是經過控制解密來達到咱們想要的機密性,繞過了如何機密的配送密鑰的問題。
公鑰密碼就是這麼一個簡單的原理:公鑰(=public key)加密,私鑰(=private key)解密,它能夠保障信息的機密性,同時解決密鑰的配送問題。那麼這個時候通訊雙方的流程就是這樣的:
public key
;public key
加密消息, public key
和加密的消息泄露都不要緊,由於只有 private key
才能解密;private key
解密消息。至於如何產生出來這樣一對 public key
和 private key
以及相對於的加密解密算法,這其中涉及到很複雜的數學問題,這裏就不展開介紹了(筆者也不懂...)。咱們看一下最普遍使用的公鑰密碼算法RSA
在C#裏面怎麼使用吧:
/// <summary>
/// RSA加密
/// </summary>
/// <param name="publicKey">公鑰</param>
/// <returns></returns>
public static byte[] RSAEncrypt(this byte[] value, string publicKey) {
//todo 參數檢查
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 參數檢查
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庫中已經提供了公鑰密碼相關的類,開箱即用:
中間人攻擊:這鐘類型的攻擊發生在上述流程中的第一步,即發送方A向接收方B請求 public key 的時候。這時有一個攔路打劫的傢伙M,截獲了這個 public key ,本身據爲己有。而後M把本身的一個 public key 給到了A,A是渾然不覺,傻乎乎的用這個假的 public key 加密了信息,發送了出去,這時候M攔截到了這個消息,用本身的 private key 解密了這個消息,而後篡改一番,用真正的 public key 進行加密,發給了B。這個時候B覺得是A發送的,A也覺得本身發給了B,其實都被M給玩了...文字可能不是很清晰,看圖:
公鑰的認證問題:公鑰密鑰能夠解決規避掉的配送問題,可是新問題又來了,**這個公鑰真的是你的嗎?**針對上述的中間人攻擊,其實咱們發現,獲取公鑰的這一方並不能確認本身收到的公鑰就是本身真正請求的那一方提供的。這個問題先放一放(後續會介紹),下面先看看保障信息的完整性方面有那些工具可用。
英文:Cryptographic hash function
密碼散列函數能夠保障的信息完整性,用來校驗要傳遞的信息是否被篡改過。好比一般在下載文件的時候,官方的網站上都會列出來其MD5
或者SHA1
的值來校驗。它的工做原理和要求大體以下:
輸入一組數據message
,而後獲得一組簡短的數據hash
,只要是採用相同的算法,輸入message
就能獲得hash:hash = hash_function (message)
; 其過程是不可逆的,你不能由hash
得出message
; 知足message
的微小變化會(好比只改動1字節)會使得hash
產生巨大的變化(就比如兩個雙胞胎,各處都很像,可是他們的指紋卻不是相同的); 兩組不一樣的消息message1
和message2
,不能得出相同的hash
(理論上能夠,只是要儘量的使這個過程困難)。 經常使用的密碼散列函數(算法)有Message Digest Algorithm
以及Secure Hash Algorithm
。
英文:Message Digest Algorithm 5
中文明爲消息摘要算法第五版,這也說明其實它也有前面幾個版本,好比MD4(這裏就不介紹了)。MD5算法是輸入任意長度的數據(Message),而後算出固定長度的數據 16byte=128bits
,用16進製表示這16個byte就是32位。C#使用MD5的代碼以下:
/// <summary>
/// MD5摘要算法
/// </summary>
/// <param name="value"></param>
/// <returns>128 bits,16 byte array</returns>
public static byte[] ToMD5(this byte[] value) {
if (value == null || value.Length == 0)
{
throw new ArgumentNullException(nameof(value));
}
using (var hashAlgorithm = MD5.Create())
{
return hashAlgorithm.ComputeHash(value);
}
}
複製代碼
再一次指出,md5的結果是固定的16byte=128bits
,用16進製表示是32個字符。網上由不少的16進制16個字符的md5,其實這都不是完整的md5,只是截取了32位中的16位而已。
英文:Secure Hash Algorithm
從使用者的角度來看,MD5
和SHA
沒有什麼本質區別,差別在於其算法的實現方式,生成的hash的長度,其抗攻擊破解的難度不同。此外因爲SHA
的強度比MD5
要大,因此在計算SHA
的時候,所消耗的資源(時間,空間都有)也會比MD5
要多。即便如此,如今MD5(128bit)和SHA-1(160bit)均已遭到了破解:news.cnblogs.com/n/563589/。S…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的庫已經幫咱們封裝好了密碼散列函數相關的類,開箱即用。
強碰撞性攻擊:好比上面提到的Google
破解了SHA-1,即便用大量的計算來找出兩個數據message不同,可是hash值卻同樣的過程,若是找到了這樣的兩塊數據,那麼再使用這個hash做爲數據的完整性校驗的手段,其實已經沒有意義了。解決辦法是升級SHA-2,增大計算出這樣兩塊數據的難度。
暴力破解:好比網上不少的MD5的解密,其實原理在於他們有大量的MD5 hash庫,好比 123456 的MD5是 e10adc3949ba59abbe56e057f20f883e ,那麼在你給我一個 e10adc3949ba59abbe56e057f20f883e 的時候,我就知道你的原文是 123456 。解決辦法是加鹽,增大這種暴力比對的難度。
針對上面兩種攻擊方式都是在於增長破解難度,使其在現有的計算能力下不能輕易的被攻破,沒有絕對的安全,只是相對上來講是安全的,當破解你帶來的收益要低於其破解成本的時候,你纔是安全的
hash被篡改了:好比上面下載文件的時候官方會給出MD5或者SHA1的hash值,這裏咱們假設一下,官方提供hash值的渠道被黑掉了,給了你一個篡改過的hash值,而後你下載了一個被篡改過的文件,你是分辨不出來的。其實咱們下載文件,而後比對官方給的hash值,這裏是假設官方的hash值是沒有被篡改的。
那麼接下來的消息認證碼MAC是能夠解決這個問題。
英文:Message Authentication Code
消息認證碼(MAC)
的做用就是在保障完整性的基礎上,同時提供認證(認證=消息是來自真正的發送者)的功能,用來解決上述密碼散列函數遺留的問題。能夠簡單的這樣理解,MAC
是在密碼散列函數+共享密鑰後算出的hash值,因爲密鑰是隻有通訊雙方纔知道的,那麼就能夠認爲經過MAC獲得的hash能夠保障信息的完整性以及同時提供認證的能力。這裏咱們假設雙方不存在密鑰配送的問題(即雙方已經持有相同的密鑰,至因而經過什麼方式傳遞的,這裏先不關心)。
使用密碼散列函數能夠實現MAC,這種方式稱爲HMAC(Hash Message Authentication Code):tools.ietf.org/html/rfc210… 和 en.wikipedia.org/wiki/Hash-b… = 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相關的類,開箱即用:
SWIFT
非蘋果的Swift
語言,而是Society for Worldwide Interbank Financial
(環球銀行金融電信協會)的簡寫。在銀行之間進行傳遞交易消息時,會用到MAC來確認消息的完整性以及對消息進行認證。在沒有使用公鑰密碼進行密鑰交換以前,消息認證碼使用的共享密鑰時靠人力經過11路來完成的。消息不是我發送的,是你本身僞造的:基於MAC的原理是在於通訊雙方共享密鑰,那麼消息接收方能夠判斷消息是來自真正的發送者,可是卻沒法向第三者證實這一點,爲何呢?由於消息的接收方也有密鑰啊,消息發送者徹底能夠說這是消息接收者本身用這個共享密鑰生成的消息,畢竟密鑰雙方都有。
那麼接下來的數字簽名是能夠解決這個問題。
上面的MAC能夠保障信息的完整性,同時具備提供消息認證的能力,可是又遺留了一個能夠否定消息是我發送的問題。究其緣由在於通訊雙方使用了同一個密鑰來生成MAC,你說是他生成的,他說是你生成的。那麼怎麼解決呢,其實也簡單,雙方使用不一樣的密鑰;消息發送方使用簽名密鑰生成一個「簽名」(就像簽字畫押按手印同樣的道理,表示我認可這些信息是我發送的),消息接收方使用另外驗證密鑰來驗證這個簽名,這其實就是數字簽名。
數字簽名對簽名密鑰和驗證密鑰進行了區分,驗證密鑰沒法生成簽名;此外簽名密鑰只能由簽名人持有,而驗證密鑰則能夠由任何想要驗證簽名的人持有。回想一下,這個簽名密鑰和驗證密鑰是否是感受似曾相識,對了,和上面咱們提到的公鑰密碼中的公鑰和私鑰很是相似吧
公鑰密碼:密鑰分爲加密密鑰和解密密鑰,用加密密鑰沒法進行解密;解密密鑰只有須要解密的人持有,而加密密鑰則是任何須要加密的人均可以持有。
實際上,數字簽名和公鑰密鑰有着很是緊密的聯繫,簡單點來講,數字簽名是經過把公鑰密碼「反過來用」來實現的:
私鑰 / 簽名密鑰 | 公鑰 / 驗證密鑰 | |
---|---|---|
公鑰密碼 | 接收者解密時使用 | 發送者加密時使用 |
數字簽名 | 簽名者生成簽名時使用 | 驗證者驗證簽名時使用 |
誰持有密鑰 | 我的持有 | 只要須要,任何人均可以持有 |
數字簽名的實現是:簽名人用私鑰加密一段信息來生成簽名,驗證者使用公鑰來解密這個簽名,若是能夠解密成功,則說明驗證成功。以爲很奇怪是否是?爲何能用公鑰解密就證實簽名驗證經過了呢?其實這是因爲私鑰和密鑰是成對出現的(具備嚴密的數學關係),只有公鑰才能解密與之配對的私鑰加密的信息,那麼既然可以解密,那麼這個消息確定是持有私鑰的這一方生成的。你估計還會想到一個問題,公鑰是公開的呀,你有我由他也有,那麼私鑰生成的這個加密的簽名你們均可以解密,根本沒有機密性啊。是的,這樣理解是徹底正確的,私鑰加密的信息是不具有機密性的;這是由於數字簽名是用來提供消息的不能否認性的,它並不關心機密性的問題。
上面咱們說到「簽名人用私鑰加密{一段信息}來生成簽名」。那麼問題來了,這{一段信息}是什麼信息?關於這一段信息咱們由兩種選擇:1是消息自己,2是消息的hash。
下圖是對消息自己進行簽名的過程:
實際中咱們通常採用的是對消息的hash
進行簽名的方式,由於消息自己可能很是大,加密解密過程會很是消耗資源。再C#中使用RSA來實現數字簽名:
/// <summary>
/// 數字簽名
/// </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>
/// 數字簽名驗證
/// </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);
//用私鑰生成數字簽名
var digitalSignature = value.DigitalSignature(privateKey);
//用公鑰驗證數字簽名
var verified = value.DigitalSignatureVerify(publicKey, digitalSignature);
Console.WriteLine();
}
複製代碼
數字簽名自己的實現是使用了公鑰密鑰相關的算法。
數字簽名能夠識別出篡改和假裝,還能夠防止否定,也就是說數字簽名能夠提供信息安全中的完整性、認證和不能否認性這3點的保障(很強大有木有)。然而這一切都基於一個假設「公鑰必須是真正的發送者提供的」,和公鑰密鑰陷入了同一個問題。咱們發現本身陷入了一個死循環:數字簽名能夠用來識別篡改、假裝以及否定的,可是爲此咱們又須要從一個沒有被假裝的真正的發送者那裏獲得一個沒有被篡改的密鑰......這是一個雞生蛋蛋生雞的問題。
細心的讀者或許能夠看出來,上面咱們的加密、散列、mac,簽名也好,消費的數據都是byte[],而byte[]是不方便書寫、打印、複製和粘貼的,下面看一看byte[]編碼的問題。換換腦子,雞生蛋仍是蛋生雞的問題放一放先。
英文:Pseudo-Random Number Generator
隨機數你們不陌生,可是隨機數怎麼就和信息安全扯上關係了呢?其實想想咱們在給本身的帳號設置密碼的時候,是否是都會盡可能的讓其餘人不會輕易的猜到咱們的密碼,雖然並非隨機,可是它就像是知足了隨機數的一個特徵:不可預測性。那麼對於信息安全來講來講,也是用到了這個特定,固然還有隨機數的隨機性,不可重複性這兩點特徵。
隨機性:徹底雜亂的序列,沒有統計學誤差; 不可預測性:不能由已經獲得的隨機數才猜想出下一個隨機數是什麼; 不可重複性:不能生成重複的隨機數。 根據生成的隨機數是否知足這3點要求(1<2<3
,依次加強)。大體能夠劃分僞弱僞隨機數,強僞隨機數,真隨機數(強度依次增大)。
隨機性 | 不可預測性 | 不可重複性 | ||
---|---|---|---|---|
弱僞隨機數 | √ | × | × | 只具有隨機性 |
強僞隨機數 | √ | √ | × | 同時具有不可預測性 |
真隨機數 | √ | √ | √ | 同時具有不可重複性 |
僅僅依靠軟件咱們是沒法生成真隨機數的,這裏咱們只關注如下僞隨機數(即強僞隨機數的生成,可用於密碼學安全)的生成,好比一個典型的生成器以下:
生成器本身維護一個內部狀態,同時接受一個隨機數的種子,來生成具體的隨機數。具體是實現方式有利用密碼散列函數(單向性支撐了不可預測性)、利用加密密鑰做爲隨機數的種子的一部分(密鑰的機密性支持了不可預測性)等等。
在C#可使用的僞隨機數生成方式:
//1. Random
var random = new Random();
var random1 = random.Next(100);
Console.WriteLine(random1);
//2. Guid
var random2 = Guid.NewGuid().ToString("N");
Console.WriteLine(random2);
//3. RNGCryptoServiceProvider
RandomNumberGenerator randomNumberGenerator = new RNGCryptoServiceProvider();
var random3Bytes = new byte[32];
randomNumberGenerator.GetBytes(random3Bytes);
var random3 = random3Bytes.ToHexString();
Console.WriteLine(random3);
複製代碼
通常狀況下,Guid便可知足要求(可是隻有固定的16byte),如需更高強度的僞隨機數,可使用 RNGCryptoServiceProvider
來生成任意長度的隨機數。
僞隨機數的程序結構能夠說很簡單,可是其中的每一個環節都有可能成爲被攻擊的突破口。
針對密碼相關的基本工具介紹就暫時能夠告一段落了,回顧總結如下有這6個**(對稱密碼、公鑰密鑰、密碼散列函數、消息認證碼、數字簽名、僞隨機數生成器)**基本工具,下面咱們用這6個基本工具來組合一些高級的工具出來。
上一篇中介紹到了對稱密碼(好比AES)和公鑰密碼(好比RSA),公鑰密碼解決了對稱密碼的密鑰配送問題(實際上是繞過了)。
那麼若是做爲接收方我想要回復發送方的消息怎麼辦?按照公鑰密碼的機制,我是不能用本身的私鑰加密信息發出去的,由於擁有持有個人公鑰的任何人都是能夠解密這個信息的。因此,若是僅使用公鑰密碼,那麼就須要通訊雙方都持有對方的公鑰+本身的私鑰。這個成本是很高昂的,首先公鑰加密解密的速度是會比對稱密碼加密低2~3個數量級,也就是幾百倍的差別;其次雙方都面臨着針對公鑰的認證問題(防止中間人攻擊)。
那麼咱們能夠結合以前提到的一些工具,組合一下,來獲得一個性價比高的加密通訊方式,即便用如下三個基本工具,組合一個高級點的工具(同時具有對稱密碼和公鑰密碼的優勢):
看一下混合密碼的加密過程:
上圖是用僞隨機數生成器生成一個加密用的會話密鑰,來加密明文;同時,把這個會話密鑰做爲公鑰密碼中的明文,用公鑰加密;而後把這兩個密文組合在一塊兒,同時發送給接收方。這裏的公鑰密鑰起到的是一個保證會話密鑰機密性的做用,並未直接用來加密真正的明文(又想起來一句話:計算機科學的中任何問題,均可以經過添加一箇中間層來解決,此言不虛;在另一個[認證受權]系列的博客中,筆者也有這樣的體會)。來看如下接收方解密的流程:
相對於加密過程來講,是徹底反過來的一個過程,就再也不解釋了。看一段C#中實際使用的代碼:
static void Main() {
string privateKey;
string publicKey;
using (var asymmetricAlgorithm = RSA.Create())
{
privateKey = asymmetricAlgorithm.ToXmlString(true);
publicKey = asymmetricAlgorithm.ToXmlString(false);
}
// 發送者加密
var hybridCiphertext = HybridEncrypt(publicKey, "lnh-明文");
Console.WriteLine(hybridCiphertext);
// 接收者解密
var plaintext = HybridDecrypt(privateKey, hybridCiphertext);
Console.WriteLine(plaintext);
Console.ReadKey();
}
static string HybridEncrypt(string publicKey, string plaintext) {
var plaintextBytes = plaintext.ToBytes(Encoding.UTF8);
//1. 生成僞隨機數,做爲會話簽名
var sessionKey = SecurityHelper.BuildPseudoRandomNumber(16);
//2. 使用sessionKey做爲AES的密鑰進行加密
var ciphertextBytes = plaintextBytes.AESEncrypt(sessionKey);
//3. 使用公鑰對會話密鑰進行加密
var sessionkeyCiphertextBytes = sessionKey.RSAEncrypt(publicKey);
//4. 模擬合成的消息
var hybridCiphertext = ciphertextBytes.ToHex() + "." + sessionkeyCiphertextBytes.ToHex();
return hybridCiphertext;
}
static string HybridDecrypt(string privateKey, string hybridCiphertext) {
//1. 分離合成的密文
var ciphertext = hybridCiphertext.Split('.')[0];
var sessionkeyCiphertext = hybridCiphertext.Split('.')[1];
var ciphertextBytes = ciphertext.HexToBytes();
var sessionkeyCiphertextBytes = sessionkeyCiphertext.HexToBytes();
//2. 用私鑰解密獲得會話密鑰
var sessionkey = sessionkeyCiphertextBytes.RSADecrypt(privateKey);
//3. 用會話密碼解密
var plaintextBytes = ciphertextBytes.AESDecrypt(sessionkey);
return plaintextBytes.GetString(Encoding.UTF8);
}
複製代碼
混合密碼系統只能說是下降了單純的公鑰密碼帶來的成本問題,而公鑰密碼遺留的公鑰認證問題,在混合密碼系統中依然存在。同時使用了僞隨機數生成器,混合密碼系統也會面臨針對僞隨機生成器的一些攻擊。
數字簽名遺留下問題,彙總在一塊兒的核心就是驗證公鑰必須是真正的發送者提供的
因此,想要解決這個問題單靠純粹的技術手段是行不通了,咱們陷入了一個死循環,所以引入了一個社會學中的信任體系來轉移咱們所面臨的問題,即證書以及相關體系結構,提供逐級的信任保障。咱們先看看證書是一個什麼東西,以及證書的這套相關體系如何提供這種「信任」保障的。
咱們從一出生就會和各類各樣的證書打交道,好比出生證,學生證,身份證,駕照,學位證等等,它們都有一個共同點,就是有你本人的真實信息以及開具證實的機構的蓋章。那麼在須要提供證實你就是你的地方,出具這個證書便可,若是對方不信任你的證書,則能夠到開具證書的機構來校驗。假如你提供一個假證,而對方沒有嚴格的審查的話,或許你是能夠矇混過關的。
計算機領域的證書和現實社會中的各類證書的工做原理是徹底同樣的,由於其工做在計算機體系中,也被稱爲「數字證書」。計算機中數字證書是這樣定義的:由證書受權中心進行數字簽名的,包含公鑰以及其擁有者信息的一個文件。注意:證書的真正用途在於爲公鑰提供認證功能,因此有時候也叫作公鑰證書。咱們使用這個被稱作證書的文件來轉移咱們在信息安全層面所面臨的死循環的問題,爲何說是轉移而不是解決呢,這是由於你拿到一個證書後,也須要進行校驗吧,而校驗又須要一個真正的發送者提供的公鑰才行,那麼你就須要另一個證書來保障,而後你就會一直的循環下去,,,這也是爲何在計算機體系中有根證書的存在,以及相關的證書受權認證中心會是一個層級的關係,這就是爲了在你不信任一個證書的時候,能夠繼續往上一個層級來尋求驗證,直到根證書。那麼問題就來了,假如你也不相信根證書怎麼辦?這實際上是一個沒法回答的問題,筆者想起來以前讀《人類簡史》的時候,有一個至今烙印在腦海中的觀點:「現在的社會,是一個由想象所構建的秩序」。其實想想,也確實是如此。好比你爲什麼相信國家的存在呢,爲什麼會把錢存進銀行呢。你拿出來一張毛爺爺(從物理的角度來看,它就是一張紙而已)爲何就能從飯店買來一堆食物,這其實就是你相信它,對方也相信它,全部人都相信它,背後有銀行體系爲其擔保,那麼什麼爲銀行提供擔保呢,背後有咱們的國家提供保障,這就是一個信任的體系。計算機體系的數字證書也是基於這麼一個共同的想象所構建的信任秩序。補充一個新聞:Google 宣佈將徹底取消對沃通和 StartCom 全部證書的信任(news.cnblogs.com/n/573409/),…
英文全稱:Public Key Infrastructure
證書得以運行的這個基本的體系稱爲PKI(Public Key Infrastructure),即公鑰基礎設施,它是一套以公鑰密碼爲核心的技術規範標準的一個代名詞。整體來講,PKI由3個要素組成:PKI的消費者;認證機構(Certificate Authority,簡稱CA);證書倉庫。咱們常說的CA證書,就是由CA機構簽名頒發的證書。CA負責生成密鑰對(也可由用戶提供)、負責對用戶身份進行認證、負責生成並頒發證書、負責做廢證書(en.wikipedia.org/wiki/Certif… CA的證書是自簽名的(也就是本身證實本身),其下級機構逐層簽名,構成一個金字塔似的結構。固然你平時本身也能夠生成自簽名的證書,可是除了你本身,其餘地方是不承認你這個證書的(就比如你拿一張白紙,寫上這是100塊,而後別人就相信你它值100塊嗎?),想要得以正常運行,是須要用戶主動確認表示承認你這個證書才行。好比咱們用Fiddler抓取HTTPS的內容的時候,其實Fiddler本身生成了一個自簽名的根證書,而後你主動的確認信賴它,只有這樣,證書構造的這個驗證鏈才能得以正常運行。想起來12306就本身搞了一個自簽名的證書,想必你們都有印象吧,,,須要本身下載下來證書,而後導入到計算機中,再確認信任它;其實這也是一個很尷尬的事情,全球最大的幾家CA清一色不是美國就是俄羅斯。
好了,分析完數字證書這套體系爲何可以運轉起來爲咱們提供公鑰的認證的保障以後。看看計算機中的公鑰證書是什麼樣的,咱們拿https://www.google.com的作例子(F12,打開安全選項卡便可),證書的相關信息以下:
證書除了包含公鑰、簽名算法和證書的層級結構(好比google這個的頂級頒發機構是GeoTurst Global CA),還有一些證書的序號,版本信息,有效時間等等。這些信息由一個證書數據格式的標準規範來規定的,一個很通用的格式是x509,感興趣的能夠了解一下,這裏就不介紹了。C#中有不少X509相關的類能夠供咱們使用。好比來讀取一下上述google的這個證書信息(我把證書導出爲了base64格式的數據,能夠方便的直接包含在代碼中):
private static readonly string GoogleBase64FormatPublicKeyCertificate = @" -----BEGIN CERTIFICATE----- MIIEgDCCA2igAwIBAgIIaCtCibL6TxQwDQYJKoZIhvcNAQELBQAwSTELMAkGA1UE BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl cm5ldCBBdXRob3JpdHkgRzIwHhcNMTcwNjIxMTQzNTUwWhcNMTcwOTEzMTM1MzAw WjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEXMBUGA1UEAwwOd3d3 Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFHtwc 1ECzMNuXPshUwS9IkyKPuHYA6WXa3ohXW/wMHo0IKnu5WgXmduLS6cGoFlT3oq3P PXJz11gKpdBJeoLs/g4lG3mOnGRSQbjtsWsXCPsunMjeq0vTfidJ2Gt+1eMHh5B4 qcgOxbXEK9AE6GZGCL3MSV2lE2oG0GDpStZkLhKt11GE+qrLSQCpH9XgzknHdrvz OU6Kl3e5W+4QO6rTq5285D18Ep6Cugf39JbZQZHSu0ejLnmtSOHwUg1i/vbJrDN/ yVwEySn+drxv0CzPDrTMiqGLVxBOSwN9wU9cRphiLLSdE4Sy2p77jCNLWzbcQQ5P 5f+2hLXb2Z/N1kAZAgMBAAGjggFLMIIBRzAdBgNVHSUEFjAUBggrBgEFBQcDAQYI KwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20waAYIKwYBBQUHAQEE XDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3J0 MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50czEuZ29vZ2xlLmNvbS9vY3NwMB0G A1UdDgQWBBTxnCXke+dTXQNZyBd5gWpjj8kdKjAMBgNVHRMBAf8EAjAAMB8GA1Ud IwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEvMCEGA1UdIAQaMBgwDAYKKwYBBAHW eQIFATAIBgZngQwBAgIwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29n bGUuY29tL0dJQUcyLmNybDANBgkqhkiG9w0BAQsFAAOCAQEAgHtFBvVyIQwRSrUC RbPu0fZFl9HJCJ0FBXVlQl0JO5PdRTtXlkfDqeoZcE3l562/FotKyaPKRyhktYDb 5tnYo74q1gKxfmTjXEtkBeUDUAlNzepuXYudu43A5athR/GPIDxXQvQc4Lakmafi LJFTZLw7ZjmkU0mkt3uaiUXTuOiA+5hjjGLzFzRpRXvUcqIggGUTVJ4v7HSmOl3x tjePNc8ps3bivp8WtB4jR6k+PvVmlYDN/Uf7+cwmOEtrXUBCrVwA/LL+j3mkwHK0 49h5xyjmB/ndmH/HgjY2DSzu2HMekkPJEnPWmkxqRP2c08UqQoUbXE9zdL35Ys5A JRO+1w== -----END CERTIFICATE----- ";
static void Main() {
var bytes = GoogleBase64FormatPublicKeyCertificate.ToBytes(Encoding.ASCII);
var x509 = new X509Certificate2(bytes);
var text = x509.ToString(true);
Console.WriteLine(text);
Console.ReadKey();
}
複製代碼
英文全稱:Public Key Cryptography Standards
證書的相關格式以及交換標準在PKCS(Public-Key Cryptography Standards)中有詳細的定義。常見的證書編碼格式:
常見到的幾個證書擴展名: