一、SHA256算法介绍
SHA256算法总体上来说就是对原始数据进行分段加密,并且当前分段的加密结果受到上一分段加密结果的影响,最后一段的加密结果就是最终的结果(和混沌系统有点类似)。
二、SHA256算法C++实现
1)sha256.h
/*
* Copyright (c)
* Filename: sha256.h
* Brief: SHA256算法实现
* Depend: C++11
*
* Version: V1.0.0
* Date: 2019/11/08-2019/11/13
* Author: LucianY
* Note: 初次版本。
*
* Version: V2.0.0
* Date: 2023/01/04
* Author: LucianY
* Note: 1、代码优化:使用单例模式;
* 2、debug:支持加密空字符串;
* 3、性能优化。
*
* Attention: 输入信息中有中文时,得到的数字指纹与使用其他工具得到数字指纹可能不相同。原因是不同平台中文的编码方式不同。
*/
#ifndef LY_SHA256_H
#define LY_SHA256_H
#include <cstdint>
#include <string>
#include <vector>
namespace Ly {
/**
* @brief SHA256加密类
*/
class Sha256 {
public:
//! 获取单例
inline static Sha256 &getInstance()
{
static Sha256 instance;
return instance;
}
/**
* @brief: 使用SHA256算法,加密输入信息(获取数字指纹)
* @param[in] message: 输入信息
* @return: 摘要(数字指纹)
*/
std::vector<uint8_t> encrypt(std::vector<uint8_t> message) const;
/**
* @brief: 获取十六进制表示的信息摘要(数字指纹)
* @param[in] message: 输入信息
* @return: 十六进制表示的信息摘要(数字指纹)
*/
std::string getHexMessageDigest(const std::string &message) const;
protected:
/// SHA256算法中定义的6种逻辑运算 ///
inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z) const noexcept
{
return (x & y) ^ ((~x) & z);
}
inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z) const noexcept
{
return (x & y) ^ (x & z) ^ (y & z);
}
inline uint32_t bigSigma0(uint32_t x) const noexcept
{
return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10);
}
inline uint32_t bigSigma1(uint32_t x) const noexcept
{
return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7);
}
inline uint32_t smallSigma0(uint32_t x) const noexcept
{
return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3);
}
inline uint32_t smallSigma1(uint32_t x) const noexcept
{
return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10);
}
/**
* @brief: SHA256算法对输入信息的预处理,包括“附加填充比特”和“附加长度值”
附加填充比特: 在报文末尾进行填充,先补第一个比特为1,然后都补0,直到长度满足对512取模后余数是448。需要注意的是,信息必须进行填充。
附加长度值: 用一个64位的数据来表示原始消息(填充前的消息)的长度,并将其补到已经进行了填充操作的消息后面。
* @param[in][out] message: 待处理的信息
*/
void preprocessing(std::vector<uint8_t> &message) const;
/**
* @brief: 将信息分解成连续的64Byte大小的数据块
* @param[in] message: 输入信息,长度为64Byte的倍数
* @return: 输出数据块
*/
std::vector<std::vector<uint8_t>> breakTextInto64ByteChunks(const std::vector<uint8_t> &message) const;
/**
* @brief: 由64Byte大小的数据块,构造出64个4Byte大小的字。
构造算法:前16个字直接由数据块分解得到,其余的字由如下迭代公式得到:
W[t] = smallSigma1(W[t-2]) + W[t-7] + smallSigma0(W[t-15]) + W[t-16]
* @param[in] chunk: 输入数据块,大小为64Byte
* @return: 输出字
*/
std::vector<uint32_t> structureWords(const std::vector<uint8_t> &chunk) const;
/**
* @breif: 基于64个4Byte大小的字,进行64次循环加密
* @param[in] words: 64个4Byte大小的字
* @param[in][out] message_digest: 信息摘要
*/
void transform(const std::vector<uint32_t> &words, std::vector<uint32_t> &message_digest) const;
/**
* @brief: 输出最终的哈希值(数字指纹)
* @param[in] input: 步长为32bit的哈希值
* @return: 步长为8bit的哈希值
*/
std::vector<uint8_t> produceFinalHashValue(const std::vector<uint32_t> &input) const;
private:
/* 单例模式 */
Sha256() = default;
Sha256(const Sha256 &) = delete;
Sha256 &operator=(const Sha256 &) = delete;
Sha256(Sha256 &&) = delete;
Sha256 &operator=(Sha256 &&) = delete;
~Sha256() = default;
// 在SHA256算法中的初始信息摘要,这些常量是对自然数中前8个质数的平方根的小数部分取前32bit而来
static const std::vector<uint32_t> initial_message_digest_;
// 在SHA256算法中,用到64个常量,这些常量是对自然数中前64个质数的立方根的小数部分取前32bit而来
static const std::vector<uint32_t> add_constant_;
};
} // namespace Ly
#endif // LY_SHA256_H
2)sha256.cpp
/*
* Copyright (c)
* Filename: sha256.cpp
* Brief: SHA256算法实现
* Depend: C++11
*
* Version: V1.0.0
* Date: 2019/11/08-2019/11/13
* Author: LucianY
* Note: 初次版本。
*
* Version: V2.0.0
* Date: 2023/01/04
* Author: LucianY
* Note: 1、代码优化:使用单例模式;
* 2、debug:支持加密空字符串;
* 3、性能优化。
*
* Attention: 输入信息中有中文时,得到的数字指纹与使用其他工具得到数字指纹可能不相同。原因是不同平台中文的编码方式不同。
*/
#include "sha256.h"
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
namespace Ly {
const std::vector<uint32_t> Sha256::initial_message_digest_{
0x6a09e667, 0xbb67ae85, 0x3c6ef372,
0xa54ff53a, 0x510e527f, 0x9b05688c,
0x1f83d9ab, 0x5be0cd19
};
const std::vector<uint32_t> Sha256::add_constant_{
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
};
std::vector<uint8_t> Sha256::encrypt(std::vector<uint8_t> input_message) const
{
//! 文本预处理
preprocessing(input_message);
//! 将文本分解成连续的64Byte大小的数据块
auto chunks = breakTextInto64ByteChunks(input_message);
//! 由64Byte大小的数据块,构造出64个4Byte大小的字。然后进行循环迭代。
std::vector<uint32_t> message_digest(initial_message_digest_); // 初始化信息摘要
for (const auto &chunk : chunks)
{
transform(structureWords(chunk), message_digest);
}
//! 获取最终结果
return produceFinalHashValue(message_digest);
}
std::string Sha256::getHexMessageDigest(const std::string &message) const
{
auto digest = encrypt(std::vector<uint8_t>(message.begin(), message.end()));
std::ostringstream o_s;
o_s << std::hex << std::setiosflags(std::ios::uppercase);
for (auto c : digest) {
o_s << std::setw(2) << std::setfill('0') << static_cast<unsigned short>(c);
}
return o_s.str();
}
void Sha256::preprocessing(std::vector<uint8_t> &message) const
{
const auto original_bit_size = message.size() * 8;
//! 附加填充比特
auto remainder = message.size() % 64;
auto origialSize = message.size();
if (remainder < 56)
{
message.resize(message.size() + 56 - remainder, 0x00);
message[origialSize] = 0x80; // ox80即10000000
}
else if (remainder == 56)
{
message.resize(message.size() + 64, 0x00);
message[origialSize] = 0x80;
}
else
{
message.resize(message.size() + 120 - remainder, 0x00);
message[origialSize] = 0x80;
}
//! 附加原始文本的长度值
for (int i = 1; i <= 8; ++i)
{
message.emplace_back(static_cast<uint8_t>(original_bit_size >> (64 - 8 * i)));
}
}
std::vector<std::vector<uint8_t>> Sha256::breakTextInto64ByteChunks(const std::vector<uint8_t> &message) const
{
if (0 != message.size() % 64)
{
std::ostringstream oss;
oss << "invalid message size: " << message.size();
throw std::invalid_argument(oss.str());
}
std::vector<std::vector<uint8_t>> chunks;
auto quotient = message.size() / 64;
for (size_t i = 0; i < quotient; ++i)
{
chunks.emplace_back(message.begin() + i * 64, message.begin() + (i + 1) * 64);
}
return chunks;
}
std::vector<uint32_t> Sha256::structureWords(const std::vector<uint8_t> &chunk) const
{
if (64 != chunk.size())
{
std::ostringstream oss;
oss << "invalid chunk size: " << chunk.size();
throw std::invalid_argument(oss.str());
}
std::vector<uint32_t> words(64);
for (size_t i = 0; i < 16; ++i)
{
words[i] = (static_cast<uint32_t>(chunk[i * 4]) << 24) | (static_cast<uint32_t>(chunk[i * 4 + 1]) << 16) |
(static_cast<uint32_t>(chunk[i * 4 + 2]) << 8) | static_cast<uint32_t>(chunk[i * 4 + 3]);
}
for (size_t i = 16; i < 64; ++i)
{
words[i] = smallSigma1(words[i - 2]) + words[i - 7] + smallSigma0(words[i - 15]) + words[i - 16];
}
return words;
}
void Sha256::transform(const std::vector<uint32_t> &words, std::vector<uint32_t> &message_digest) const
{
if (8 != message_digest.size() || 64 != words.size())
{
std::ostringstream oss;
oss << "invalid message_digest size: " << message_digest.size() <<
"Or invalid words size: " << words.size();
throw std::invalid_argument(oss.str());
}
auto d = message_digest;
for (int i = 0; i < 64; ++i)
{
uint32_t temp1 = d[7] + bigSigma1(d[4]) + ch(d[4], d[5], d[6]) + add_constant_[i] + words[i];
uint32_t temp2 = bigSigma0(d[0]) + maj(d[0], d[1], d[2]);
d[7] = d[6];
d[6] = d[5];
d[5] = d[4];
d[4] = d[3] + temp1;
d[3] = d[2];
d[2] = d[1];
d[1] = d[0];
d[0] = temp1 + temp2;
}
for (int i = 0; i < 8; ++i)
{
message_digest[i] += d[i];
}
}
std::vector<uint8_t> Sha256::produceFinalHashValue(const std::vector<uint32_t> &input) const
{
std::vector<uint8_t> output;
for (auto it = input.begin(); it != input.end(); ++it)
{
for (int i = 0; i < 4; i++)
{
output.emplace_back(static_cast<uint8_t>((*it) >> (24 - 8 * i)));
}
}
return output;
}
} // namespace Ly
3)main.cpp
/*
* Filename: main.cpp
* Author: L.Y.
* Brief: SHA256算法测试
*/
#include <iostream>
#include <string>
#include "sha256.h"
void testSha256()
{
//! 输入报文为空报文
if (true) {
std::string message; // 待加密的信息
std::string message_digest = Ly::Sha256::getInstance().getHexMessageDigest(message); // 加密
// 输出加密结果:E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
std::cout << '\"' << message << "\" digest is: " << message_digest << std::endl << std::endl;
}
//! 输入报文长度小于56
if (true) {
std::string message = "seek truth from facts"; // 待加密的信息
std::string message_digest = Ly::Sha256::getInstance().getHexMessageDigest(message); // 加密
// 输出加密结果:E2E63CD711B3FE1A6613A54010F4C198894DA5E87517FEFCEE239031A0270CC9
std::cout << '\"' << message << "\" digest is: " << message_digest << std::endl << std::endl;
}
//! 输入报文长度等于56
if (true) {
std::string message(56, '@'); // 待加密的信息
std::string message_digest = Ly::Sha256::getInstance().getHexMessageDigest(message); // 加密
// 输出加密结果:1081DDB54326E1D17F90857107D2F5C2DE80ACD2A3FC2C57A12DC605FC2CB07D
std::cout << '\"' << message << "\" digest is: " << message_digest << std::endl << std::endl;
}
//! 输入报文长度大于56并且小于64
if (true) {
std::string message(60, '#'); // 待加密的信息
std::string message_digest = Ly::Sha256::getInstance().getHexMessageDigest(message); // 加密
// 输出加密结果:AFD1B7A7CA681D044AF56BE6B0D2E56066A82E32BBBAAB5BA76A7C93E63E585B
std::cout << '\"' << message << "\" digest is: " << message_digest << std::endl << std::endl;
}
}
int main()
{
testSha256();
getchar();
return 0;
}