mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
444 字
1 分钟
sha256从原理到实现
2026-09-15

sha256从原理到cpp实现#

一、原理#

我们直接来看流程图

flowchart TD %% 样式定义 classDef default fill:#f9fafb,stroke:#6b7280,stroke-width:2px,color:#1f2937,rx:8,ry:8; classDef input fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a8a,rx:10,ry:10; classDef preprocess fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#0c4a6e; classDef init fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f; classDef condition fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#831843; classDef compress fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d; classDef update fill:#f3e8ff,stroke:#9333ea,stroke-width:2px,color:#581c87; classDef final fill:#d1fae5,stroke:#059669,stroke-width:2px,color:#064e3b,rx:10,ry:10; classDef output fill:#ede9fe,stroke:#7c3aed,stroke-width:2px,color:#4c1d95,rx:10,ry:10; A["输入消息 M"]:::input B["步骤1: 消息填充 Padding"]:::preprocess C["步骤2: 分块 512 bit"]:::preprocess D["步骤3: 初始化 8 个 32bit 寄存器<br/>H0 ~ H7"]:::init E{"还有 512bit 块吗?"}:::condition F["步骤4: 消息扩展<br/>16 → 64 字"]:::compress G["步骤5: 64 轮压缩"]:::compress H["步骤6: 更新 H0 ~ H7"]:::update I["步骤7: 拼接 H0 ~ H7"]:::final J["输出 256bit 摘要"]:::output A --> B B --> C C --> D D --> E E -- 是 --> F F --> G G --> H H --> E E -- 否 --> I I --> J %% 连线样式 linkStyle default stroke:#6b7280,stroke-width:2px;

我们从图中可以直观对比一下sha256与md5和sha1

对比项MD5SHA-1SHA-256
寄存器数量4 个 32 位 (A, B, C, D)5 个 32 位 (A, B, C, D, E)8 个 32 位 (H0 ~ H7)
消息扩展
扩展后字数16 字 (不扩展)80 字 (W0 ~ W79)64 字 (W0 ~ W63)
压缩轮数64 轮 (4 轮 × 16 步)80 轮 (4 轮 × 20 步)64 轮 (64 步)
最终输出位数128 位160 位256 位

填充#

flowchart LR A["原始消息 M<br/>(L bits)"] --> B["追加 1 bit"] B --> C["追加 k 个 0<br/>使 L+1+k ≡ 448 mod 512"] C --> D["追加 64 bit 大端 L"] D --> E["总长是 512 的倍数"]

这里和sha1完全一样,追加的八字节也是大端序。

初始化寄存器#

这个跟之前不同了。这些是前 8 个素数(2,3,5,7,11,13,17,19)平方根小数部分的前 32 位

H0 = 0x6a09e667 H1 = 0xbb67ae85
H2 = 0x3c6ef372 H3 = 0xa54ff53a
H4 = 0x510e527f H5 = 0x9b05688c
H6 = 0x1f83d9ab H7 = 0x5be0cd19

消息扩展#

flowchart TD %% 样式定义 classDef default fill:#f9fafb,stroke:#6b7280,stroke-width:2px,color:#1f2937,rx:8,ry:8; classDef input fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a8a,rx:10,ry:10; classDef loop fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f; classDef sigma0 fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#0c4a6e; classDef sigma1 fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d; classDef formula fill:#ede9fe,stroke:#7c3aed,stroke-width:2px,color:#4c1d95; classDef output fill:#d1fae5,stroke:#059669,stroke-width:2px,color:#064e3b,rx:10,ry:10; A["W0..W15<br/>= 大端读入的<br/>16 个 32bit 字"]:::input B["for t = 16..63"]:::loop C["σ0 = rotr(Wt-15, 7)<br/>^ rotr(Wt-15, 18)<br/>^ shr(Wt-15, 3)"]:::sigma0 D["σ1 = rotr(Wt-2, 17)<br/>^ rotr(Wt-2, 19)<br/>^ shr(Wt-2, 10)"]:::sigma1 E["Wt = Wt-16 + σ0<br/>+ Wt-7 + σ1"]:::formula F["得到 W0..W63"]:::output A --> B B --> C C --> D D --> E E --> B B -->|结束| F %% 连线样式 linkStyle default stroke:#6b7280,stroke-width:2px;

