密码学那些事———SHA-512及其C++实现

SHA-512及其C++实现

转载请注明出处

一、引言

  相信大家对于哈希压缩加密算法应该不陌生,在我们用微信或者支付宝接口的时候经常会遇到用这类算法加密,以验证数据的完整性。可以说这类算法无处不在,那这些算法的原理是什么呢?

今天我们以SHA-512为例来说明。

二、简单介绍

  SHA (Secure Hash Algorithm,译作安全散列算法) 是美国国家安全局 (NSA) 设计,美国国家标准与技术研究院 (NIST) 发布的一系列密码散列函数。我们将要介绍的SHA-512就是SHA2系列的一种,到目前为止,SHA系列已经发展到SHA3,

其中SHA1早在2005年就被证明是不安全的,已经有了破解的办法,谷歌也在很多年前就不再使用SHA1,当前主流的是SHA2。

下图是一些简单的介绍。(转自维基)

公式:

h = Hash(message)

Hash:哈希函数

message:不超过最大消息长度的任意长度消息

h: 相应位数的密文

安全哈希算法是一种超损压缩:将一个非常大的数据压缩到固定位长的数据,因而这是一个不可逆的算法。

为什么不可逆?

  在不知算法具体流程的情况下,我们来考虑类似的问题。

  123456789abc  => 123 请问给你123,你怎么推出原字符是什么?

  同时你会说一个函数那我们找它逆函数,再映射回去不就好了?那么好我告诉你我们的原函数是:截断后面的bits,只留下开头的3个。那么你现在可以帮我找到原消息了吗?

  显然这样有2^n种可能。因而我们认为安全哈希函数是不可逆的。这也就可以保证我们的安全行了。

  

三、算法描述

  一、处理原文

    1、消息bits(二进制)化,将消息化为二进制(不一定直接一次性转为二进制,在后面我贴的代码里是动态转换的)。

    2、填充字长:

      (在我们后面的处理过程中,都是以1024bits(128B)为一次操作的最小单位的,因而消息得满足128B对齐)

896 = length(message)%1024

             也就是满足填充后消息长度(bits)除1024取余数等于896(1024-128=896)

    3、填充

       第一位填充1,其他位填充0,满足除余为896后,还剩下128bits.这个用于存消息长度。(SHA512中为128位,因而此算法最长消息为2^128-1)

  二、设置初始值

    SHA512算法的结果长度为512位,按每组64位分成8组,这8组结果是由8个初始值A,B,C,D,E,F,G,H经过不断演变得到的。这8个初始值是:

        A = 0x6a09e667f3bcc908ULL;
        B = 0xbb67ae8584caa73bULL;
        C = 0x3c6ef372fe94f82bULL;
        D = 0xa54ff53a5f1d36f1ULL;
        E = 0x510e527fade682d1ULL;
        F = 0x9b05688c2b3e6c1fULL;
        G = 0x1f83d9abfb41bd6bULL;
        H = 0x5be0cd19137e2179ULL;

  三、循环加工

    (这就是算法最核心的地方,我们形象的把它称为哈希工厂)

     下面我们看图说话

     

    图中A-H哈希的8个分组,每次循环从旧的中产生新的,一共得循环多少次呢?

    主循环次数 = 消息长度/1024

    每次主循环中又保存80次子循环

    上图就是表达了单次子循环的流程

    主要操作

        

        

        

        

        >>>表示循环右移

        田:加法

        对应C语言表达式子:

          #define Ch( x, y, z )     (z ^ (x & (y ^ z)))
          #define Maj(x, y, z )     (((x | y) & z) | (x & y))
          #define S( x, n )         ROR64( x, n )
          #define R( x, n )         (((x)&0xFFFFFFFFFFFFFFFFULL)>>((unsigned long long)n))
          #define Sigma0( x )       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
          #define Sigma1( x )       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
          #define Gamma0( x )       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
          #define Gamma1( x )       (S(x, 19) ^ S(x, 61) ^ R(x, 6))

      W,K是两个常量

      其中W是计算出的,具体计算看代码。

      K:预先给出的80个常量

      相信大家有了上图后已经明白了核心操作。要是还不明白建议根据附录中的代码进一步理解。    

    四、拼接结果

      将最后的8个常量一次拼接则得到结果。

 

C语言实现(C++)

SHA512.h

#ifndef SHA512_H
#define SHA512_H

//////////////////////////////////////////////////////////
// SHA512_CB(control block)                             //
// SHA512_CB:SHA512控制块,包含算法运算过程中将用到的信息//
// count[2]:记录128位的数字长度(两个64位)             //
// state[8]:A-H八个初始常量(64bit)                      //
// buffer[128]:用于每次运算的1024bit                      //
//                                                      //
//////////////////////////////////////////////////////////
typedef struct
{
    unsigned long long count[2];
    unsigned long long state[8];
    unsigned char buffer[128];
} SHA512_CB;

