Web開發之編碼與解碼、簽名、加密與解密

cch_cch發表於2015-08-07

分類: Web開發

        在Web開發中,編碼與解碼、簽名、加密與解密是非常常見的問題。本文不會介紹具體例項,而是介紹這些的原理、用途與區別。
一、編碼與解碼
        在Web開發中,需要通過URL的query引數來傳遞資料,但是對於URL來說,有些字元是不安全的,所以需要對此進行編碼。常見的編碼方式有Base64,關於Base64的詳細資訊可以參見這裡。但是Base64中有+這種對於URL不安全的字元,這就衍生出了URL安全的Base64編碼,簡單地說就是基本Base64編碼的改進。
        其實 ,也不定要做Base64,也可以直接用URL編碼來規避不全字元,關於URL編碼與解碼的詳細資訊請參見這裡
        對於傳送方來說,就是編碼,反之,對於接收方來說,就是解碼。

二、簽名
        簽名的場景很多,比如:在雲盤中,會對檔案進行MD5簽名,對於相同檔案之存一份來節省空間和做到秒傳;在TCP/IP中,會用CRC來對通訊資料做簽名,用於校驗資料是否被篡改,已決定是否丟棄。
        常見的簽名方式有很多,比如:MD5、SHA系列、HmacSHA系列、CRC等。
1、MD5
        MD5即Message-Digest Algorithm 5(資訊-摘要演算法 5),用於確保資訊傳輸完整一致,是計算機廣泛使用的雜湊演算法之一(又譯摘要演算法、雜湊演算法),主流程式語言普遍已有MD5實現。 將資料(如漢字)運算為另一固定長度值,是雜湊演算法的基礎原理,MD5的前身有MD2、MD3和MD4。
        MD5一度被廣泛應用於安全領域,但是由於MD5的弱點被不斷髮現以及計算機能力不斷的提升,現在已經可以構造兩個具有相同MD5的資訊,使本演算法不再適合當前的安全環境。目前,MD5計算廣泛應用於錯誤檢查,例如:在一些BitTorrent下載中,軟體通過計算MD5和檢驗下載到的碎片的完整性。
        MD5是輸入不定長度資訊,輸出固定長度128-bits的演算法,經過程式流程,生成四個32位資料,最後聯合起來成為一個128-bits雜湊。基本方式為,求餘、取餘、調整長度、與連結變數進行迴圈運算,得出結果。
2、SHA系列
        SHA是一個系列,SHA-1在許多安全協議中廣為使用,包括TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被視為是MD5(更早之前被廣為使用的雜湊函式)的後繼者。但SHA-1的安全性如今被密碼學家嚴重質疑,因此,為了解決問題而衍生出了SHA-224、SHA-256、SHA-384和SHA-512,這些演算法並稱為SHA-2。
        SHA-2並沒有接受像SHA-1一樣的公眾密碼社群做詳細的檢驗,所以它們的密碼安全性還不被大家廣泛的信任。雖然至今尚未出現對SHA-2有效的攻擊,它的演算法跟SHA-1基本上仍然相似,因此有些人開始發展其它替代的雜湊演算法。
        SHA-3,之前名為Keccak演算法,是一個加密雜湊演算法。SHA-3並不是要取代SHA-2,因為SHA-2目前並沒有出現明顯的弱點。由於對MD5出現成功的破解,以及對SHA-0和SHA-1出現理論上破解的方法,NIST感覺需要一個與之前演算法不同的,可替換的加密雜湊演算法,也就是現在的SHA-3。
