天天看點

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

0、摘要

      今天看到

吉日嘎拉

一篇關于管理軟體中資訊加密和安全的文章

,感覺非常有實際意義。文中作者從實踐經驗出發,讨論了資訊管理軟體中如何通過哈希和加密進行資料保護。但是從文章評論中也可以看出很多朋友對這個方面一些基本概念比較模糊,這樣就容易“照葫蘆畫瓢”,不能根據自身具體情況靈活選擇和使用各種哈希和加密方式。本文不對哈希和加密做過于深入的讨論,而是對哈希和加密的基本概念和原理進行闡述、比較,并結合具體實踐說明如何選擇哈希和加密算法、如何提高安全性等問題,使朋友們做到“知其然,知其是以然”,這樣就能通過分析具體情況,靈活運用哈希和加密保護資料。

1、哈希(Hash)與加密(Encrypt)的差別

      在本文開始,我需要首先從直覺層面闡述

哈希(Hash)

加密(Encrypt)

的差別,因為我見過很多朋友對這兩個概念不是很清晰,容易混淆兩者。而正确差別兩者是正确選擇和使用哈希與加密的基礎。

      概括來說,哈希(Hash)是将目标文本轉換成具有相同長度的、不可逆的雜湊字元串(或叫做消息摘要),而加密(Encrypt)是将目标文本轉換成具有不同長度的、可逆的密文。

      具體來說,兩者有如下重要差別:

      1、雜湊演算法往往被設計成生成具有相同長度的文本,而加密算法生成的文本長度與明文本身的長度有關。

      例如,設我們有兩段文本:“Microsoft”和“Google”。兩者使用某種雜湊演算法得到的結果分别為:“140864078AECA1C7C35B4BEB33C53C34”和“8B36E9207C24C76E6719268E49201D94”,而使用某種加密算法的到的結果分别為“Njdsptpgu”和“Hpphmf”。可以看到,哈希的結果具有相同的長度,而加密的結果則長度不同。實際上,如果使用相同的雜湊演算法,不論你的輸入有多麼長,得到的結果長度是一個常數,而加密算法往往與明文的長度成正比。

      2、雜湊演算法是不可逆的,而加密算法是可逆的。

      這裡的不可逆有兩層含義,一是“給定一個哈希結果R,沒有方法将E轉換成原目标文本S”,二是“給定哈希結果R,即使知道一段文本S的哈希結果為R,也不能斷言當初的目标文本就是S”。其實稍微想想就知道,哈希是不可能可逆的,因為如果可逆,那麼哈希就是世界上最強悍的壓縮方式了——能将任意大小的檔案壓縮成固定大小。

      加密則不同,給定加密後的密文R,存在一種方法可以将R确定的轉換為加密前的明文S。

      這裡先從直覺層面簡單介紹兩者的差別,等下文從數學角度對兩者做嚴謹描述後,讀者朋友就知道為什麼會有這兩個差別了。

2、哈希(Hash)與加密(Encrypt)的數學基礎

      從數學角度講,哈希和加密都是一個映射。下面正式定義兩者:

      一個雜湊演算法

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

是一個多對一映射,給定目标文本S,H可以将其唯一映射為R,并且對于所有S,R具有相同的長度。由于是多對一映射,是以H不存在逆映射

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

使得R轉換為唯一的S。

      一個加密算法

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

是一個一一映射,其中第二個參數叫做加密密鑰,E可以将給定的明文S結合加密密鑰Ke唯一映射為密文R,并且存在另一個一一映射

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

,可以結合Kd将密文R唯一映射為對應明文S,其中Kd叫做解密密鑰。

      下圖是哈希和加密過程的圖示:

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

      有了以上定義,就很清楚為什麼會存在上文提到的兩個差別了。由于雜湊演算法的定義域是一個無限集合,而值域是一個有限集合,将無限集合映射到有限集合,根據“

鴿籠原理

(Pigeonhole principle)”,每個哈希結果都存在無數個可能的目标文本,是以哈希不是一一映射,是不可逆的。

      而加密算法是一一映射,是以理論上來說是可逆的。

      但是,符合上面兩個定義的映射僅僅可以被叫做雜湊演算法和加密算法,但未必是好的哈希和加密,好的哈希和加密往往需要一些附加條件,下面介紹這些内容。

      一個設計良好的雜湊演算法應該很難從哈希結果找到哈希目标文本的碰撞(Collision)。那麼什麼是碰撞呢?對于一個雜湊演算法H,如果

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

