数据加密 ---- SHA-2 加密
接 上一篇 博文
4. SHA-2
SHA-2,名称来自于安全散列算法2(英语:Secure Hash Algorithm 2)的缩写,一种密码散列函数(见MD5算法)算法标准,由美国国家安全局研发,由美国国家标准与技术研究院(NIST)在2001年发布。属于SHA算法之一,是SHA-1的后继者。其下又可再分为六个不同的算法标准,包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。
4.1 简介
NIST发布了三个额外的SHA变体,这三个函数都将消息对应到更长的消息摘要。以它们的摘要长度(以比特计算)加在原名后面来命名:SHA-256,SHA-384和SHA-512。它们发布于2001年的FIPS PUB 180-2草稿中,随即通过审查和评论。包含SHA-1的FIPS PUB 180-2,于2002年以官方标准发布。2004年2月,发布了一次FIPS PUB 180-2的变更通知,加入了一个额外的变种SHA-224,这是为了匹配双密钥3DES所需的密钥长度而定义。
SHA-256和SHA-512是很新的散列函数,SHA-224以及SHA-384则是前述二种散列函数的截短版,利用不同的初始值做计算。
这些新的散列函数并没有接受像SHA-1一样的公众密码社群做详细的检验,所以它们的密码安全性还不被大家广泛的信任。Gilbert和Handschuh在2003年曾对这些新变种作过一些研究,声称他们没有找到弱点。
我们大概可以将SHA-2分为两类,两类使用不同的偏移量、不同的敞亮、不同的循环次数、不同的数据长度。但是两类的运算结构是相同的。
- SHA-256 和 SHA-224
其中SHA-224是SHA-256 的截短版,运算的数据都是32位(4字节),核心循环次数为64次。
- SHA-512 和 SHA-384
其中SHA-384是SHA-512 的截短版,运算的数据都是64位(8字节),核心循环次数为80次。
在下面一节会继续介绍两类运算的不同,以及相同类之间的两种算法差异。
4.2 SHA-256 算法
上面一节说过,这几种算法的结构是相同的,所以,这里使用SHA-256 算法来解释下运算结构。
1、扩充数据
同MD5 算法、SHA-1 算法,第一步还是要将数据填充为512 bits 的整数倍,也就是64字节的整数倍。这些n 段512bits(64字节)的数据会作为原始信息进行处理
2、循环运算每一段512bits(64 字节)的数据(MainLoop)
- 将512bits数据(16*4字节)扩展为64*4字节
对于SHA-256 算法是64次循环,会将16 * 4 字节(32位)扩展到64 * 4字节(32位)。
相比,SHA-512 算法是80次循环,所以会将16 * 8字节(64位)扩展到80 * 8字节(64位)。
下面是SHA-256 扩展的伪代码:
Extend the sixteen 32-bit words into sixty-four 32-bit words:for i from 16 to 63s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor(w[i-15] rightshift 3)s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor(w[i-2] rightshift 10)w[i] := w[i-16] + s0 + w[i-7] + s1
从第 17个开始进行扩展,用C 代码表示为:
#define shift(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
#define S0(x) ((shift(x, 25)) ^ (shift(x, 14)) ^ ((x) >> 3))
#define S1(x) ((shift(x, 17)) ^ (shift(x, 19)) ^ ((x) >> 10))...String getSHA1() {......for(unsigned int i=0;i
- 进入主循环,进行64次循环
这里的64次循环跟MD5 算法、SHA-1 算法 有一些区别,这里不会再分几组,而是64次都是相同的操作。
但是还是需要4个函数:
Sigma0(x)=shift(x, 30) ⊕ shift(x, 19) ⊕ shift(x, 10);
Sigma1(x)=shift(x, 26) ⊕ shift(x, 21) ⊕ shift(x, 7);
Maj(x,y,z)=(x & y) ⊕ (x & z) ⊕ (y & z);
Ch(x,y,z)=(x & y) ⊕ (~x & z);
其中,& 是与运算, | 是或运算,~ 是取反运算,⊕ 是异或运算
具体的运算流程如下图所示:

