哈希(Hash)與加密(Encrypt)的基本原理、區別及工程應用
[2] 哈希(Hash)與加密(Encrypt)的基本原理、區別及工程應用
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)的數學基礎
從數學角度講,哈希和加密都是一個映射。下面正式定義兩者:
一個哈希算法是一個多對一映射,給定目標文本S,H可以將其唯一映射為R,并且對于所有S,R具有相同的長度。由于是多對一映射,所以H不存在逆映射
使得R轉換為唯一的S。
一個加密算法是一個一一映射,其中第二個參數叫做加密密鑰,E可以將給定的明文S結合加密密鑰Ke唯一映射為密文R,并且存在另一個一一映射
,可以結合Kd將密文R唯一映射為對應明文S,其中Kd叫做解密密鑰。
下圖是哈希和加密過程的圖示:
有了以上定義,就很清楚為什么會存在上文提到的兩個區別了。由于哈希算法的定義域是一個無限集合,而值域是一個有限集合,將無限集合映射到有限集合,根據“鴿籠原理(Pigeonhole principle)”,每個哈希結果都存在無數個可能的目標文本,因此哈希不是一一映射,是不可逆的。
而加密算法是一一映射,因此理論上來說是可逆的。
但是,符合上面兩個定義的映射僅僅可以被叫做哈希算法和加密算法,但未必是好的哈希和加密,好的哈希和加密往往需要一些附加條件,下面介紹這些內容。
一個設計良好的哈希算法應該很難從哈希結果找到哈希目標文本的碰撞(Collision)。那么什么是碰撞呢?對于一個哈希算法H,如果,則S1和S2互為碰撞。關于為什么好的哈希需要難以尋找碰撞,在下面講應用的時候會詳解。另外,好的哈希算法應該對于輸入的改變極其敏感,即使輸入有很小的改動,如一億個字符變了一個字符,那么結果應該截然不同。這就是為什么哈希可以用來檢測軟件的完整性。
一個設計良好的加密算法應該是一個“單向陷門函數(Trapdoor one-way function)”,單向陷門函數的特點是一般情況下即使知道函數本身也很難將函數的值轉換回函數的自變量,具體到加密也就是說很難從密文得到明文,雖然從理論上這是可行的,而“陷門”是一個特殊的元素,一旦知道了陷門,則這種逆轉換則非常容易進行,具體到加密算法,陷門就是密鑰。
順便提一句,在加密中,應該保密的僅僅是明文和密鑰。也就是說我們通常假設攻擊者對加密算法和密文了如指掌,因此加密的安全性應該僅僅依賴于密鑰而不是依賴于假設攻擊者不知道加密算法。
3、哈希(Hash)與加密(Encrypt)在軟件開發中的應用
哈希與加密在現代工程領域應用非常廣泛,在計算機領域也發揮了很大作用,這里我們僅僅討論在平常的軟件開發中最常見的應用——數據保護。
所謂數據保護,是指在數據庫被非法訪問的情況下,保護敏感數據不被非法訪問者直接獲取。這是非常有現實意義的,試想一個公司的安保系統數據庫服務器被入侵,入侵者獲得了所有數據庫數據的查看權限,如果管理員的口令(Password)被明文保存在數據庫中,則入侵者可以進入安保系統,將整個公司的安保設施關閉,或者刪除安保系統中所有的信息,這是非常嚴重的后果。但是,如果口令經過良好的哈希或加密,使得入侵者無法獲得口令明文,那么最多的損失只是被入侵者看到了數據庫中的數據,而入侵者無法使用管理員身份進入安保系統作惡。
3.1、哈希(Hash)與加密(Encrypt)的選擇
要實現上述的數據保護,可以選擇使用哈希或加密兩種方式。那么在什么時候該選擇哈希、什么時候該選擇加密呢?
基本原則是:如果被保護數據僅僅用作比較驗證,在以后不需要還原成明文形式,則使用哈希;如果被保護數據在以后需要被還原成明文,則需要使用加密。
例如,你正在做一個系統,你打算當用戶忘記自己的登錄口令時,重置此用戶口令為一個隨機口令,而后將此隨機口令發給用戶,讓用戶下次使用此口令登錄,則適合使用哈希。實際上很多網站都是這么做的,想想你以前登錄過的很多網站,是不是當你忘記口令的時候,網站并不是將你忘記的口令發送給你,而是發送給你一個新的、隨機的口令,然后讓你用這個新口令登錄。這是因為你在注冊時輸入的口令被哈希后存儲在數據庫里,而哈希算法不可逆,所以即使是網站管理員也不可能通過哈希結果復原你的口令,而只能重置口令。
相反,如果你做的系統要求在用戶忘記口令的時候必須將原口令發送給用戶,而不是重置其口令,則必須選擇加密而不是哈希。
3.2、使用簡單的一次哈希(Hash)方法進行數據保護
首先我們討論使用一次哈希進行數據保護的方法,其原理如下圖所示:
對上圖我想已無需多言,很多朋友應該使用過類似的哈希方法進行數據保護。當前最常用的哈希算法是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"); } } }