3、HmacSHA系列
        HmacSHA也是一個系列,有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512、HmacMD5等,這裡以HmacSHA1為例說明之。
        HmacSHA1是從 SHA1 雜湊函式構造的一種鍵控雜湊演算法,被用作 HMAC(基於雜湊的訊息驗證程式碼)。 此 HMAC 程式將金鑰與訊息資料混合,使用雜湊函式對混合結果進行雜湊計算,將所得雜湊值與該金鑰混合,然後再次應用雜湊函式。 輸出的雜湊值長度為160位。HmacSHA1接受任何大小的金鑰,併產生長度為160位的雜湊序列。
        在傳送方和接收方共享機密金鑰的前提下,HMAC 可用於確定通過不安全通道傳送的訊息是否已被篡改。 傳送方計算原始資料的雜湊值,並將原始資料和雜湊值放在一個訊息中同時傳送。 接收方重新計算所接收訊息的雜湊值,並檢查計算所得的 HMAC 是否與傳送的 HMAC 匹配。
        因為更改訊息和重新生成正確的雜湊值需要金鑰,所以對資料或雜湊值的任何更改都會導致不匹配。 因此,如果原始的雜湊值與計算得出的雜湊值相匹配,則訊息通過身份驗證。
        簡單來說就是確認請求的URL或者引數是否存在被篡改,以QQ簽名為例:傳送方(自己)將引數等進行HMAC演算法計算,將得到的雜湊值(即簽名值)與請求的引數一同提交至接收方(QQ端),然後接收方再次將引數等值進行HMAC演算法計算,將得到的雜湊值與你傳遞過來的雜湊值進行核對驗證,若一樣,說明請求正確、驗證通過,進行一下步工作,若不一樣,將返回錯誤。 
        這裡有兩個不錯的線上簽名工具,請分別參看這裡這裡,以備平時測試用。

三、加密與解密
        有時候對資料簽名並不能滿足我們的需求,比如:在移動應用中明文傳遞imei、idfa或者mac會存在一定的風險;在Web開發中傳遞密碼或者價格有關的資訊,這些都需要接收方反解資料,這種場景僅僅簽名就不夠了。
        加密與解密簡單說就是傳送方用金鑰對資料進行加密,接收方用金鑰對資料進行解密來滿足上面場景需求。
        常用的加密與解密演算法有很多種,比如:DES系列、RC系列、AES(Rijndael)、BlowFish、RSA等。