// 用于补齐的数,最多补128字节也就是1024bit
unsigned char PADDING[] = {
    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

// 每次子循环中用到的常量
// 后面加ULL表示long long
static const unsigned long long K[80] = {
    0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
    0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
    0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
    0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
    0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
    0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
    0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
    0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
    0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
    0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
    0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
    0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
    0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
    0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
    0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
    0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
    0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
    0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
    0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
    0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
};
// 初始化函数,初始化SHA_CB的各个值
void SHA512Init(SHA512_CB *context);

// 将数据加入
void SHA512Update(SHA512_CB *context, unsigned char *input, unsigned long long inputlen);

// 处理完最后再调用,这个处理尾数
void SHA512Final(SHA512_CB *context, unsigned char digest[32]);

// 加密处理函数:Hash加密的核心工厂
void SHA512Transform(unsigned long long state[8], unsigned char block[128]);

// 编码函数:将整型编码转为字符
void SHA512Encode(unsigned char *output, unsigned long long *input, unsigned long long len);

// 解码函数:将字符数组保存的编码转为整型
void SHA512Decode(unsigned long long *output, unsigned char *input, unsigned long long len);
#endif

 

SHA512.c

 

#include <memory.h>
#include <string.h>
#include <stdio.h>
#include "SHA512.h"



// 循环右移(64位)
#define ROR64( value, bits ) (((value) >> (bits)) | ((value) << (64 - (bits))))

//////////////////////////////////////////////////////
//                                                    //
// Ch,:Maj操作                                        //
// S:循环右移    R:同2**128除余右移                    //
// Sigma0:Sigma0函数                                //
// Sigma1:Sigma2函数                                //
// Gamma0:Gamma0函数                                //
// Gamma1:Gamma1函数                                //
//////////////////////////////////////////////////////

#define Ch( x, y, z )     (z ^ (x & (y ^ z)))
#define Maj(x, y, z )     (((x | y) & z) | (x & y))
#define S( x, n )         ROR64( x, n )
#define R( x, n )         (((x)&0xFFFFFFFFFFFFFFFFULL)>>((unsigned long long)n))
#define Sigma0( x )       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
#define Sigma1( x )       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
#define Gamma0( x )       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
#define Gamma1( x )       (S(x, 19) ^ S(x, 61) ^ R(x, 6))

#define Sha512Round( a, b, c, d, e, f, g, h, i )       \
     t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];   \
     t1 = Sigma0(a) + Maj(a, b, c);                    \
     d += t0;                                          \
     h  = t0 + t1;

void SHA512Init(SHA512_CB *context) {
    context->count[0] = 0;
    context->count[1] = 0;
    context->state[0] = 0x6a09e667f3bcc908ULL;
    context->state[1] = 0xbb67ae8584caa73bULL;
    context->state[2] = 0x3c6ef372fe94f82bULL;
    context->state[3] = 0xa54ff53a5f1d36f1ULL;
    context->state[4] = 0x510e527fade682d1ULL;
    context->state[5] = 0x9b05688c2b3e6c1fULL;
    context->state[6] = 0x1f83d9abfb41bd6bULL;
    context->state[7] = 0x5be0cd19137e2179ULL;
}

void SHA512Update(SHA512_CB *context, unsigned char *input, unsigned long long inputlen) {
    unsigned long long index = 0, partlen = 0, i = 0; // i记录input的当前位置(初始为0)
    index = (context->count[1] >> 3) & 0x7F;  //index:总字长除127(11111111)取余后的余数
    partlen = 128 - index;  //partlen:同128相差的长度
    context->count[1] += inputlen << 3;  //更新count

    // 统计字符的bit长度,如果小于说明类型溢出了(64bit)无法装下了
    // 由于最后留下128bit填充字符长度,因而必须引入count[1]保存
    // 64bit+64bit=128bit
    if (context->count[1] < (inputlen << 3))
        context->count[0]++;
    //右移动61位后就是count[0]应该记录的值。(左移3位,溢出的就是右移动61位的)
    context->count[0] += inputlen >> 61;

    //////////////////////////////////////////////////////////
    //                                                        //
    //     如果此次更新的长度,大于原长度同128做差的值,    //
    //     .ie. 加上刚更新的长度满足了128Bytes(1024位)      //
    //     因而可以进行一次加密循环                            //
    //                                                        //
    //////////////////////////////////////////////////////////

    if (inputlen >= partlen)
    {
        //将缺的partlen个字节数据加入缓冲区
        memcpy(&context->buffer[index], input, partlen);
        SHA512Transform(context->state, context->buffer);

        // 如果输入的字,还可以进行(还有整128字的)就继续进行一次加密循环

        for (i = partlen; i + 128 <= inputlen; i += 128)
            SHA512Transform(context->state, &input[i]);
        // 将当前位置设为0
        index = 0;
    }
    else
    {
        i = 0;
    }
    // 重新设置buffer区(处理过的字被覆盖成新字)
    memcpy(&context->buffer[index], &input[i], inputlen - i);
}

