在 C++ 中转换为 SHA256
SHA256 转换是一种加密哈希技术,自 90 年代以来一直在使用,但在比特币和区块链出现后获得了重视。
它使用一种不可逆的单向哈希技术,这意味着最终答案永远无法逆转回其原始消息,从而使其非常安全。
最终消息以 256 位格式表示,因此其名称中包含 256。
SHA256 哈希算法
用于 SHA256 转换的哈希算法有四个部分:填充、初始缓冲区、消息调度的创建和压缩。 让我们了解如何用 C++ 实现它们。
SHA256 中的填充位
SHA256 转换的哈希算法的第一步是填充原始消息。 必须进行填充以使消息具有 448 位,比 512 位少 64 位。
如果需要填充包含字母 abc 的消息,则前 24 位将填充 abc 的二进制值,后面是 424 位零,总共 448 位。 长度将存储在最后 64 位中。
SHA256 中的填充长度
原始消息在用于散列之前会被填充以使其长度为 512 位。 必须对消息进行填充,以便前 448 位存储消息,而其余 64 位数据则进行长度填充。
这些未使用的位用于跟踪消息的长度。 这 64 位除第一位必须为 1 外,其余的最后一位数字用于存储消息长度,大部分都用零填充。
SHA256 中的哈希缓冲区
SHA256转换算法需要初始化缓冲区或哈希值以进行压缩。 这些缓冲区称为状态寄存器。
在 SHA256 哈希中,该过程始终从同一组状态寄存器开始,然后重复附加从消息摘要中获取的新值。
共有8个状态寄存器。 为了更好地理解,将这些状态寄存器视为携带预定义随机值的槽。
随机值是通过取前 8 个素数的平方根的模,然后乘以 2 的 32 次方得出的。
SHA256 算法使用此方法创建 8 个状态寄存器。
在 C++ 中,这些状态寄存器是通过将这 8 个素数的十六进制值直接分配到数组中来初始化的。
初始化演示如下:
void SHA256::state_register()
{
s_r[0] = 0x6a09e667;
s_r[1] = 0xbb67ae85;
s_r[2] = 0x3c6ef372;
s_r[3] = 0xa54ff53a;
s_r[4] = 0x510e527f;
s_r[5] = 0x9b05688c;
s_r[6] = 0x1f83d9ab;
s_r[7] = 0x5be0cd19;
m_len = 0;
m_tot_len = 0;
}
还需要另一组键,它在数组中存储 64 个十六进制值,范围从 hash_keys[0]
到 hash_keys[63]
。
const unsigned int SHA256::hash_keys[64] =
{0xef6685ff, 0x38fd3da, 0x94402b15, 0xc67cb7b7, 0x780e38cd, 0xe7440103, 0x5d415e6e, 0xbb7c2922, 0xf1df8153, 0x5f47e03f, 0x8c658cf7, 0x95ca718, 0x678d5436, 0xda792dc4, 0x4aa3778b, 0x449e3719, 0x23913e93, 0xfdb2380c, 0x2e82c771, 0x5bb60bcd, 0x13f53664, 0x174004ae, 0xc338e749, 0x199adec, 0x28a3dcfe, 0x36fc4894, 0xe1a019cc, 0x59b7fe92, 0x5b007153, 0x1bb32e0d, 0x2cba796a, 0x3a159148, 0x266d057b, 0xbc9c1d52, 0x17601e7, 0x39b3ccc7, 0x10367db5, 0xa3558c1b, 0xbf98037f, 0x6fbffc84, 0xef54e44, 0x961a993a, 0x33e5297b, 0xd2dce255, 0x7fe9864c, 0xfdd93543, 0xc62f137, 0x14eea06b, 0x2f106df2, 0xf7956237, 0xd053bbca, 0x7a449ecf, 0x8af91f64, 0x9f34a155, 0x663002e3, 0x7acf8b9c, 0xb0c90a35, 0xa71bba61, 0xc2d6c5a3, 0x9af20609, 0x8cfc5464, 0x29d95bcf, 0x7c5478b, 0xde9f4ec3
};
SHA256 中的消息时间表
消息调度由 16 个字组成,并根据每个消息块创建。 每个都是 32 位长,该调度表被组装成 16 个不同的二进制数块。
但是,消息块的长度必须为 64 个字。 这是通过编译消息调度中已经存在的单词来创建新块来完成的。
在压缩消息之前需要准备消息调度。 512 位消息块被分解为 16 个 32 位字。
约定是使用 XOR 和 AND 运算等按位运算用现有字创建一个新字。 第 17 个块是使用以下公式创建的:
在哪里,
sigma 0 和 sigma 1 旋转函数的初始化:
#define SHAF_3(x) (R_ROTATE(x, 7) ^ R_ROTATE(x, 18) ^ R_SHFT(x, 3))
#define SHAF_4(x) (R_ROTATE(x, 17) ^ R_ROTATE(x, 19) ^ R_SHFT(x, 10))
创建 W(16) 字:
int m;
int n;
for (m = 0; m < (int) block_nb; m++) {
sub_block = message + (m << 6);
for (n = 0; n < 16; n++) {
SHAF_PACK32(&sub_block[n << 2], &w[n]);
}
for (n = 16; n < 64; n++) {
w[n] = SHAF_4(w[n - 2]) + w[n - 7] + SHAF_3(w[n - 15]) + w[n - 16];
}
for (n = 0; n < 8; n++) {
buffer[n] = s_r[n];
}
这些轮换函数使用块内的现有数据来融合它们,并用许多新位扩展消息调度。
SHA256 压缩
这就是哈希函数的核心。 每个位都被交换在一起并相互叠加以创建最终的消息摘要。
该函数使用我们上面创建的状态寄存器,并将它们与消息调度和原始消息块组合以生成摘要。
每个 SHA256 转换哈希函数在修改之前都以相同的状态寄存器开始。 该函数使用以下命令创建两个临时单词:
这里,Ch(e,f,g)
代表函数的选择,Maj(a,b,c)
代表函数的主函数。 下面详细解释这两个函数。
创建这些临时字后,该函数将两个临时字相加,将状态寄存器中的每个字下移一位,并将添加的字填充到第一个状态寄存器内,并将 T1 添加到寄存器 e。
从下面的代码中可以看出,buffer[]是状态寄存器。 第 8 个寄存器 (buffer[7]) 内的值与第 7 个寄存器 (buffer[6]) 内的值交换。
交换过程继续进行,直到所有寄存器都通过 buffer[0]
获得新值,或者第一个寄存器收到 T1 和 T2 的总和。
此压缩过程对所有 64 个字进行,最后留下更新的状态寄存器。
for (n = 0; n < 64; n++) {
t1 = buffer[7] + SHAF_2(buffer[4]) + CHOICE_OF(buffer[4], buffer[5], buffer[6])
+ hash_keys[n] + w[n];
t2 = SHAF_1(buffer[0]) + MAJORITY_OF(buffer[0], buffer[1], buffer[2]);
buffer[7] = buffer[6];
buffer[6] = buffer[5];
buffer[5] = buffer[4];
buffer[4] = buffer[3] + t1;
buffer[3] = buffer[2];
buffer[2] = buffer[1];
buffer[1] = buffer[0];
buffer[0] = t1 + t2;
}
for (n = 0; n < 8; n++) {
s_r[n] += buffer[n];
这里,CHOICE_OF()
和 MAJORITY_OF 是选择函数和主要函数。 这些定义为:
#define CHOICE_OF(x, y, z) ((x & y) ^ (~x & z))
#define MAJORITY_OF(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
这里:
x & y = x and y
~x = negation of x
^ = XOR
该消息块压缩的最后一部分是获取我们开始时使用的初始哈希值,并将它们与压缩结果相加。 最终的哈希值被转换为十六进制并连接以创建最终的消息摘要。
将纯文本转换为 SHA256 的 C++ 程序
该程序有两部分 - 包含定义的所有基本函数的 SHA256 头文件和主 C++ 程序文件。
C++ SHA256 算法的头文件
#ifndef HASHFUNCTIONS_H
#define HASHFUNCTIONS_H
#include <string>
class hash_functions
{
protected:
typedef unsigned char register_8;
typedef unsigned int register_32;
typedef unsigned long long register_64;
const static register_32 hash_keys[];
static const unsigned int BLOCK_SIZE_of_256 = (512/8);
public:
void stateregister(); //init
void adjust_digest(const unsigned char *text, unsigned int text_len);
void digest_final(unsigned char *digest);
static const unsigned int PADD_SIZE = ( 256 / 8);
protected:
void compress(const unsigned char *message, unsigned int block_nb);
unsigned int s_r_totlen;
unsigned int s_r_len;
unsigned char s_r_block[2*BLOCK_SIZE_of_256];
register_32 s_r[8];
};
std::string sha256(std::string 输出);
#define R_SHFT(x, n) (x >> n) //Right shift function
#define R_ROTATE(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) //Right rotate function
#define L_ROTATE(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) //Left rotate function
#define CHOICE_OF(x, y, z) ((x & y) ^ (~x & z)) //function to find choice of
#define MAJORITY_OF(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) //function to find majority of
#define SHAF_1(x) (R_ROTATE(x, 2) ^ R_ROTATE(x, 13) ^ R_ROTATE(x, 22)) //sigma rotation function
#define SHAF_2(x) (R_ROTATE(x, 6) ^ R_ROTATE(x, 11) ^ R_ROTATE(x, 25)) //sigma rotation function
#define SHAF_3(x) (R_ROTATE(x, 7) ^ R_ROTATE(x, 18) ^ R_SHFT(x, 3)) //sigma0 rotation
#define SHAF_4(x) (R_ROTATE(x, 17) ^ R_ROTATE(x, 19) ^ R_SHFT(x, 10)) //sigma1 rotation
#define SHAF_UNPACK32(x, str) \
{ \
*((str) + 3) = (register_8) ((x) ); \
*((str) + 2) = (register_8) ((x) >> 8); \
*((str) + 1) = (register_8) ((x) >> 16); \
*((str) + 0) = (register_8) ((x) >> 24); \
}
#define SHAF_PACK32(str, x) \
{ \
*(x) = ((register_32) *((str) + 3) ) \
| ((register_32) *((str) + 2) << 8) \
| ((register_32) *((str) + 1) << 16) \
| ((register_32) *((str) + 0) << 24); \
}
#endif
在 C++ 中执行 SHA256 的主文件
#include <iostream>
#include <cstring>
#include <fstream>
#include "hash_functions.h"
const unsigned int hash_functions::hash_keys[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};
void hash_functions::compress(const unsigned char *message, unsigned int block_nb)
{
register_32 w[64];
register_32 buffer[8];
register_32 t1, t2;
const unsigned char *sub_block;
int m;
int n;
for (m = 0; m < (int) block_nb; m++) {
sub_block = message + (m << 6);
for (n = 0; n < 16; n++) {
SHAF_PACK32(&sub_block[n << 2], &w[n]);
}
for (n = 16; n < 64; n++) {
w[n] = SHAF_4(w[n - 2]) + w[n - 7] + SHAF_3(w[n - 15]) + w[n - 16];
}
for (n = 0; n < 8; n++) {
buffer[n] = s_r[n];
}
for (n = 0; n < 64; n++) {
t1 = buffer[7] + SHAF_2(buffer[4]) + CHOICE_OF(buffer[4], buffer[5], buffer[6])
+ hash_keys[n] + w[n];
t2 = SHAF_1(buffer[0]) + MAJORITY_OF(buffer[0], buffer[1], buffer[2]);
buffer[7] = buffer[6];
buffer[6] = buffer[5];
buffer[5] = buffer[4];
buffer[4] = buffer[3] + t1;
buffer[3] = buffer[2];
buffer[2] = buffer[1];
buffer[1] = buffer[0];
buffer[0] = t1 + t2;
}
for (n = 0; n < 8; n++) {
s_r[n] += buffer[n];
}
}
}
void hash_functions::stateregister()
{
s_r[0] = 0x6a09e667;
s_r[1] = 0xbb67ae85;
s_r[2] = 0x3c6ef372;
s_r[3] = 0xa54ff53a;
s_r[4] = 0x510e527f;
s_r[5] = 0x9b05688c;
s_r[6] = 0x1f83d9ab;
s_r[7] = 0x5be0cd19;
s_r_len = 0;
s_r_totlen = 0;
}
void hash_functions::adjust_digest(const unsigned char *text, unsigned int text_len)
{
unsigned int block_nb;
unsigned int new_len, rem_len, tmp_len;
const unsigned char *shifted_message;
tmp_len = BLOCK_SIZE_of_256 - s_r_len;
rem_len = text_len < tmp_len ? text_len : tmp_len;
memcpy(&s_r_block[s_r_len], text, rem_len);
if (s_r_len + text_len < BLOCK_SIZE_of_256) {
s_r_len += text_len;
return;
}
new_len = text_len - rem_len;
block_nb = new_len / BLOCK_SIZE_of_256;
shifted_message = text + rem_len;
compress(s_r_block, 1);
compress(shifted_message, block_nb);
rem_len = new_len % BLOCK_SIZE_of_256;
memcpy(s_r_block, &shifted_message[block_nb << 6], rem_len);
s_r_len = rem_len;
s_r_totlen += (block_nb + 1) << 6;
}
void hash_functions::digest_final(unsigned char *digest)
{
unsigned int block_nb;
unsigned int pm_len;
unsigned int len_b;
int i;
block_nb = (1 + ((BLOCK_SIZE_of_256 - 9)
< (s_r_len % BLOCK_SIZE_of_256)));
len_b = (s_r_totlen + s_r_len) << 3;
pm_len = block_nb << 6;
memset(s_r_block + s_r_len, 0, pm_len - s_r_len);
s_r_block[s_r_len] = 0x80;
SHAF_UNPACK32(len_b, s_r_block + pm_len - 4);
compress(s_r_block, block_nb);
for (i = 0 ; i < 8; i++) {
SHAF_UNPACK32(s_r[i], &digest[i << 2]);
}
}
std::string sha256(std::string input)
{
unsigned char digest[hash_functions::PADD_SIZE];
memset(digest,0,hash_functions::PADD_SIZE);
hash_functions obj = hash_functions();
obj.stateregister();
obj.adjust_digest( (unsigned char*)input.c_str(), input.length());
obj.digest_final(digest);
char buf[2*hash_functions::PADD_SIZE+1];
buf[2*hash_functions::PADD_SIZE] = 0;
for (int i = 0; i < hash_functions::PADD_SIZE; i++)
sprintf(buf+i*2, "%02x", digest[i]);
return std::string(buf);
}
using std::string;
using std::cout;
using std::endl;
int main(int argc, char *argv[])
{
string text_in = "Paris";
string text_out = sha256(text_in);
cout << "Final Output('"<< text_in << "'):" << text_out << endl;
return 0;
}
输出:
sha256('Paris'):5dd272b4f316b776a7b8e3d0894b37e1e42be3d5d3b204b8a5836cc50597a6b1
--------------------------------
Process exited after 0.03964 seconds with return value 0
Press any key to continue . . .
总结
本文详细讲解了使用C++进行SHA256转换的密码学。 散列算法和消息调度的压缩是加密散列的核心,通过代码片段进行演示,以便于理解。
还附带了一个现成的 C++ 程序,可以直接用于学习和回测。
相关文章
在 macOS 中编译 C++ 代码
发布时间:2023/08/18 浏览次数:198 分类:C++
-
本文包含有关 macOS X 的 C++ 编译器的信息。我们将讨论如何使用命令行界面(即终端)使用 g++ 编译器编译和运行代码。
在 Dev C++ 中编译并运行 C++ 11 代码
发布时间:2023/08/18 浏览次数:79 分类:C++
-
本文是关于使用 Dev C++ 编译 C++ 11 代码。C++ 11 版本 C++ 11是继C++ 3之后的C++新版本,经国际标准组织(ISO)批准于2011年8月12日发布。
C++ 中的清除字符串流
发布时间:2023/08/18 浏览次数:119 分类:C++
-
本文将介绍在 C++ 中清除或清空字符串流的不同方法。在C++中使用str("")和clear()方法清除字符串流 要清除或清空字符串流,我们可以使用 str("") 和 clear() 方法,但我们必须同时使用这两种方法
在 C++ 中获取文件的 MD5 哈希值
发布时间:2023/08/18 浏览次数:62 分类:C++
-
MD5 是一种密码协议,以前用于加密,但现在通常用于身份验证。 它基于哈希函数中的哈希过程,针对某些纯文本生成加密的哈希值。什么是哈希函数 在探索 MD5(消息摘要算法)之前,了解哈
将 C# 代码转换为 C++
发布时间:2023/08/18 浏览次数:183 分类:C++
-
本指南将讨论如何将 C# 代码转换为 C++。将整个语言转换为另一种语言被认为几乎是不可能的。 在这种情况下,C# 到 C++ 代码适用于 Unix,但 .NET Framework 无法从 Unix 上的 C++ 获得。
在 C++ 中使用 extern C
发布时间:2023/08/18 浏览次数:140 分类:C++
-
本文讨论 C++ 中的名称重整,这是理解 C++ 中 extern "C" 影响的先决条件。 此外,它还介绍了 C++ 编程中的 extern“C”。C++ 中 extern "C" 的使用 我们使用extern关键字来定义全局变量,也称为外部变量
C++ 中 DWORD 和 Unsigned Int 的区别
发布时间:2023/08/18 浏览次数:187 分类:C++
-
本文将介绍 DWORD 在 C++ 中的一般用法,它与 unsigned int 有着根本的不同,尽管它们目前具有相同的值。C++ 中 DWORD 和 unsigned int 的区别 根据定义,unsigned int 至少有 16 位长。 unsigned int 通常是特定