A、B、C、D 、E、F、G、H分别是上一段512bits 处理后留下来的8个整数(第一次运算的时候这8个数为固定的常数)。
在对该512bits 数据运算前需要先把这8个整数临时存起来(后面会使用到)。
表示一个32 bits(4个字节) 的输入数据(512bits 数据其中的32bits),
表示一个32bits 的常数(这个也是固定的)。
将上图运算总结成公式:
s0 = Sigma0(A);
maj = Ma(A, B, C);
s1 = Sigma1(E);
ch = Ch(E, F, G);
t1 = H + s1 + ch + k[i] + M[i];
H = G;
G = F;
F = E;
E = D + t1;
D = C;
C = B;
B = A;
A = t1 + s0 + maj;
将每一组运算后得到最新的8个数A、B、C、D、E、F、G、H作为下一组运算的初始值,一直到64 次循环彻底结束。
- 一段512bits 的64 次循环结束之后,需要为下一段512bits 准备新的A、B、C、D、E、F、G、H。
将上一段64 次循环后最终得到的A、B、C、D、E、F、G、H(也就是上面一步得到的最后的8个数)与循环之前的保存下来的初始值对应相加。
3、最后一段512bits 运算后得到最终的A、B、C、D、E,即为最终的160bits数
因为需要得到最后256bits(64 位16进制)的字符串,所以要将每个4字节的数转换成8位的16进制字符串。
注意:
1、SHA-224 与SHA-256基本相同,除了:
- 8个初始值不同
- SHA-224 输出时截掉H 值,得到最后的56位16进制字符串
2、SHA-512 与SHA-256结构是相同的,但:
- SHA-512所有数都是64位
- SHA-512运行80次循环
- SHA-512的初始值、常量值都是64位
- SHA-512中偏移量和循环中的位移量也是不同的
下面是截取的SHA-512中 几个函数:
#ifndef ROTR
#define ROTR(x, s) (((x) >> s) | (x) << (64 - s))
#endif#define S0(x) (ROTR((x), 1) ^ ROTR((x), 8) ^ ((x) >> 7))
#define S1(x) (ROTR((x), 19) ^ ROTR((x), 61) ^ ((x) >> 6))#define Sigma0(x) (ROTR((x), 28) ^ ROTR((x), 34) ^ ROTR((x), 39))
#define Sigma1(x) (ROTR((x), 14) ^ ROTR((x), 18) ^ ROTR((x), 41))
#define Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
3、SHA-384和SHA-512基本上是相同的,除了:
8个初始值不同
SHA-384输出时截掉F、H两个值,得到最后的96位16进制字符串
4.3 散列举例
一般128位的SHA-256 散列被表示为64位十六进制数字。以下是一个43位长的仅ASCII字母列的SHA-256散列:
#include
#include
using namespace std;
#define shift(x, n) (((x) << (n)) | ((x) >> (32-(n))))//右移的时候,高位一定要补零,而不是补充符号位
#define S0(x) ((shift(x, 25)) ^ (shift(x, 14)) ^ ((x) >> 3))
#define S1(x) ((shift(x, 17)) ^ (shift(x, 19)) ^ ((x) >> 10))#define Sigma0(x) ((shift(x, 30)) ^ (shift(x, 19)) ^ (shift(x, 10)))
#define Sigma1(x) ((shift(x, 26)) ^ (shift(x, 21)) ^ (shift(x, 7)))
#define Ma(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define Ch(x,y,z) (((x) & (y)) ^ ((~x) & (z)))#define A 0x6a09e667
#define B 0xbb67ae85
#define C 0x3c6ef372
#define D 0xa54ff53a
#define E 0x510e527f
#define F 0x9b05688c
#define G 0x1f83d9ab
#define H 0x5be0cd19//strBaye的长度
unsigned int strlength;
//A,B,C,D的临时变量
unsigned int atemp;
unsigned int btemp;
unsigned int ctemp;
unsigned int dtemp;
unsigned int etemp;
unsigned int ftemp;
unsigned int gtemp;
unsigned int htemp;static const uint32_t 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};const char str16[]="0123456789abcdef";
void mainLoop(unsigned int M[])
{unsigned int s0, maj, s1, ch, t;unsigned int a = atemp;unsigned int b = btemp;unsigned int c = ctemp;unsigned int d = dtemp;unsigned int e = etemp;unsigned int f = ftemp;unsigned int g = gtemp;unsigned int h = htemp;for (unsigned int i = 0; i < 64; i++){s0 = Sigma0(a);maj = Ma(a, b, c);s1 = Sigma1(e);ch = Ch(e, f, g);t1 = h + s1 + ch + k[i] + M[i];h = g;g = f;f = e;e = d + t1;d = c;c = b;b = a;a = t1 + s0 + maj;}atemp=a+atemp;btemp=b+btemp;ctemp=c+ctemp;dtemp=d+dtemp;etemp=e+etemp;ftemp=f+ftemp;gtemp=g+gtemp;htemp=h+htemp;
}
/*
*填充函数
*处理后应满足bits≡448(mod512),字节就是bytes≡56(mode64)
*填充方式为先加一个1,其它位补零
*最后加上64位的原来长度
*/
unsigned int* add(string str)
{unsigned int num=((str.length()+8)/64)+1;//以512位,64个字节为一组unsigned int *strByte=new unsigned int[num*16]; //64/4=16,所以有16个整数strlength=num*16;for (unsigned int i = 0; i < num*16; i++)strByte[i]=0;for (unsigned int i=0; i >2]|=(str[i])<<((i%4)*8);//一个整数存储四个字节,i>>2表示i/4 一个unsigned int对应4个字节,保存4个字符信息}strByte[str.length()>>2]|=0x80<<(((str.length()%4))*8);//尾部添加1 一个unsigned int保存4个字符信息,所以用128左移/**添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个,这里长度只用了32位*/strByte[num*16-2]=str.length()*8;return strByte;
}
string changeHex(int a)
{int b;string str1;string str="";for(int i=0;i<4;i++){str1="";b=((a>>i*8)%(1<<8))&0xff; //逆序处理每个字节for (int j = 0; j < 2; j++){str1.insert(0,1,str16[b%16]);b=b/16;}str+=str1;}return str;
}
string getSHA1(string source)
{atemp=A; //初始化btemp=B;ctemp=C;dtemp=D;etemp=E;ftemp=F;gtemp=G;htemp=H;unsigned int *strByte=add(source);for(unsigned int i=0;i>ss;string s=getSHA1("abc");cout<
android 中也提供了source code:external/boringssl/src/crypto/fipsmodule/sha/sha256.c
其他算法可以看:数据加密 ---- 总篇
附:
android 实例代码:https://blog.csdn.net/shift_wwx/article/details/84100407
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