注意这里 σ0/σ1 用的是 rotr(循环右移)shr(逻辑右移) 的组合,与 SHA-1 的简单 rotl 不同。

64轮压缩#

用到的辅助函数
Ch(x,y,z) = (x & y) ^ (~x & z)
Maj(x,y,z) = (x & y) ^ (x & z) ^ (y & z)
Σ0(x) = rotr(x,2) ^ rotr(x,13) ^ rotr(x,22)
Σ1(x) = rotr(x,6) ^ rotr(x,11) ^ rotr(x,25)
σ0(x) = rotr(x,7) ^ rotr(x,18) ^ shr(x,3)
σ1(x) = rotr(x,17) ^ rotr(x,19) ^ shr(x,10)
每一轮的公式:
T1 = h + Σ1(e) + Ch(e,f,g) + K[t] + W[t]
T2 = Σ0(a) + Maj(a,b,c)
h = g
g = f
f = e
e = d + T1
d = c
c = b
b = a
a = T1 + T2
flowchart TD %% 样式定义 classDef default fill:#f9fafb,stroke:#6b7280,stroke-width:2px,color:#1f2937,rx:8,ry:8; classDef init fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a8a,rx:10,ry:10; classDef t1 fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#0c4a6e; classDef t2 fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d; classDef update fill:#f3e8ff,stroke:#9333ea,stroke-width:2px,color:#581c87; classDef condition fill:#fce7f3,stroke:#db2777,stroke-width:2px,color:#831843; classDef final fill:#d1fae5,stroke:#059669,stroke-width:2px,color:#064e3b,rx:10,ry:10; A["a, b, c, d, e, f, g, h"]:::init B["T1 = h + Σ1(e) + Ch(e,f,g)<br/>+ Kt + Wt"]:::t1 C["T2 = Σ0(a) + Maj(a,b,c)"]:::t2 D["h = g<br/>g = f<br/>f = e<br/>e = d + T1"]:::update E["d = c<br/>c = b<br/>b = a<br/>a = T1 + T2"]:::update F{"还有轮次?"}:::condition G["H0 += a<br/>H1 += b<br/>...<br/>H7 += h"]:::final A --> B A --> C B --> D C --> D D --> E E --> F F -->|是| A F -->|否| G %% 连线样式 linkStyle default stroke:#6b7280,stroke-width:2px;

累加#

H0 += a; H1 += b; ... H7 += h

可以看到,大体上和前两个没啥区别,但是细看的话,内部实现确实一套新的逻辑,正所谓只可远观而不可亵玩焉。

二、实现#

依旧从应用层入手,结构完全一样,我们直接看主要函数。

// ============================================================
// 一次性接口
// ============================================================
std::string sha256(const std::string& input) {
SHA256Context ctx;
sha256Update(ctx, reinterpret_cast<const uint8_t*>(input.data()), input.size());
uint8_t digest[32];
sha256Final(ctx, digest);
std::ostringstream oss;
for (int i = 0; i < 32; i++) {
oss << std::hex << std::setw(2) << std::setfill('0')
<< (int)digest[i];
}
return oss.str();
}

1.context#

// ============================================================
// SHA-256 上下文
// ============================================================
struct SHA256Context {
uint32_t state[8]; // H0..H7
uint64_t bitCount; // 已处理比特数
uint8_t buffer[64]; // 未处理完的块
size_t bufferLen;
SHA256Context() { init(); }
void init() {
// 前 8 个素数平方根小数部分前 32 位(FIPS 180-4)
state[0] = 0x6a09e667;
state[1] = 0xbb67ae85;
state[2] = 0x3c6ef372;
state[3] = 0xa54ff53a;
state[4] = 0x510e527f;
state[5] = 0x9b05688c;
state[6] = 0x1f83d9ab;
state[7] = 0x5be0cd19;
bitCount = 0;
bufferLen = 0;
}
};

2.update#

// ============================================================
// 更新
// ============================================================
static void sha256Update(SHA256Context& ctx, const uint8_t* data, size_t len) {
ctx.bitCount += (uint64_t)len * 8;//计算已经处理的字节数,第一次调用就是原始消息长度
while (len > 0) {
size_t space = 64 - ctx.bufferLen; //计算缓冲区剩余空间
size_t copy = (len < space) ? len : space;
std::memcpy(ctx.buffer + ctx.bufferLen, data, copy);
ctx.bufferLen += copy;
data += copy; //移动指针
len -= copy;
if (ctx.bufferLen == 64) {
sha256Transform(ctx.state, ctx.buffer);
ctx.bufferLen = 0;
}
}
}