1、DES系列
        DES即資料加密標準(Data Encryption Standard),由IBM設計作為非機密資料的正式資料加密標準,速度較快,適用於加密大量資料的場合。
        DES演算法的入口引數有三個:Key、Data、Mode,其中,Key為8個位元組共64位,是DES演算法的工作金鑰;Data也為8個位元組64位,是要被加密或被解密的資料;Mode為DES的工作模式,有兩種:加密或解密。
        當模式為加密模式時,明文按照64位進行分組,形成明文組,key用於對資料加密,當模式為解密模式時,key用於對資料解密。實際運用中,金鑰只用到了64位中的56位,這樣才具有高的安全性。
        3DES即(Triple DES),是DES向AES過度的加密演算法,它以DES為基本模組,通過組合分組方法設計出分組加密演算法,對一塊資料使用三個不同的64位金鑰進行三次加密,強度較DES更高。假設Ek()和Dk()代表DES演算法的加密和解密過程,K代表DES演算法使用的金鑰,P代表明文,C代表密表,這樣:
        3DES加密過程為:C=Ek3(Dk2(Ek1(P)))
        3DES解密過程為:P=Dk1((EK2(Dk3(C)))
        K1、K2、K3決定了演算法的安全性,若三個金鑰互不相同,本質上就相當於用一個長為168位的金鑰進行加密。多年來,它在對付強力攻擊時是比較安全的。若資料對安全性要求不那麼高,K1可以等於K3。在這種情況下,金鑰的有效長度為112位。
2、RC系列
        RC系列包括RC2、RC4,用變長密碼對大量資料進行加密,比DES快。
        RC2可作為DES演算法的建議替代演算法。它的輸入和輸出都是64位元。金鑰的長度是從1位元組到128位元組可變,但目前的實現是8位元組(1998年)。此演算法被設計為可容易地在16位的微處理器上實現。在一個IBM AT機上,RC2加密演算法的執行可比DES演算法快兩倍(假設進行金鑰擴充套件)。
        RC4是金鑰長度可變的流加密演算法,RC4由偽隨機數生成器和異或運算組成,RC4的金鑰長度可變,範圍是[1,255],但一般為256位元組。RC4一個位元組一個位元組地加解密。給定一個金鑰,偽隨機數生成器接受金鑰併產生一個S-box,S-box用來加密資料,而且在加密過程中S-box會變化。RC4的速度可以達到DES的10倍左右,且具有很高階別的非線性,是有線等效加密(WEP)中採用的加密演算法,也是安全套接層(SSL)可採用的演算法之一。
3、IDEA
        IDEA即(International Data Encryption Algorithm)國際資料加密演算法,在DES基礎上發展出來,類似於3DES,使用128位金鑰提供非常強的安全性。IDEA設計了一系列加密輪次,每輪加密都使用從完整的加密金鑰中生成的一個子金鑰,與DES的不同處在於,它採用軟體實現和採用硬體實現同樣快速。
        由於IDEA是在美國之外提出並發展起來的,避開了美國法律上對加密技術的諸多限制,因此,有關IDEA演算法和實現技術的書籍都可以自由出版和交流,可極大地促進IDEA的發展和完善。IDEA曾今也是AES演算法標準的主要競爭者,其安全性已經在國際密碼年會上被證明。 
3、AES(Rijndael)
        AES即高階加密標準(Advanced Encryption Standard),是下一代的加密演算法標準,速度快,安全級別高,目前AES標準的一個實現是Rijndael演算法。
        AES演算法基於排列和置換運算,排列是對資料重新進行安排,置換時將一個資料單元替換為另一個。AES的分組區塊長度固定為128位元,金鑰長度則可以是128,192或256位元(如果資料塊及金鑰長度不足時,會補齊)。
        AES加密有很多輪的重複和變換。大致步驟如下:1、金鑰擴充套件(KeyExpansion),2、初始輪(Initial Round),3、重複輪(Rounds),每一輪又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey,4、最終輪(Final Round),最終輪沒有MixColumns。
        AES加密過程是在一個4×4的位元組矩陣上運作,這個矩陣又稱為“狀態(state)”,其初值就是一個明文區塊(矩陣中一個元素大小就是明文區塊中的一個Byte)。加密時,各輪AES加密迴圈(除最後一輪外)均包含以下4個步驟:
        (1)AddRoundKey:矩陣中的每一個位元組都與該次輪祕鑰(round key)做XOR運算,每個子金鑰由金鑰生成方案產生(即Rijndael金鑰生成方案)。
        (2)SubBytes:通過一個非線性的替換函式,用查詢表的方式把每個位元組替換成對應的位元組。
        (3)ShiftRows:將矩陣中的每個橫列進行迴圈式移位。
        (4)MixColumns:為了充分混合矩陣中各個直行的操作,這個步驟使用線性轉換來混合每列的四個位元組。
        最後一個加密迴圈中省略MixColumns步驟,而以另一個AddRoundKey取代。
        關於AES(Rijndael)演算法的實現可以參見這裡
4、BlowFish
        BlowFish使用變長的金鑰,長度可達448位,資料塊大小是64bit(8位元組),加解密速度很快。
        OpenSSL中提供了BlowFish演算法的實現,主要包括三個函式:
        void BF_set_key(BF_KEY *key, int len, const unsigned char *data)
        void BF_encrypt(BF_LONG *data,const BF_KEY *key)
        void BF_decrypt(BF_LONG *data,const BF_KEY *key)
        BF_set_key函式,它是用來初始化金鑰的,把金鑰資訊(首地址是data,長度為len的位元組陣列)設定到BF_KEY結構裡。設定之後,我們只需要使用BF_KEY結構就可以表徵金鑰了,在後續呼叫加解密函式時,我們只需要把這個結構傳遞進去就可以了。這裡需要注意的一點是Blowfish實際使用的金鑰是根據使用者的金鑰資訊進行運算產生的,這是一個比較耗時的過程,這個BF_set_key函式就進行了金鑰初始化的過程,所以,我們應該呼叫一次BF_set_key,然後重複使用經過初始化後的BF_KEY結構,而不是每次需要加解密時都用data和len來重新初始化一把BF_KEY。
        BF_encrypt和BF_decrypt顧名思義就是用於資料加解密的,輸入/輸出使用相同引數來傳入傳出,即引數BF_LONG *data。
        -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
        以上演算法都屬於對稱金鑰演算法,金鑰都是私有的,不公開。所謂對稱加密,簡單來說,“加密”就是把容易識別的資訊變成不易識別的資訊;而“對稱”則表示加密者和解密者之間擁有相同的金鑰。
        對稱金鑰的缺陷在於其安全性依賴於加密演算法的強大和金鑰的私密性,而金鑰又必須讓解密方知道,這對於有些應用場景來說本身就是不安全的,這樣就產生了非對稱加密。
5、RSA
        非對稱加密/解密演算法中加密和解密使用不同的密碼,有代表性的有RSA、DSA、ElGamal和ECDSA,這裡以RSA為例說明之。非對稱加密/解密的安全性是基於複雜數學難題,特點是運算複雜、速度慢(極端情況下慢上1000倍),主要應用於金融、軍事等重大機密的系統(這些系統中極有可能所有出口都有間諜監聽,傳遞私密金鑰是不安全的)。
        非對稱加密演算法(asymmetric cryptographic algorithm)又名“公開金鑰加密演算法”,非對稱加密演算法需要兩個金鑰:公開金鑰(publickey)和私有金鑰(privatekey),其工作原理如下:
        (1)A要向B傳送資訊,A和B都要產生一對用於加密和解密的公鑰和私鑰。  
        (2)A的私鑰保密,A的公鑰告訴B;B的私鑰保密,B的公鑰告訴A。
        (3)A要給B傳送資訊時,A用B的公鑰加密資訊,因為A知道B的公鑰。
        (4)A將這個訊息發給B(已經用B的公鑰加密訊息)。
        (5)B收到這個訊息後,B用自己的私鑰解密A的訊息。其他所有收到這個報文的人都無法解密,因為只有B才有B的私鑰。
        RSA演算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密金鑰。RSA金鑰長度隨著保密級別提高,增加很快,只要其鑰匙的長度足夠長(RSA金鑰長度一般在1024bits及以上,比如:1024、2048、3072、7680等),用RSA加密的資訊實際上是不能被解破的。
        RSA演算法是一種非對稱密碼演算法,所謂非對稱,就是指該演算法需要一對金鑰,使用其中一個加密,則需要用另一個才能解密。
        RSA的演算法涉及三個引數,n、e1、e2。
        其中,n是兩個大質數p、q的積,n的二進位制表示時所佔用的位數,就是所謂的金鑰長度。
        e1和e2是一對相關的值,e1可以任意取,但要求e1與(p-1)*(q-1)互質;再選擇e2,要求(e2*e1)mod((p-1)*(q-1))=1。
        (n,e1),(n,e2)就是金鑰對。其中,(n,e1)為公鑰,(n,e2)為私鑰。
        RSA加解密的演算法完全相同,假設A為明文,B為密文,則:A=B^e2 mod n;B=A^e1 mod n;(公鑰加密體制中,一般用公鑰加密,私鑰解密)
        e1和e2可以互換使用,即:
        A=B^e1 mod n;B=A^e2 mod n;
        速度一直是RSA的缺陷,一般來說只用於少量資料加密,RSA的速度比對應同樣安全級別的對稱密碼演算法要慢1000倍左右。

相關文章