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对齐)

= 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次子循环

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

    主要操作

        

        

        

        

        >>>表示循环右移

        田:加法 ⊞ {\displaystyle \color {red}\boxplus } ⊞ {\displaystyle \color {red}\boxplus }

        对应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[];
unsigned long long state[];
unsigned char buffer[];
} SHA512_CB; // 用于补齐的数,最多补128字节也就是1024bit
unsigned char PADDING[] = {
0x80, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
}; // 每次子循环中用到的常量
// 后面加ULL表示long long
static const unsigned long long K[] = {
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[]); // 加密处理函数:Hash加密的核心工厂
void SHA512Transform(unsigned long long state[], unsigned char block[]); // 编码函数:将整型编码转为字符
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[] = ;
context->count[] = ;
context->state[] = 0x6a09e667f3bcc908ULL;
context->state[] = 0xbb67ae8584caa73bULL;
context->state[] = 0x3c6ef372fe94f82bULL;
context->state[] = 0xa54ff53a5f1d36f1ULL;
context->state[] = 0x510e527fade682d1ULL;
context->state[] = 0x9b05688c2b3e6c1fULL;
context->state[] = 0x1f83d9abfb41bd6bULL;
context->state[] = 0x5be0cd19137e2179ULL;
} void SHA512Update(SHA512_CB *context, unsigned char *input, unsigned long long inputlen) {
unsigned long long index = , partlen = , i = ; // i记录input的当前位置(初始为0)
index = (context->count[] >> ) & 0x7F; //index:总字长除127(11111111)取余后的余数
partlen = - index; //partlen:同128相差的长度
context->count[] += inputlen << ; //更新count // 统计字符的bit长度,如果小于说明类型溢出了(64bit)无法装下了
// 由于最后留下128bit填充字符长度,因而必须引入count[1]保存
// 64bit+64bit=128bit
if (context->count[] < (inputlen << ))
context->count[]++;
//右移动61位后就是count[0]应该记录的值。(左移3位,溢出的就是右移动61位的)
context->count[] += inputlen >> ; //////////////////////////////////////////////////////////
// //
// 如果此次更新的长度,大于原长度同128做差的值, //
// .ie. 加上刚更新的长度满足了128Bytes(1024位) //
// 因而可以进行一次加密循环 //
// //
////////////////////////////////////////////////////////// if (inputlen >= partlen)
{
//将缺的partlen个字节数据加入缓冲区
memcpy(&context->buffer[index], input, partlen);
SHA512Transform(context->state, context->buffer); // 如果输入的字,还可以进行(还有整128字的)就继续进行一次加密循环 for (i = partlen; i + <= inputlen; i += )
SHA512Transform(context->state, &input[i]);
// 将当前位置设为0
index = ;
}
else
{
i = ;
}
// 重新设置buffer区(处理过的字被覆盖成新字)
memcpy(&context->buffer[index], &input[i], inputlen - i);
} void SHA512Final(SHA512_CB *context, unsigned char digest[]) {
unsigned int index = , padlen = ;
unsigned char bits[]; // 记录字长信息
index = (context->count[] >> ) & 0x7F; // 字长除127(11111111)取余长度
padlen = (index < ) ? ( - index) : ( - index); // 补齐的字长
SHA512Encode(bits, context->count, );
SHA512Update(context, PADDING, padlen);
SHA512Update(context, bits, );
SHA512Encode(digest, context->state, );
} void SHA512Encode(unsigned char *output, unsigned long long *input, unsigned long long len) {
unsigned long long i = , j = ;
while (j < len)
{
output[j+] = input[i] & 0xFF;
output[j + ] = (input[i] >> ) & 0xFF; //0xFF:11111111
output[j + ] = (input[i] >> ) & 0xFF;
output[j + ] = (input[i] >> ) & 0xFF;
output[j + ] = (input[i] >> ) & 0xFF;
output[j + ] = (input[i] >> ) & 0xFF;
output[j + ] = (input[i] >> ) & 0xFF;
output[j] = (input[i] >> ) & 0xFF;
i++;
j += ;
}
} void SHA512Decode(unsigned long long *output, unsigned char *input, unsigned long long len) {
unsigned long long i = , j = ;
while (j < len)
{
output[i] = ((unsigned long long)input[j+]) |
((unsigned long long)input[j + ] << ) |
((unsigned long long)input[j + ] << ) |
((unsigned long long)input[j + ] << ) |
((unsigned long long)input[j + ] << ) |
((unsigned long long)input[j + ] << ) |
((unsigned long long)input[j + ] << ) |
((unsigned long long)input[j] << );
i++;
j += ;
}
} void SHA512Transform(unsigned long long state[], unsigned char block[]) {
unsigned long long S[];
unsigned long long W[];
unsigned long long t0;
unsigned long long t1;
int i = ;
printf("\n填充后(1024bits):\n0x");
for(int index=;index<;index++){
printf("%02x", block[index]);
}
printf("\n");
// 把state的值复制给S
for ( i = ; i < ; i++ )
{
S[i] = state[i];
} // 将字符数组保存的编码转为unsigned long long
SHA512Decode(W, block, ); for ( i = ; i < ; i++ )
{
W[i] = Gamma1(W[i - ]) + W[i - ] + Gamma0(W[i - ]) + W[i - ];
} for ( i = ; i < ; i += )
{
Sha512Round(S[], S[], S[], S[], S[], S[], S[], S[], i + );
Sha512Round(S[], S[], S[], S[], S[], S[], S[], S[], i + );
Sha512Round(S[], S[], S[], S[], S[], S[], S[], S[], i + );
Sha512Round(S[], S[], S[], S[], S[], S[], S[], S[], i + );
Sha512Round(S[], S[], S[], S[], S[], S[], S[], S[], i + );
Sha512Round(S[], S[], S[], S[], S[], S[], S[], S[], i + );
Sha512Round(S[], S[], S[], S[], S[], S[], S[], S[], i + );
Sha512Round(S[], S[], S[], S[], S[], S[], S[], S[], i + );
}
printf("\n");
printf("A:%I64u\n", S[]);
printf("B:%I64u\n", S[]);
printf("C:%I64u\n", S[]);
printf("D:%I64u\n", S[]);
printf("E:%I64u\n", S[]);
printf("F:%I64u\n", S[]);
printf("G:%I64u\n", S[]);
printf("H:%I64u\n", S[]);
printf("\n");
// Feedback
for ( i = ; i < ; 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=;i<strlen((char*)input);i++){
printf("%02x", input[i]);
}
printf("\n"); unsigned char sha512Code[]; SHA512_CB sha512; SHA512Init(&sha512);
SHA512Update(&sha512, input, strlen((char *)input));
SHA512Final(&sha512, sha512Code); //Md5加密后的32位结果
printf("\n加密前:%s\n加密后128位:", input);
for (i = ; i < ; i++)
{
printf("%02x", sha512Code[i]);
} getchar(); return ;
}