void SHA512Final(SHA512_CB *context, unsigned char digest[64]) {
    unsigned int index = 0, padlen = 0;
    unsigned char bits[16]; // 记录字长信息
    index = (context->count[1] >> 3) & 0x7F; // 字长除127(11111111)取余长度
    padlen = (index < 112) ? (112 - index) : (240 - index); // 补齐的字长
    SHA512Encode(bits, context->count, 16);
    SHA512Update(context, PADDING, padlen);
    SHA512Update(context, bits, 16);
    SHA512Encode(digest, context->state, 64);
}

void SHA512Encode(unsigned char *output, unsigned long long *input, unsigned long long len) {
    unsigned long long i = 0, j = 0;
    while (j < len)
    {
        output[j+7] = input[i] & 0xFF;
        output[j + 6] = (input[i] >> 8) & 0xFF; //0xFF:11111111
        output[j + 5] = (input[i] >> 16) & 0xFF;
        output[j + 4] = (input[i] >> 24) & 0xFF;
        output[j + 3] = (input[i] >> 32) & 0xFF;
        output[j + 2] = (input[i] >> 40) & 0xFF;
        output[j + 1] = (input[i] >> 48) & 0xFF;
        output[j] = (input[i] >> 56) & 0xFF;
        i++;
        j += 8;
    }
}


void SHA512Decode(unsigned long long *output, unsigned char *input, unsigned long long len) {
    unsigned long long i = 0, j = 0;
    while (j < len)
    {
        output[i] = ((unsigned long long)input[j+7]) |
                    ((unsigned long long)input[j + 6] << 8) |
                    ((unsigned long long)input[j + 5] << 16) |
                    ((unsigned long long)input[j + 4] << 24) |
                    ((unsigned long long)input[j + 3] << 32) |
                    ((unsigned long long)input[j + 2] << 40) |
                    ((unsigned long long)input[j + 1] << 48) |
                    ((unsigned long long)input[j] << 56);
        i++;
        j += 8;
    }
}

void SHA512Transform(unsigned long long state[8], unsigned char block[128]) {
    unsigned long long S[8];
    unsigned long long W[80];
    unsigned long long t0;
    unsigned long long t1;
    int i = 0;
    printf("\n填充后(1024bits):\n0x");
    for(int index=0;index<128;index++){
        printf("%02x", block[index]);
    }
    printf("\n");
    // 把state的值复制给S
    for ( i = 0; i < 8; i++ )
    {
        S[i] = state[i];
    }

    // 将字符数组保存的编码转为unsigned long long
    SHA512Decode(W, block, 128);

    for ( i = 16; i < 80; i++ )
    {
        W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
    }

    for ( i = 0; i < 80; i += 8 )
    {
        Sha512Round(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i + 0);
        Sha512Round(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], i + 1);
        Sha512Round(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], i + 2);
        Sha512Round(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], i + 3);
        Sha512Round(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], i + 4);
        Sha512Round(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], i + 5);
        Sha512Round(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], i + 6);
        Sha512Round(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], i + 7);
    }
    printf("\n");
    printf("A:%I64u\n", S[0]);
    printf("B:%I64u\n", S[1]);
    printf("C:%I64u\n", S[2]);
    printf("D:%I64u\n", S[3]);
    printf("E:%I64u\n", S[4]);
    printf("F:%I64u\n", S[5]);
    printf("G:%I64u\n", S[6]);
    printf("H:%I64u\n", S[7]);
    printf("\n");
    // Feedback
    for ( i = 0; i < 8; i++ )
    {
        state[i] = state[i] + S[i];
    }
}

int main(int argc, char* argv[])
{

    int i;
    unsigned char input[] = "jack";
    printf("输入字符串的十六进制: 0x");
    for(unsigned int i=0;i<strlen((char*)input);i++){
        printf("%02x", input[i]);
    }
    printf("\n");

    unsigned char sha512Code[64];

    SHA512_CB sha512;

    SHA512Init(&sha512);
    SHA512Update(&sha512, input, strlen((char *)input));
    SHA512Final(&sha512, sha512Code);

    //Md5加密后的32位结果
    printf("\n加密前:%s\n加密后128位:", input);
    for (i = 0; i < 64; i++)
    {
        printf("%02x", sha512Code[i]);
    }

    getchar();

    return 0;
}

 

 转载请注明出处。

 

posted @ 2017-11-07 23:35  coding==1?  阅读(9668)  评论(0编辑  收藏  举报