密码学那些事———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; }
转载请注明出处。