,則S1和S2互為碰撞。關于為什麼好的哈希需要難以尋找碰撞,在下面講應用的時候會詳解。另外,好的雜湊演算法應該對于輸入的改變極其敏感,即使輸入有很小的改動,如一億個字元變了一個字元,那麼結果應該截然不同。這就是為什麼哈希可以用來檢測軟體的完整性。

      一個設計良好的加密算法應該是一個“單向陷門函數(Trapdoor one-way function)”,

單向陷門函數

的特點是一般情況下即使知道函數本身也很難将函數的值轉換回函數的自變量,具體到加密也就是說很難從密文得到明文,雖然從理論上這是可行的,而“陷門”是一個特殊的元素,一旦知道了陷門,則這種逆轉換則非常容易進行,具體到加密算法,陷門就是密鑰。

      順便提一句,在加密中,應該保密的僅僅是明文和密鑰。也就是說我們通常假設攻擊者對加密算法和密文了如指掌,是以加密的安全性應該僅僅依賴于密鑰而不是依賴于假設攻擊者不知道加密算法。

3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用

      哈希與加密在現代工程領域應用非常廣泛,在計算機領域也發揮了很大作用,這裡我們僅僅讨論在平常的軟體開發中最常見的應用——資料保護。

      所謂資料保護,是指在資料庫被非法通路的情況下,保護敏感資料不被非法通路者直接擷取。這是非常有現實意義的,試想一個公司的安保系統資料庫伺服器被入侵,入侵者獲得了所有資料庫資料的檢視權限,如果管理者的密碼(Password)被明文儲存在資料庫中,則入侵者可以進入安保系統,将整個公司的安保設施關閉,或者删除安保系統中所有的資訊,這是非常嚴重的後果。但是,如果密碼經過良好的哈希或加密,使得入侵者無法獲得密碼明文,那麼最多的損失隻是被入侵者看到了資料庫中的資料,而入侵者無法使用管理者身份進入安保系統作惡。

3.1、哈希(Hash)與加密(Encrypt)的選擇

      要實作上述的資料保護,可以選擇使用哈希或加密兩種方式。那麼在什麼時候該選擇哈希、什麼時候該選擇加密呢?

      基本原則是:如果被保護資料僅僅用作比較驗證,在以後不需要還原成明文形式,則使用哈希;如果被保護資料在以後需要被還原成明文,則需要使用加密。

      例如,你正在做一個系統,你打算當使用者忘記自己的登入密碼時,重置此使用者密碼為一個随機密碼,而後将此随機密碼發給使用者,讓使用者下次使用此密碼登入,則适合使用哈希。實際上很多網站都是這麼做的,想想你以前登入過的很多網站,是不是當你忘記密碼的時候,網站并不是将你忘記的密碼發送給你,而是發送給你一個新的、随機的密碼,然後讓你用這個新密碼登入。這是因為你在注冊時輸入的密碼被哈希後存儲在資料庫裡,而雜湊演算法不可逆,是以即使是網站管理者也不可能通過哈希結果複原你的密碼,而隻能重置密碼。

      相反,如果你做的系統要求在使用者忘記密碼的時候必須将原密碼發送給使用者,而不是重置其密碼,則必須選擇加密而不是哈希。

3.2、使用簡單的一次哈希(Hash)方法進行資料保護

      首先我們讨論使用一次哈希進行資料保護的方法,其原理如下圖所示:

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

      對上圖我想已無需多言,很多朋友應該使用過類似的哈希方法進行資料保護。目前最常用的雜湊演算法是

MD5 SHA1

,下面給出在.NET平台上用C#語言實作MD5和SHA1哈希的代碼,由于.NET對于這兩個雜湊演算法已經進行很很好的封裝,是以我們不必自己實作其算法細節,直接調用相應的庫函數即可(實際上MD5和SHA1算法都十分複雜,有興趣的可以參考

維基百科

)。

using

System;

using

System.Web.Security;

namespace

HashAndEncrypt