转载请注明出处。

密码学那些事———SHA-512及其C++实现的更多相关文章

  1. day5-hashlib模块

    一.概述 在程序开发过程中,很多时候会涉及用户信息验证环节,这类场景下我们往往需要对字符串进行加密处理.python中也有专门的加密模块,它就是hashlib.下面章节将详述它的常见用法. 二.常见加 ...

  2. JavaScript简明教程之Node.js

    Node.js是目前非常火热的技术,但是它的诞生经历却很奇特. 众所周知,在Netscape设计出JavaScript后的短短几个月,JavaScript事实上已经是前端开发的唯一标准. 后来,微软通 ...

  3. 设置grub密码

    一,明文加密的方法 vi /etc/grub.conf 在hiddenmenu下添加password=1234,保存退出. 二,密文加密的方法 2.1, 使用SHA加密方式.grub-crypt  回 ...

  4. Apache Kafka: 优化部署的10个最佳实践

    原文作者:Ben Bromhead      译者:江玮 原文地址:https://www.infoq.com/articles/apache-kafka-best-practices-to-opti ...

  5. Linux基础命令---管理组gpasswd

    gpasswd gpasswd指令用来管理组文件“/etc/group”和“/etc/gshadow”,每个组可以设置管理员.组员.密码.系统管理员可以使用-A选项定义组管理员,使用-M选项定义成员. ...

  6. [简单到爆]eclipse-jee-neon的下载和安装

    Eclipse的下载安装: 访问https://www.eclipse.org/downloads/eclipse-packages/ 选择Eclipse IDE for Java EE Develo ...

  7. Apache Kafka 学习笔记

    1. 介绍Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写.Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据. 这种动 ...

  8. 密码学系列之:NIST和SHA算法

    目录 简介 SHA1 SHA2 SHA3 简介 SHA算法大家应该都很熟悉了,它是一个用来计算hash的算法,目前的SHA算法有SHA1,SHA2和SHA3种.这三种算法都是由美国NIST制定的. N ...

  9. [区块链] 密码学中Hash算法(基础)

    在介绍Hash算法之前,先给大家来个数据结构中对hash表(散列表)的简单解释,然后我再逐步深入,讲解一下hash算法. 一.Hash原理——基础篇 1.1 概念 哈希表就是一种以 键-值(key-i ...

随机推荐

  1. pdf点击超链接后返回:alt+ 向左 /向右

    pdf点击超链接后返回:alt+ 向左 /向右

  2. 渣渣学QT

    初学QT,自己的一些小总结,错误之处,望大神指点一二: 1,添加资源文件时想应用在界面的背景,但是发现用不了,后来才知道是没有"构建"?应该是要在构建之后才将所添加的资源文件真正的 ...

  3. 前端框架——AngularJS

      前  言 AngularJS是一款为了克服HTML在构建应用上的不足而设计的优秀的前端JS框架.AngularJS有着诸多特性,最为核心的是:MVC.模块化.自动化双向数据绑定.语义化标签.依赖注 ...

  4. 读书笔记之宿舍共享wifi

    若有某方面侵权,请邮件1047697114@qq.com,一个工作日即可处理,谢谢   目录一.简单安装虚拟机二.简单设置,开热点! 我没试过那些wifi软件之类的,以下是个人测试的过程 一.简单安装 ...

  5. 一个强迫症的Git 选择

    选择 1,经常性的commit or 干净的历史 在本地(私有)的开发分支中,选择经常性的commit,以便于实时记录修改,回退等操作.eg.develop,feature... 实现方式: comm ...

  6. KM算法新识

    看了很多写的好的文章,但是针对代码注释来讲,这篇文章最合适.                                 如果人生会有很长,愿你的荣耀永不散场--wenr大牛. #include ...

  7. java web jsp学习笔记--概述-常用语法,指令,动作元素,隐式对象,域对象

     JSP学习笔记 1.什么是jsp JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术.JSP/Servlet规范.JS ...

  8. Rx 入门指引 (一)

    自学 Rx 快有一个周了, 它非常适合处理复杂的异步场景.结合自己所学,决定写系列教程. 我认为, Rx 中强大的地方在于两处 管道思想,通过管道,我们订阅了数据的来源,并在数据源更新时响应 . 强大 ...

  9. 浅谈oracle树状结构层级查询测试数据

    浅谈oracle树状结构层级查询 oracle树状结构查询即层次递归查询,是sql语句经常用到的,在实际开发中组织结构实现及其层次化实现功能也是经常遇到的,虽然我是一个java程序开发者,我一直觉得只 ...

  10. win10 uwp 获得元素绝对坐标

    有时候需要获得一个元素,相对窗口的坐标,在修改他的位置可以使用. 那么 UWP 如何获得元素坐标? 我提供了一个方法,可以获得元素的坐标. 首先需要获得元素,如果没有获得元素,那么如何得到他的坐标? ...