加密算法原理分析(MD五、SHA-256)

最近回顧了一下數據結構,發現一直在使用HasMap,可是大學期間針對哈希表的概念老師當時居然跳過啦,所以從新瞭解了一下哈希表的基礎知識。有個不錯的講解視頻: v.youku.com/v_show/id_X…html

順便也瞭解了一下HashMap的原理,是如何使用哈希表的,感興趣的小夥伴能夠看一下: www.cnblogs.com/xiaoxi/p/72…www.cnblogs.com/chengxiao/p…前端

先放兩張哈希表的結構吧:ios

  • JDK1.8以前,以及JDK1.8以後,同一個hash值的節點數小於8時 算法

  • JDK1.8以後,當同一個hash值的節點數不小於8時,再也不採用單鏈表形式存儲,而是採用紅黑樹 數組

本文主要仍是看一看再研究哈希表過程當中,比較有意思的哈希的加密算法(非對稱加密)緩存

哈希函數:Hash,通常翻譯作散列、雜湊,或音譯爲哈希,就是把任意長度的輸入(又叫作預映射, pre-image),經過散列算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間一般遠小於輸入的空間,不一樣的輸入可能會散列成相同的輸出,而不可能從散列值來惟一的肯定輸入值。簡單的說就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的函數。安全

本文主要講解兩種經常使用的哈希函數MD5和SHA-256(目前最流行的安全加密算法)bash

MD5

MD5消息摘要算法(英語:MD5 Message-Digest Algorithm),一種被普遍使用的密碼散列函數,能夠產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致。MD5由美國密碼學家羅納德·李維斯特(Ronald Linn Rivest)設計,於1992年公開,用以取代MD4算法。服務器

算法步驟:數據結構

一、數據填充

對消息進行數據填充,使消息的長度對512取模得448,設消息長度爲X,即知足X mod 512=448。根據此公式得出須要填充的數據長度。

填充方法:在消息後面進行填充,填充第一位爲1,其他爲0。

二、添加消息長度

在第一步結果以後再填充上原消息的長度,可用來進行的存儲長度爲64位。若是消息長度大於264,則只使用其低64位的值,即(消息長度 對 264取模)。

通過上面兩步的處理,如今的信息的位長=N*512+448+64=(N+1)*512,即長度剛好是512的整數倍。這樣作的緣由是爲知足後面處理中對信息長度的要求。

三、數據處理:分組處理

  • 準備須要用到的數據:

    4個常數:

    A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
    複製代碼

    4個函數:(&是與,|是或,~是非,^是異或)

    F(X,Y,Z)=(X & Y) | ((~X) & Z); G(X,Y,Z)=(X & Z) | (Y & (~Z));   
      H(X,Y,Z)=X ^ Y ^ Z;   I(X,Y,Z)=Y ^ (X | (~Z));
    複製代碼
  • 具體操做:

  1. 首先將消息以512位爲一分組進行處理,分爲N組
  2. 將每組消息N(i)進行4輪變換(四輪主循環),以上面所說4個常數首先賦值給a、b、c、d爲起始變量進行計算,從新輸出4個變量,並從新賦值給a、b、c、d四個值。
  3. 以第2步得到的新的a、b、c、d四個值,再進行下一分組的運算,若是已是最後一個分組,則這4個變量的最後結果按照從低內存到高內存排列起來,共128位,這就是MD5算法的輸出。

第二步詳細介紹:主循環有四輪(MD4只有三輪),每輪循環都很類似。每輪主循環都有16輪次循環,16輪次循環:實際上是將每組512位分爲16組,每次操做對a、b、c和d中的其中三個做一次非線性函數運算,而後將所得結果加上第四個變量,文本的一個子分組和一個常數。再將所得結果向左環移一個不定的數,並加上a、b、c或d中之一。最後用該結果取代a、b、c或d中之一。

注(a、b、c或d):可被稱爲消息摘要部分信息,所以上面四個非線性函數,是針對a、b、c或d運算

*** 四輪主循環主要的運算函數爲下面四個包含了上面四個非線程函數的函數:

設Mj表示消息的第j個子分組(從0到15),<<< s表示循環左移s位,則四種操做爲: FF(a,b,c,d,Mj,s,ti)表示a=b+((a+(F(b,c,d)+Mj+ti)<<< s)

GG(a,b,c,d,Mj,s,ti)表示a=b+((a+(G(b,c,d)+Mj+ti)<<< s)

HH(a,b,c,d,Mj,s,ti)表示a=b+((a+(H(b,c,d)+Mj+ti)<<< s)

II(a,b,c,d,Mj,s,ti)表示a=b+((a+(I(b,c,d)+Mj+ti)<<< s)

此四個函數中的前四個位置的a,b,c,d同理X、Y、Z等,不只僅表明上面所說的消息摘要的信息a,b,c,d,可理解爲代碼中的四個參數。而參數一:就是本次次循環須要求的值。具體可經過下面具體的64步瞭解。

此圖只選了FF函數說明一下,16輪次循環每一步FF函數中前四個參數a,b,c,d的變化,同時根據上面所闡述的,參數一即爲這次函數所求值可知,FF會根據16組消息M(i)、以及s和ti,循環的計算消息摘要的a,b,c,d四個值。(GG、HH、II函數同理)

這四輪(64步)是:

第一輪 
FF(a,b,c,d,M0,7,0xd76aa478) 
FF(d,a,b,c,M1,12,0xe8c7b756) 
FF(c,d,a,b,M2,17,0×242070db) 
FF(b,c,d,a,M3,22,0xc1bdceee) 
FF(a,b,c,d,M4,7,0xf57c0faf) 
FF(d,a,b,c,M5,12,0×4787c62a) 
FF(c,d,a,b,M6,17,0xa8304613) 
FF(b,c,d,a,M7,22,0xfd469501) 
FF(a,b,c,d,M8,7,0×698098d8) 
FF(d,a,b,c,M9,12,0×8b44f7af) 
FF(c,d,a,b,M10,17,0xffff5bb1) 
FF(b,c,d,a,M11,22,0×895cd7be) 
FF(a,b,c,d,M12,7,0×6b901122) 
FF(d,a,b,c,M13,12,0xfd987193) 
FF(c,d,a,b,M14,17,0xa679438e) 
FF(b,c,d,a,M15,22,0×49b40821) 
第二輪 
GG(a,b,c,d,M1,5,0xf61e2562) 
GG(d,a,b,c,M6,9,0xc040b340) 
GG(c,d,a,b,M11,14,0×265e5a51) 
GG(b,c,d,a,M0,20,0xe9b6c7aa) 
GG(a,b,c,d,M5,5,0xd62f105d) 
GG(d,a,b,c,M10,9,0×02441453) 
GG(c,d,a,b,M15,14,0xd8a1e681) 
GG(b,c,d,a,M4,20,0xe7d3fbc8) 
GG(a,b,c,d,M9,5,0×21e1cde6) 
GG(d,a,b,c,M14,9,0xc33707d6) 
GG(c,d,a,b,M3,14,0xf4d50d87) 
GG(b,c,d,a,M8,20,0×455a14ed) 
GG(a,b,c,d,M13,5,0xa9e3e905) 
GG(d,a,b,c,M2,9,0xfcefa3f8) 
GG(c,d,a,b,M7,14,0×676f02d9) 
GG(b,c,d,a,M12,20,0×8d2a4c8a) 
第三輪 
HH(a,b,c,d,M5,4,0xfffa3942) 
HH(d,a,b,c,M8,11,0×8771f681) 
HH(c,d,a,b,M11,16,0×6d9d6122) 
HH(b,c,d,a,M14,23,0xfde5380c) 
HH(a,b,c,d,M1,4,0xa4beea44) 
HH(d,a,b,c,M4,11,0×4bdecfa9) 
HH(c,d,a,b,M7,16,0xf6bb4b60) 
HH(b,c,d,a,M10,23,0xbebfbc70) 
HH(a,b,c,d,M13,4,0×289b7ec6) 
HH(d,a,b,c,M0,11,0xeaa127fa) 
HH(c,d,a,b,M3,16,0xd4ef3085) 
HH(b,c,d,a,M6,23,0×04881d05) 
HH(a,b,c,d,M9,4,0xd9d4d039) 
HH(d,a,b,c,M12,11,0xe6db99e5) 
HH(c,d,a,b,M15,16,0×1fa27cf8) 
HH(b,c,d,a,M2,23,0xc4ac5665) 
第四輪 
II(a,b,c,d,M0,6,0xf4292244) 
II(d,a,b,c,M7,10,0×432aff97) 
II(c,d,a,b,M14,15,0xab9423a7) 
II(b,c,d,a,M5,21,0xfc93a039) 
II(a,b,c,d,M12,6,0×655b59c3) 
II(d,a,b,c,M3,10,0×8f0ccc92) 
II(c,d,a,b,M10,15,0xffeff47d) 
II(b,c,d,a,M1,21,0×85845dd1) 
II(a,b,c,d,M8,6,0×6fa87e4f) 
II(d,a,b,c,M15,10,0xfe2ce6e0) 
II(c,d,a,b,M6,15,0xa3014314) 
II(b,c,d,a,M13,21,0×4e0811a1) 
II(a,b,c,d,M4,6,0xf7537e82) 
II(d,a,b,c,M11,10,0xbd3af235) 
II(c,d,a,b,M2,15,0×2ad7d2bb) 
II(b,c,d,a,M9,21,0xeb86d391) 
複製代碼

全部這些完成以後,將A,B,C,D分別加上a,b,c,d。而後用下一分組數據繼續運行算法,最後的輸出是A,B,C和D的級聯。 把這四個數A -> B -> C -> D按照從低內存到高內存排列起來,共128位,這就是MD5算法的輸出。

Java代碼實現:

public class MD5{
    /*
    *四個連接變量:寄存器中數值(小端模式)// 真實數值
    */
    private final int A=0x67452301;// 真實A=0×01234567
    private final int B=0xefcdab89;// 真實B=0×89abcdef 
    private final int C=0x98badcfe;// 真實C=0xfedcba98 
    private final int D=0x10325476;// 真實D=0×76543210 
    /*
    *ABCD的臨時變量
    */
    private int Atemp,Btemp,Ctemp,Dtemp;
     
    /*
    * 常量ti:即代碼中的K[i]
    *   公式:floor(abs(sin(i+1))×(2pow32)
    *   ti是4294967296*abs(sin(i))的整數部分,i的單位是弧度。 
    *   4294967296 == 2的32次方
    * 因爲是常量,能夠在計算時直接嵌入數據。
    */
    private final int K[]={
        0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
        0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
        0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
        0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
        0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
        0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
        0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
        0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
        0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
        0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
        0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
        0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
        0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
    /*
    *向左位移數,因爲是常量,也能夠在計算時直接嵌入數據。
    *(此數據有規律,實際代碼中能夠對此進行優化,即改變此部分值)。
    */
    private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
        12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
        4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
        15,21,6,10,15,21,6,10,15,21,6,10,15,21};
     
    /*
    *初始化函數
    */
    private void init(){
        Atemp=A;
        Btemp=B;
        Ctemp=C;
        Dtemp=D;
    }
    /*
    *移動必定位數
    */
    private int shift(int a,int s){
        return(a<<s)|(a>>>(32-s));//右移的時候,高位必定要補零,而不是補充符號位
    }
    /*
    *主循環
    */
    private void MainLoop(int M[]){
        int F,g;
        int a=Atemp;
        int b=Btemp;
        int c=Ctemp;
        int d=Dtemp;
        for(int i = 0; i < 64; i ++){
            if(i<16){
                F=(b&c)|((~b)&d);
                g=i;
            }else if(i<32){
                F=(d&b)|((~d)&c);
                g=(5*i+1)%16;
            }else if(i<48){
                F=b^c^d;
                g=(3*i+5)%16;
            }else{
                F=c^(b|(~d));
                g=(7*i)%16;
            }
            int tmp=d;
            d=c;
            c=b;
            b=b+shift(a+F+K[i]+M[g],s[i]);
            a=tmp;
        }
        Atemp=a+Atemp;
        Btemp=b+Btemp;
        Ctemp=c+Ctemp;
        Dtemp=d+Dtemp;
     
    }
    /*
    *填充函數
    *處理後應知足bits≡448(mod512),字節就是bytes≡56(mode64)
    *填充方式爲先加一個0,其它位補零
    *最後加上64位的原來長度
    */
    private int[] add(String str){
        int num=((str.length()+8)/64)+1;//以512位,64個字節爲一組
        int strByte[]=new int[num*16];//64/4=16,因此有16個整數
        for(int i=0;i<num*16;i++){//所有初始化0
            strByte[i]=0;
        }
        int    i;
        for(i=0;i<str.length();i++){
            strByte[i>>2]|=str.charAt(i)<<((i%4)*8);//一個整數存儲四個字節,小端序
        }
        strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
        /*
        *添加原長度,長度指位的長度,因此要乘8,而後是小端序,因此放在倒數第二個,這裏長度只用了32位
        */
        strByte[num*16-2]=str.length()*8;
        return strByte;
    }
    /*
    *調用函數
    */
    public String getMD5(String source){
        init();
        int strByte[]=add(source);
        for(int i = 0; i < strByte.length / 16; i++){
            int num[] = new int[16];
            for(int j = 0; j < 16; j++){
                num[j] = strByte[i*16+j];
            }
            MainLoop(num);
        }
        return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);
    }
    /*
    *整數變成16進制字符串
    */
    private String changeHex(int a){
        String str="";
        for(int i=0;i<4;i++){
            str+=String.format("%2s", Integer.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ', '0');
 
        }
        return str;
    }
    /*
    *單例
    */
    private static MD5 instance;
    public static MD5 getInstance(){
        if(instance==null){
            instance=new MD5();
        }
        return instance;
    }
     
    private MD5(){};
     
    public static void main(String[] args){
        String str=MD5.getInstance().getMD5("");
        System.out.println(str);
    }
}
複製代碼

MD5算法的不足:

如今看來,MD5已經較老,散列長度一般爲128位,隨着計算機運算能力提升,找到「碰撞」是可能的。所以,在安全要求高的場合不使用MD5。

2004年,王小云教授證實MD5數字簽名算法能夠產生碰撞。2007年,Marc Stevens,Arjen K. Lenstra和Benne de Weger進一步指出經過僞造軟件簽名,可重複性攻擊MD5算法。研究者使用前綴碰撞法(chosen-prefix collision),使程序前端包含惡意程序,利用後面的空間添上垃圾代碼湊出一樣的MD5 Hash值。2007年,荷蘭埃因霍芬技術大學科學家成功把2個可執行文件進行了MD5碰撞,使得這兩個運行結果不一樣的程序被計算出同一個MD5。2008年12月科研人員經過MD5碰撞成功生成了僞造的SSL證書,這使得在https協議中服務器能夠僞造一些根CA的簽名。

MD5被攻破後,在Crypto2008上, Rivest提出了MD6算法,該算法的Block size爲512 bytes(MD5的Block Size是512 bits), Chaining value長度爲1024 bits, 算法增長了並行 機制,適合於多核CPU。 在安全性上,Rivest宣稱該算法可以抵抗截至目前已知的全部的 攻擊(包括差分攻擊)。

SHA-256

SHA256簡介

SHA256是SHA-2下細分出的一種算法

SHA-2,名稱來自於安全散列算法2(英語:Secure Hash Algorithm 2)的縮寫,一種密碼散列函數算法標準,由美國國家安全局研發,屬於SHA算法之一,是SHA-1的後繼者。

SHA-2下又可再分爲六個不一樣的算法標準

包括了:SHA-22四、SHA-25六、SHA-38四、SHA-5十二、SHA-512/22四、SHA-512/256。

這些變體除了生成摘要的長度 、循環運行的次數等一些微小差別外,算法的基本結構是一致的。

對於任意長度的消息,SHA256都會產生一個256bit長的哈希值,稱做消息摘要。這個摘要至關因而個長度爲32個字節的數組,一般用一個長度爲64的十六進制字符串來表示

SHA256算法步驟:

  • 初始化常量(初始化緩存:8個哈希初值)和算法中用到的64個常量

    8個哈希初值:是對天然數中前8個質數(2,3,5,7,11,13,17,19)的平方根的小數部分取前32位, 此8個哈希初值位SHA256算法中的最小運算單元稱爲「字」(Word),一個字是32位。

    A=0x6A09E667 , B=0xBB67AE85 , C=0x3C6EF372 , D=0xA54FF53A, 
      E=0x510E527F , F=0x9B05688C , G=0x1F83D9AB , H=0x5BE0CD19 。
    複製代碼

    64個算法常量:對天然數中前64個質數(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97…)的立方根的小數部分取前32位

    0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
      0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
      0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
      0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
      0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
      0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
      0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
      0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
      0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
      0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
      0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
      0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
      0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
    複製代碼
  • 預處理:附加填充比特和附加長度值

    一、附加填充比特

    在報文末尾進行填充,使報文長度在對512取模之後的餘數是448
      填充是這樣進行的:先補第一個比特爲1,而後都補0,直到長度知足對512取模後餘數是448。
      須要注意的是,信息必須進行填充,也就是說,即便長度已經知足對512取模後餘數是448,
      補位也必需要進行,這時要填充512個比特。所以,填充是至少補一位,最多補512位。
    複製代碼

    二、附加長度值

    附加長度值就是將原始數據(第一步填充前的消息)的長度信息補到已經進行了填充操做的消息後面。
      SHA256用一個64位的數據來表示原始消息的長度。
      所以,經過SHA256計算的消息長度必需要小於2^64,固然絕大多數狀況這足夠大了。
      而SHA-384和SHA-512,消息長度最大可接近2^128
    複製代碼
  • 分組運算

先上一張通用圖吧

一、分組運算所需的6個函數

Ch(x,y,z) = ((x & y) ^ ((~x) & z))
Maj(x,y,z) = ((x & y) ^ (x & z) ^ (y & z))
E0(x) = (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10);
E1(x) = (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7)
Q0(x) = (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3)
Q1(x) = (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10)
複製代碼

二、將消息分解成512-bit大小的塊,分紅N組,每組都會通過上面6個函數的循環運算

三、構造64個字(word):將上述分完組的塊,每塊分解爲16個32-bit的big-endian的字,記爲w[0], …, w[15],即前16個字直接由消息的第i個塊分解獲得,而其他的字由以下迭代公式獲得:

Wt = Q1(Wt-2) + Wt-7 + Q0(Wt-15) + Wt-16
複製代碼

四、進行64次循環:算法使用64個32位字的消息列表、8個32位工做變量以及8個32位字的散列值。

  • 初始化工做變量

    a = A // H(0)0 = A
    b = B // H(0)1 = B
    c = C // H(0)2 = C
    d = D // H(0)3 = D
    e = E // H(0)4 = E
    f = F // H(0)5 = F
    g = G // H(0)6 = G
    h = H // H(0)7 = H

  • 執行散列計算

    For t = 0 to 63 T1 = h + E1(e) + CH(e,f,g) + Kt + Wt
    T2 = E0(a) + MAj(a,b,c) h = g
    g = f
    f = e
    e = d + T1
    d = c
    c = b
    b = a
    a = T1 + T2

  • 計算中間散列值

    H(i)0 = a + H(i-1)0

    H(i)1 = b + H(i-1)1

    H(i)2 = c + H(i-1)2

    H(i)3 = d + H(i-1)3

    H(i)4 = e + H(i-1)4

    H(i)5 = f + H(i-1)5

    H(i)6 = g + H(i-1)6

    H(i)7 = h + H(i-1)7

在對全部消息分組完成上述計算以後,計算最終輸出。對於SHA-256,是全部H(N)0、H(N)1到H(N)7的串聯。對於SHA-224,則是H(N)0、H(N)1直到H(N)6的串聯。

C++的代碼實現:

借鑑一份別人寫好的C++實現(下方已標明引用地址)

//SHA-256
/*理解算法最重要,最好本身動手實現試試看,可使用MFC寫一個簡單的交互界面*/
 
#include <iostream> 
#include <cstdio>
#include <cstdlib>
 
using namespace std;
 
#define SHA256_ROTR(a,b) (((a>>(32-b))&(0x7fffffff>>(31-b)))|(a<<b))
#define SHA256_SR(a,b) ((a>>b)&(0x7fffffff>>(b-1)))
#define SHA256_Ch(x,y,z) ((x&y)^((~x)&z))
#define SHA256_Maj(x,y,z) ((x&y)^(x&z)^(y&z))
#define SHA256_E0(x) (SHA256_ROTR(x,30)^SHA256_ROTR(x,19)^SHA256_ROTR(x,10))
#define SHA256_E1(x) (SHA256_ROTR(x,26)^SHA256_ROTR(x,21)^SHA256_ROTR(x,7))
#define SHA256_Q0(x) (SHA256_ROTR(x,25)^SHA256_ROTR(x,14)^SHA256_SR(x,3))
#define SHA256_Q1(x) (SHA256_ROTR(x,15)^SHA256_ROTR(x,13)^SHA256_SR(x,10))
char* StrSHA256(const char* str, long long length, char* sha256){
    char *pp, *ppend;
    long l, i, W[64], T1, T2, A, B, C, D, E, F, G, H, H0, H1, H2, H3, H4, H5, H6, H7;
    H0 = 0x6a09e667, H1 = 0xbb67ae85, H2 = 0x3c6ef372, H3 = 0xa54ff53a;
    H4 = 0x510e527f, H5 = 0x9b05688c, H6 = 0x1f83d9ab, H7 = 0x5be0cd19;
    long K[64] = {
        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
        0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
        0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
        0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
        0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
        0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
    };
    l = length + ((length % 64 > 56) ? (128 - length % 64) : (64 - length % 64));
    if (!(pp = (char*)malloc((unsigned long)l))) return 0;
    for (i = 0; i < length; pp[i + 3 - 2 * (i % 4)] = str[i], i++);
    for (pp[i + 3 - 2 * (i % 4)] = 128, i++; i < l; pp[i + 3 - 2 * (i % 4)] = 0, i++);
    *((long*)(pp + l - 4)) = length << 3;
    *((long*)(pp + l - 8)) = length >> 29;
    for (ppend = pp + l; pp < ppend; pp += 64){
        for (i = 0; i < 16; W[i] = ((long*)pp)[i], i++);
        for (i = 16; i < 64; W[i] = (SHA256_Q1(W[i - 2]) + W[i - 7] + SHA256_Q0(W[i - 15]) + W[i - 16]), i++);
        A = H0, B = H1, C = H2, D = H3, E = H4, F = H5, G = H6, H = H7;
        for (i = 0; i < 64; i++){
            T1 = H + SHA256_E1(E) + SHA256_Ch(E, F, G) + K[i] + W[i];
            T2 = SHA256_E0(A) + SHA256_Maj(A, B, C);
            H = G, G = F, F = E, E = D + T1, D = C, C = B, B = A, A = T1 + T2;
        }
        H0 += A, H1 += B, H2 += C, H3 += D, H4 += E, H5 += F, H6 += G, H7 += H;
    }
    free(pp - l);
    sprintf(sha256, "%08X%08X%08X%08X%08X%08X%08X%08X", H0, H1, H2, H3, H4, H5, H6, H7);
    return sha256;
}
char* FileSHA256(const char* file, char* sha256){
 
    FILE* fh;
    char* addlp, T[64];
    long addlsize, j, W[64], T1, T2, A, B, C, D, E, F, G, H, H0, H1, H2, H3, H4, H5, H6, H7;
    long long length, i, cpys;
    void *pp, *ppend;
    H0 = 0x6a09e667, H1 = 0xbb67ae85, H2 = 0x3c6ef372, H3 = 0xa54ff53a;
    H4 = 0x510e527f, H5 = 0x9b05688c, H6 = 0x1f83d9ab, H7 = 0x5be0cd19;
    long K[64] = {
        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
        0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
        0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
        0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
        0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
        0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
    };
    fh = fopen(file, "rb");
    fseek(fh, 0, SEEK_END);
    length = _ftelli64(fh);
    addlsize = (56 - length % 64 > 0) ? (64) : (128);
    if (!(addlp = (char*)malloc(addlsize))) return 0;
    cpys = ((length - (56 - length % 64)) > 0) ? (length - length % 64) : (0);
    j = (long)(length - cpys);
    if (!(pp = (char*)malloc(j))) return 0;
    fseek(fh, -j, SEEK_END);
    fread(pp, 1, j, fh);
    for (i = 0; i < j; addlp[i + 3 - 2 * (i % 4)] = ((char*)pp)[i], i++);
    free(pp);
    for (addlp[i + 3 - 2 * (i % 4)] = 128, i++; i < addlsize; addlp[i + 3 - 2 * (i % 4)] = 0, i++);
    *((long*)(addlp + addlsize - 4)) = length << 3;
    *((long*)(addlp + addlsize - 8)) = length >> 29;
    for (rewind(fh); 64 == fread(W, 1, 64, fh);){
        for (i = 0; i < 64; T[i + 3 - 2 * (i % 4)] = ((char*)W)[i], i++);
        for (i = 0; i < 16; W[i] = ((long*)T)[i], i++);
        for (i = 16; i < 64; W[i] = (SHA256_Q1(W[i - 2]) + W[i - 7] + SHA256_Q0(W[i - 15]) + W[i - 16]), i++);
        A = H0, B = H1, C = H2, D = H3, E = H4, F = H5, G = H6, H = H7;
        for (i = 0; i < 64; i++){
            T1 = H + SHA256_E1(E) + SHA256_Ch(E, F, G) + K[i] + W[i];
            T2 = SHA256_E0(A) + SHA256_Maj(A, B, C);
            H = G, G = F, F = E, E = D + T1, D = C, C = B, B = A, A = T1 + T2;
        }
        H0 += A, H1 += B, H2 += C, H3 += D, H4 += E, H5 += F, H6 += G, H7 += H;
    }
    for (pp = addlp, ppend = addlp + addlsize; pp < ppend; pp = (long*)pp + 16){
        for (i = 0; i < 16; W[i] = ((long*)pp)[i], i++);
        for (i = 16; i < 64; W[i] = (SHA256_Q1(W[i - 2]) + W[i - 7] + SHA256_Q0(W[i - 15]) + W[i - 16]), i++);
        A = H0, B = H1, C = H2, D = H3, E = H4, F = H5, G = H6, H = H7;
        for (i = 0; i < 64; i++){
            T1 = H + SHA256_E1(E) + SHA256_Ch(E, F, G) + K[i] + W[i];
            T2 = SHA256_E0(A) + SHA256_Maj(A, B, C);
            H = G, G = F, F = E, E = D + T1, D = C, C = B, B = A, A = T1 + T2;
        }
        H0 += A, H1 += B, H2 += C, H3 += D, H4 += E, H5 += F, H6 += G, H7 += H;
    }
    free(addlp); fclose(fh);
    sprintf(sha256, "%08X%08X%08X%08X%08X%08X%08X%08X", H0, H1, H2, H3, H4, H5, H6, H7);
    return sha256;
}
 
char* StrSHA256(const char* str, long long length, char* sha256);
 
int main(void){
    char text[256];
    cout<<"請輸入原文:\n" ;
    while(cin>>text) 
    {
        cout<<"請輸入原文:\n" ;
        char sha256[256];
        StrSHA256(text,sizeof(text)-1,sha256);  // sizeof()包含了末尾的終止符'\0'故 -1
        cout<<"執行SHA-256算法後的結果以下:\n";
        puts(sha256);
        
    }
    
    system("pause");
    return 0;
}
複製代碼

參考資料:

百度百科

zh.wikipedia.org/wiki/SHA-2

www.cnblogs.com/chars/p/498…

blog.csdn.net/u011583927/…

blog.csdn.net/cbacq/artic…

(注:如有什麼地方闡述有誤,敬請指正。)

相關文章
相關標籤/搜索