{

/// <summary>

/// 哈希(Hash)工具類

/// </summary>

public

sealed

class

HashHelper

{

/// <summary>

/// 使用MD5算法進行哈希

/// </summary>

/// <param name="source">源字串</param>

/// <returns>雜湊字串</returns>

public

static

string

MD5Hash(

string

source)

{

return

FormsAuthentication.HashPasswordForStoringInConfigFile(source,

"MD5"

);

}

/// <summary>

/// 使用SHA1算法進行哈希

/// </summary>

/// <param name="source">源字串</param>

/// <returns>雜湊字串</returns>

public

static

string

SHA1Hash(

string

source)

{

return

FormsAuthentication.HashPasswordForStoringInConfigFile(source,

"SHA1"

);

}

}

}

3.3、對簡單哈希(Hash)的攻擊

      下面我們讨論上述的資料保護方法是否安全。

      對于哈希的攻擊,主要有尋找碰撞法和窮舉法。

      先來說說尋找碰撞法。從哈希本身的定義和上面的資料保護原理圖可以看出,如果想非法登入系統,不一定非要得到注冊時的輸入密碼,隻要能得到一個注冊密碼的碰撞即可。是以,如果能從雜湊串中分析出一個密碼的碰撞,則大功告成。

      不過我的意見是,對這種攻擊大可不必擔心,因為目前對于MD5和SHA1并不存在有效地尋找碰撞方法。雖然我國傑出的數學家

王小雲

教授曾經在國際密碼學會議上釋出了對于MD5和SHA1的碰撞尋找改進算法,但這種方法和很多人口中所說的“破解”相去甚遠,其理論目前僅具有數學上的意義,她将破解MD5的預期步驟數從2^80降到了2^69,雖然從數學上降低了好幾個數量級,但2^69對于實際應用來說仍然是一個天文數字,就好比以前需要一億年,現在需要一萬年一樣。

      不過這并不意味着使用MD5或SHA1後就萬事大吉了,因為還有一種對于哈希的攻擊方法——窮舉法。通俗來說,就是在一個範圍内,如從000000到999999,将其中所有值一個一個用相同的雜湊演算法哈希,然後将結果和雜湊串比較,如果相同,則這個值就一定是源字串或源字串的一個碰撞,于是就可以用這個值非法登入了。

      例如,下文是對MD5的窮舉攻擊的代碼(設攻擊範圍為000000到999999):

using

System;

using

System.Web.Security;

namespace

HashAndEncrypt

{

/// <summary>

/// MD5攻擊工具類

/// </summary>

public

sealed

class

MD5AttackHelper

{

/// <summary>

/// 對MD5進行窮舉攻擊

/// </summary>

/// <param name="hashString">雜湊串</param>

/// <returns>雜湊串的源串或源串碰撞(攻擊失敗則傳回null)</returns>

public

static

string

AttackMD5(

string

hashString)

{

for

(

int

i = 0; i <= 999999; i++)

{

string

testString = i.ToString();

while

(testString.Length < 6)

testString =

"0"

+ testString;

if

(FormsAuthentication.HashPasswordForStoringInConfigFile(testString,

"MD5"

) == hashString)

return

testString;

}

return

null

;

}

}

}

      這種看似笨拙的方法,在現實中爆發的能量卻是驚人的,目前幾乎所有的MD5破解機或MD5線上破解都是用這種窮舉法,但就是這種“笨”方法,卻成功破解出很多哈希串。糾其緣由,就是相當一部分密碼是非常簡單的,如“123456”或“000000”這種密碼還有很多人在用,可以看出,窮舉法是否能成功很大程度上取決于密碼的複雜性。因為窮舉法掃描的區間往往是單字元集、規則的區間,或者由字典資料進行組合,是以,如果使用複雜的密碼,例如“ASDF#$%uiop.8930”這種變态級密碼,窮舉法就很難奏效了。

3.4、對一次哈希(Hash)的改進——多重混合哈希(Hash)

      上面說過,如果密碼過于簡單,則使用窮舉法可以很有效地破解出一次哈希後的雜湊串。如果不想這樣,隻有讓使用者使用複雜密碼,但是,很多時候我們并不能強迫使用者,是以,我們需要想一種辦法,即使使用者使用諸如“000000”這種簡單密碼,也令窮舉法難奏效。其中一種辦法就是使用多重哈希,所謂多重哈希就是使用不同的哈希函數配合自定義的Key對密碼進行多次哈希,如果Key很複雜,那麼窮舉法将變得異常艱難。

      例如,如果使用下面的混合公式進行哈希:

哈希(Hash)與加密(Encrypt)的基本原理、差別及工程應用0、摘要1、哈希(Hash)與加密(Encrypt)的差別2、哈希(Hash)與加密(Encrypt)的數學基礎3、哈希(Hash)與加密(Encrypt)在軟體開發中的應用4、總結

      如果将Key設為一個極為複雜的字元串,那麼在攻擊者不知道Key的情況下,幾乎無法通過窮舉法破解。因為即使S很簡單,但是Key的MD5值幾乎是無法在合理時間内窮舉完的。下面是這種多重混合哈希的代碼實作:

using

System;

using

System.Web.Security;

namespace

HashAndEncrypt

{

/// <summary>

/// 多重混合哈希工具類

/// </summary>

public

sealed

class

HashHelper

{

private

static

readonly

String hashKey =

"qwer#&^Buaa06"

;

/// <summary>

/// 對敏感資料進行多重混合哈希

/// </summary>

/// <param name="source">待處理明文</param>

/// <returns>Hasn後的資料</returns>

public

static

String Hash(String source)

{

String hashCode = FormsAuthentication.HashPasswordForStoringInConfigFile(source,

"MD5"

) +

FormsAuthentication.HashPasswordForStoringInConfigFile(hashKey,

"MD5"

);

return

FormsAuthentication.HashPasswordForStoringInConfigFile(hashCode,

"SHA1"

);

}

}

}

3.5、使用加密(Encrypt)方法進行資料保護

      加密方法如果用于密碼保護的話,與上述哈希方法的流程基本一緻,隻是在需要時,可以使用解密方法得到明文。關于加密本身是一個非常龐大的系統,而對于加密算法的攻擊更是可以寫好幾本書了,是以這裡從略。下面隻給出使用C#進行

DES

加密和解密的代碼。

using

System;

using

System.Security.Cryptography;

using

System.Text;

using

System.Web.Security;

namespace

HashAndEncrypt

{

/// <summary>

/// 工具類,封裝了加解密相關操作

/// </summary>

public

sealed

class

EncryptHelper

{

private

static

readonly

Byte[] DesKey = {5, 7, 8, 9, 0, 2, 1, 6};

private

static

readonly

Byte[] DesVi = { 6, 9, 8, 5, 1, 6, 2, 8 };

/// <summary>

/// 使用DES算法加密資料

/// </summary>

/// <param name="data">待加密資料</param>

/// <returns>密文</returns>

public

static

String Encrypt(String data)

{

DESCryptoServiceProvider des =

new

DESCryptoServiceProvider();

Encoding utf =

new

UTF8Encoding();

ICryptoTransform encryptor = des.CreateEncryptor(DesKey, DesVi);

byte

[] bData = utf.GetBytes(data);

byte

[] bEnc = encryptor.TransformFinalBlock(bData, 0, bData.Length);

return

Convert.ToBase64String(bEnc);

}

/// <summary>

/// 使用DES算法解密資料

/// </summary>

/// <param name="data">待解密資料</param>

/// <returns>明文</returns>

public

static

String Decrypt(String data)

{

DESCryptoServiceProvider des =

new

DESCryptoServiceProvider();

Encoding utf =

new

UTF8Encoding();

ICryptoTransform decryptor = des.CreateDecryptor(DesKey, DesVi);

byte

[] bEnc = Convert.FromBase64String(data);

byte

[] bDec = decryptor.TransformFinalBlock(bEnc, 0, bEnc.Length);

return

utf.GetString(bDec);

}

}

}

4、總結

      密碼學本身是一個非常深奧的數學分支,對于普通開發者,不需要了解過于深入的密碼學知識。本文僅僅講述哈希與加密的基礎内容,并對兩者做了比較,幫助讀者明晰概念,另外,對一些實際應用情況進行了簡單的讨論。希望本文對大家有所幫助。看了下時間,零點剛過,祝大家十一快樂!玩得開心!