3.transform#

工具函数

// ============================================================
// SHA-256 常数 K[0..63]
// 来源:前 64 个素数立方根小数部分的前 32 位(FIPS 180-4)
// ============================================================
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
};
// ============================================================
// 大端序读写工具(SHA-256 规定大端)
// ============================================================
static inline uint32_t read_be32(const uint8_t* p) {
return ((uint32_t)p[0] << 24)
| ((uint32_t)p[1] << 16)
| ((uint32_t)p[2] << 8)
| (uint32_t)p[3];
}
static inline void write_be32(uint8_t* p, uint32_t v) {
p[0] = (uint8_t)((v >> 24) & 0xff);
p[1] = (uint8_t)((v >> 16) & 0xff);
p[2] = (uint8_t)((v >> 8) & 0xff);
p[3] = (uint8_t)( v & 0xff);
}
// 循环右移
static inline uint32_t rotr(uint32_t x, uint32_t n) {
return (x >> n) | (x << (32 - n));
}
// ============================================================
// 6 个辅助函数
// ============================================================
static inline uint32_t Ch (uint32_t x, uint32_t y, uint32_t z) {
return (x & y) ^ (~x & z);
}
static inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) {
return (x & y) ^ (x & z) ^ (y & z);
}
static inline uint32_t Sigma0(uint32_t x) { // 大写 Σ0
return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
}
static inline uint32_t Sigma1(uint32_t x) { // 大写 Σ1
return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
}
static inline uint32_t sigma0(uint32_t x) { // 小写 σ0
return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
}
static inline uint32_t sigma1(uint32_t x) { // 小写 σ1
return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
}
// ============================================================
// 处理一个 512bit 块
// ============================================================
static void sha256Transform(uint32_t state[8], const uint8_t block[64]) {
uint32_t W[64];
// 1. 前 16 字:大端读入
for (int i = 0; i < 16; i++) {
W[i] = read_be32(block + i * 4);
}
// 2. 扩展到 64 字
for (int i = 16; i < 64; i++) {
W[i] = sigma1(W[i - 2]) + W[i - 7]
+ sigma0(W[i - 15]) + W[i - 16];
}
// 3. 加载工作变量
uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
uint32_t e = state[4], f = state[5], g = state[6], h = state[7];
// 4. 64 轮
for (int i = 0; i < 64; i++) {
uint32_t T1 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];
uint32_t T2 = Sigma0(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
}
// 5. 累加回 state
state[0] += a; state[1] += b; state[2] += c; state[3] += d;
state[4] += e; state[5] += f; state[6] += g; state[7] += h;
}

4.dofinal#

最后再看看

// ============================================================
// 收尾:填充 + 长度 + 输出 32 字节
// ============================================================
static void sha256Final(SHA256Context& ctx, uint8_t digest[32]) {
uint8_t padding[64];
std::memset(padding, 0, sizeof(padding));
padding[0] = 0x80;
uint64_t bits = ctx.bitCount;
size_t padLen = (ctx.bufferLen < 56)
? (56 - ctx.bufferLen)
: (120 - ctx.bufferLen);
sha256Update(ctx, padding, padLen);
uint8_t lenBytes[8];
for (int i = 0; i < 8; i++) {
lenBytes[i] = (uint8_t)((bits >> (8 * (7 - i))) & 0xff);
}
sha256Update(ctx, lenBytes, 8);
for (int i = 0; i < 8; i++) {
write_be32(digest + i * 4, ctx.state[i]);
}
}

ok,水完了。

64轮变换在transform里,我这种密码学菜鸟暂时碰不得。

所以目前比较简单的能够识别的魔改点就是k表,初始的八个寄存器值。😭

三、唠嗑#

欢乐的时光总是短暂,又到了吃饭时间,我去吃饭了886,咱们下期再见。

下期讲讲HMAC。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

sha256从原理到实现
https://fatdog.20060113.xyz/posts/sha256/
作者
神秘大胖狗
发布于
2026-09-15
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00