一、前言
在数字信息时代,数据的安全性和隐私保护成为了至关重要的问题。随着信息技术的迅猛发展和互联网的普及,各种网络攻击和数据泄露事件屡见不鲜,给个人和企业都带来了很大的威胁。加密技术作为保护数据安全的重要手段,越来越受到重视。
加密算法是指将敏感信息转化为不可读形式的数学方法,包括对称加密算法和非对称加密算法等。这些算法被广泛应用于各种场景,如在线支付、电子邮件、文件传输等,以确保数据在传输和存储过程中的安全性和完整性。
本文将介绍几种常用的加密算法,理解它们的工作原理、优缺点以及应用场景。希望通过这篇博客,能够为大家提供一些有用的信息,帮助在实际应用中选择合适的加密技术来保护数据安全。
二、常用的加密算法
2.1、对称加密算法
-
AES(Advanced Encryption Standard)
- 简介:
AES是一种对称加密算法,广泛应用于各类数据加密场景。 - 密钥长度:
128位、192位、256位。 - 优点:安全性高、性能好,被广泛认为是目前最安全的对称加密算法之一。
- 简介:
-
DES(Data Encryption Standard)
- 简介:
DES曾是广泛使用的对称加密算法,但由于密钥长度较短(56位),现已被认为不够安全。 - 密钥长度:56位。
- 优点:历史悠久,曾经被广泛使用。
- 简介:
-
3DES(Triple DES)
- 简介:
3DES是对DES算法的扩展,通过三次加密提高安全性。 - 密钥长度:
112位或168位。 - 优点:比
DES更安全,但性能较差。
- 简介:
2.2、非对称加密算法
-
RSA(Rivest-Shamir-Adleman)
- 简介:
RSA是一种非对称加密算法,广泛应用于数据加密和数字签名。 - 密钥长度:
1024位、2048位、4096位等。 - 优点:安全性高,适用于加密少量数据和密钥交换。
- 简介:
-
ECC(Elliptic Curve Cryptography)
- 简介:
ECC是一种基于椭圆曲线数学的非对称加密算法,提供相同安全级别的情况下,密钥长度更短。 - 密钥长度:
160位、224位、256位等。 - 优点:高效且安全,适用于资源受限的环境。
- 简介:
2.3、哈希算法
-
SHA-256(Secure Hash Algorithm 256-bit)
- 简介:
SHA-256是一种广泛使用的哈希算法,生成256位的哈希值。 - 优点:安全性高,广泛应用于数字签名和区块链技术。
- 简介:
-
MD5(Message Digest Algorithm 5)
- 简介:
MD5是一种常用的哈希算法,生成128位的哈希值。 - 优点:计算速度快,但已被证明存在安全漏洞,不推荐用于安全性要求高的场景。
- 简介:
-
SHA-1(Secure Hash Algorithm 1)
- 简介:
SHA-1生成160位的哈希值,曾经广泛使用,但已被认为不够安全。 - 优点:曾经广泛使用,但现在被认为不安全。
- 简介:
加密哈希算法
- HMAC(Hash-based Message Authentication Code)
- 简介:
HMAC是一种基于哈希函数和密钥的消息认证码,用于验证消息的完整性和真实性。 - 优点:结合了哈希算法和密钥,提供了较高的安全性。
- 简介:
三、对称加密算法 - AES
AES(Advanced Encryption Standard,高级加密标准)是一种对称加密算法,用于加密和解密数据。它由美国国家标准与技术研究院(NIST)于2001年发布,取代了之前的DES(Data Encryption Standard)算法,成为新的加密标准。AES广泛应用于各种安全通信和数据保护场景,包括网络通信、文件加密、硬盘加密等。
主要特点:
- 对称加密:
AES是一种对称加密算法,这意味着加密和解密使用相同的密钥。 - 分组加密:
AES是一种分组加密算法,处理固定长度的数据块(128位,即16字节)。 - 多种密钥长度:
AES支持128位、192位和256位的密钥长度,提供不同级别的安全性。 - 高效性:
AES设计高效,适合在硬件和软件中实现,具有较高的加密和解密速度。
粗略加密流程:

放大看AES里面的加密过程:

3.1、Initial round
初始轮AddRoundKey操作是AES加密的第一步,AddRoundKey主要的工作就是把矩阵中的每一个字节都与该次回合密钥(round key)做XOR运算;AddRoundKey用的密钥依赖密钥扩展

3.2、字节代换(SubBytes)
字节代换(SubBytes)通过使用一个固定的S-Box(Substitution box)对状态矩阵中的每个字节进行替换,从而引入非线性变换,增加加密的复杂性和安全性。
S-Box通常是固定的(例如DES和AES加密算法), 也有一些加密算法的S-Box是基于密钥动态生成的(例如Blowfish和双鱼算法加密算法)。

3.3、⾏移位(ShiftRows)

3.4、列混合(MixColumn)
MixColumns操作的主要作用是将状态矩阵中的每一列视为一个多项式,并在有限域GF(2^8)上与一个固定的多项式矩阵相乘。这一步骤通过混合列内的字节,增加了数据的扩散性,使得单个字节的变化能够影响整个数据块。


举例:
c‘0 = 02*d4 ⊕ 03*bf ⊕ 01*5d ⊕ 01*30
这里乘法不是普通意义上的乘法,而是有限域 GF(2^8) 上的乘法。在有限域GF(2^8)上,乘法是通过以下步骤进行的:
- 左移操作:将数值左移一位,相当于乘以
2。 - 模约减:如果左移操作导致数值超过一个字节(即结果大于
0xFF),则需要模约减(模多项式为0x11B)。模约减是通过按位异或0x11B来实现的。
详细计算过程:
-
左移操作:
0xd4 = 11010100 (二进制) 0xd4 << 1 = 110101000 (二进制) = 0x1A8 (十六进制) -
模约减:
0x1A8 = 110101000 (二进制) 0x11B = 100011011 (二进制) 0x1A8 XOR 0x11B = 110101000 XOR 100011011 = 010110101 (二进制) = 0xB5 (十六进制)
3.5、密钥扩展(Key Expansion)
AES128、AES192、AES256,分别会使用AddRoundKey加密11、13、15轮,每轮需要用16字节的密钥,所以AES128、AES192、AES256分别需要11*16=176、13*16=208、15*16=240字节的密钥。
拿AES128举例,第一轮AddRoundKey的16Byte的密钥,全部来自用户的输入(密钥长度是128bit),假设需要用用到的所有密钥是一个长度为44的Word(一个Word大小4个字节)数组 W[i],i的范围是0~43。后面每一轮用到的密钥计算方式如下:
- 如果
i不是4的倍数(即i%4 != 0)则计算方式:W[i] = W[i-4] ⨁ W[i-1]

- 如果
i是4的倍数(即i%4 == 0),则:W[i]=W[i-4]⨁T(W[i-1])- 函数
T由3部分组成:字循环、字节代换和轮常量异或。 - RotWord:对字进行循环左移一个字节。

- SubWord:对字的每个字节应用
S盒(Substitution box)变换。

- Rcon:轮常量(
Round Constant),是一个特定的常量数组。

- 函数
3.6、加密模式
ECB(Electronic Codebook)
- 工作原理:每个数据块独立加密。
- 优点:实现简单,适合加密少量数据。
- 缺点:相同的明文块加密后会产生相同的密文块,容易被攻击者利用模式识别攻击。
- 应用场景:由于安全性较差,通常不推荐在实际应用中使用。

CBC(Cipher Block Chaining)
- 工作原理:每个明文块在加密前先与前一个密文块进行
XOR操作,第一个块与初始化向量(IV)进行XOR。 - 优点:相同的明文块不会产生相同的密文块,增强了安全性。
- 缺点:加密是串行的,不能并行处理;解密可以并行处理。
- 应用场景:常用于文件加密和数据传输。
加密过程:

解密过程:

CFB(Cipher Feedback)
- 工作原理:将前一个密文块加密后,再与当前明文块进行
XOR操作生成当前密文块。 - 优点:可以处理
任意长度的数据,适合流式数据加密。 - 缺点:加密和解密都是串行的,不能并行处理。
- 应用场景:适用于流数据加密,如网络通信。
加密过程:

解密过程:

OFB(Output Feedback)
- 工作原理:将前一个加密输出作为下一个块的输入,与当前明文块进行
XOR操作生成当前密文块。 - 优点:类似于流密码,可以处理任意长度的数据;加密和解密可以并行处理。
- 缺点:对
IV非常敏感,IV的重复会导致安全问题。 - 应用场景:适用于流数据加密和同步数据加密。
加密过程:

解密过程:

CTR(Counter)
- 工作原理:使用一个计数器值加密,与明文块进行
XOR操作生成密文块。计数器值通常是一个不断增加的数值。 - 优点:可以并行处理加密和解密操作,效率高;可以随机访问加密数据。
- 缺点:对计数器的管理要求严格,计数器值不能重复。
- 应用场景:适用于高性能要求的场景,如磁盘加密、网络数据加密。
加密过程:

解密过程:

GCM(Galois/Counter Mode)
- 加密和认证:
GCM模式不仅加密数据,还生成一个消息认证码(MAC),用于验证数据的完整性和真实性。 - 并行处理:
GCM模式支持并行处理,能够提高加密和解密的效率。 - 计数器模式:
GCM模式基于计数器模式(CTR),每个数据块使用一个唯一的计数器值进行加密。 - Galois消息认证码:
GCM模式使用Galois域上的乘法运算生成消息认证码(GMAC),确保数据的完整性和真实性。
工作原理
GCM模式的工作流程可以分为两个主要部分:加密和认证。
加密过程:
- 初始化向量(IV):选择一个随机的初始化向量(
IV)。 - 计数器初始化:将
IV和一个计数器值(通常为1)结合,生成初始计数器值。 - 计数器加密:对初始计数器值进行
AES加密,生成一个密钥流。 - 块加密:将密钥流与明文块进行
XOR操作,生成密文块。 - 计数器递增:每加密一个块,计数器递增
1。
认证过程:
- 附加数据:
GCM模式允许附加数据(AAD),这些数据不被加密,但需要认证。 - 生成认证标签:使用
Galois域上的乘法运算,结合密文块和附加数据,生成一个消息认证码(MAC)。 - 附加认证标签:将生成的认证标签附加到密文后,形成最终的加密输出。
解密过程:
- 计数器初始化:与加密过程相同,使用
IV和初始计数器值。 - 计数器加密:对初始计数器值进行
AES加密,生成密钥流。 - 块解密:将密钥流与密文块进行
XOR操作,恢复明文块。 - 计数器递增:每解密一个块,计数器递增
1。 - 验证认证标签:使用
Galois域上的乘法运算,结合密文块和附加数据,验证消息认证码(MAC)。
加密过程:

解密过程:

3.7、填充模式
在使用AES(Advanced Encryption Standard)加密算法时,加密的数据块大小必须是固定的128位(16字节)。然而,实际应用中待加密的数据长度通常不是16字节的整数倍,因此需要填充(Padding)机制来确保数据块的长度符合要求。
PKCS#7 填充
PKCS#7是最常见的填充方式之一,广泛应用于各种加密标准中。
- 填充方法:在数据的末尾添加
N个字节,每个字节的值都是N,其中N是需要填充的字节数。 - 示例:
- 如果数据长度为
14字节,需要填充2字节,填充值为0x02。 - 原始数据:
[0x01, 0x02, ..., 0x0E] - 填充后数据:
[0x01, 0x02, ..., 0x0E, 0x02, 0x02]
- 如果数据长度为
ANSI X.923 填充
ANSI X.923填充方式在数据末尾添加零字节,最后一个字节是填充的字节数。
- 填充方法:在数据的末尾添加
N-1个零字节,最后一个字节的值是N,其中N是需要填充的字节数。 - 示例:
- 如果数据长度为
14字节,需要填充2字节。 - 原始数据:
[0x01, 0x02, ..., 0x0E] - 填充后数据:
[0x01, 0x02, ..., 0x0E, 0x00, 0x02]
- 如果数据长度为
ISO/IEC 7816-4 填充
ISO/IEC 7816-4填充方式在数据末尾添加一个0x80字节,然后添加零字节,直到数据块长度为16字节。
- 填充方法:在数据的末尾添加一个
0x80字节,然后添加零字节,直到数据块长度为16字节。 - 示例:
- 如果数据长度为14字节,需要填充2字节。
- 原始数据:
[0x01, 0x02, ..., 0x0E] - 填充后数据:
[0x01, 0x02, ..., 0x0E, 0x80, 0x00]
Zero Padding(零填充)
Zero Padding在数据末尾添加零字节,直到数据块长度为16字节。
- 填充方法:在数据的末尾添加零字节,直到数据块长度为
16字节。 - 示例:
- 如果数据长度为
14字节,需要填充2字节。 - 原始数据:
[0x01, 0x02, ..., 0x0E] - 填充后数据:
[0x01, 0x02, ..., 0x0E, 0x00, 0x00]
- 如果数据长度为
- 注意:这种填充方式在解密时可能存在歧义,因为无法区分原始数据末尾的零字节和填充的零字节。

四、非对称加密算法 - RSA
RSA(Rivest-Shamir-Adleman)是一种广泛使用的非对称加密算法,由Ron Rivest、Adi Shamir和Leonard Adleman在1977年共同设计。RSA算法基于大整数分解的数学难题,其安全性依赖于大数分解的计算复杂性。RSA算法用于数据加密和数字签名,广泛应用于网络安全、电子商务和数字证书等领域。
4.1、基础知识
质数:是指在⼤于1的⾃然数中,除了1和它本身以外不再有其他因数的⾃然数。
互质:是公约数只有1的两个整数,叫做互质整数。
欧拉函数:
- 欧拉函数(
Euler's Totient Function),通常记作φ(n),是数论中的一个重要函数。对于一个正整数n,φ(n)表示小于n且与n互质的正整数的个数。这里,两个数互质的意思是它们的最大公约数(GCD)为1。 - 对于两个互质数
m和n,如果m和n互质,则欧拉函数满足乘法性质:φ(mn) = φ(m) * φ(n)φ(6)在1,2,3,4,5,6中只有1和5与6是互质的,所以φ(6) = 2φ(7)因为7是质数,只能被自己和1整除,所以φ(7) = 6
同余符号:(≡)是数学中的同余符号,表示两个数在模运算下是同余的。具体来说,对于整数a、b和正整数n,如果a和b除以n的余数相同,则称a和b在模n下是同余的,记作:a ≡ b (mod n) 也等同于 a - b = kn,k是一个整数。举例:
17 ≡ 5 (mod 12)23 ≡ 2 (mod 7)
同余的性质:
- 自反性:对于任意整数
a和正整数n,有a ≡ a (mod n)。 - 对称性:如果
a ≡ b (mod n),则b ≡ a (mod n)。 - 传递性:如果
a ≡ b (mod n)且b ≡ c (mod n),则a ≡ c (mod n)。 - 加法:如果
a ≡ b (mod n)且c ≡ d (mod n),则a+c ≡ b+d (mod n)。 - 减法:如果
a ≡ b (mod n)且c ≡ d (mod n),则a-c ≡ b-d (mod n)。 - 乘法:如果
a ≡ b (mod n)且b ≡ c (mod n),则a*c ≡ b*d (mod n)。
模运算
- 加法模运算:
(a+b) mod n = ((a mod n) + (b mod n)) mod n - 减法模运算:
(a-b) mod n = ((a mod n) - (b mod n) + n) mod n - 乘法模运算:
(a * b) mod n = ((a mod n) * (b mod n) ) mod n - 指数模运算:(ab) mod n = ((a mod n)b) mod n
欧拉定理:

等同 aφ(m) - 1 = km
模反元素:
模反元素(Modular Inverse)是数论中的一个概念,通常用于解决模运算中的方程。对于给定的整数a和模数m,模反元素是指一个整数x,使得: a*x ≡ 1 (mod m),等同于 a*x - 1 = k*m。
模反元素x存在的条件是a和m互质,即最大公约数gcd(a,m)=1 。如果a和m不是互质的,则a在模m下没有模反元素。
4.2、加解密流程
RSA算法包括密钥生成、加密和解密三个主要步骤。以下是RSA算法的详细原理和操作步骤。
| 步骤 | 说明 | 描述 |
|---|---|---|
| 1 | 选择一对不相等且足够大的质数 | p, q |
| 2 | 计算 p, q 的乘积 | n = p * q |
| 3 | 计算 n 的欧拉函数 | φ(n) = (p - 1) * (q - 1) |
| 4 | 选一个与 φ(n) 互质的整数 e | 1 < e < φ(n) |
| 5 | 计算出 e 对于 φ(n) 的模反元素 d | e*d ≡ 1 (mod φ(n))=> ed mod φ(n) = 1 |
| 6 | 公钥 | KU = (e, n) |
| 7 | 私钥 | KR = (d, n) |
| 8 | 将明文 M 加密 | Me mod n = C |
| 9 | 将密⽂ C 解密 | Cd mod n = M |
举例
- 假设 p = 3 、 q = 7 ,n = 21
- φ(n) = (q-1)(p-1) = 12
- 公钥 e 要满足 1 < e < φ(n) ,随机选择 e = 5
- 私钥 d 满足 e*d ≡ 1 (mod φ(n)),所以 d * 5 ≡ 1 (mod 12),根据扩展欧几里得算法算的 d =5
- 所以公钥就是 (e, n) = (5,21)
- 私钥就是 (d, n) = (5,21)
- 假设明文是 10,使用公钥加密 C = C = 105 mod 21 = 19
- 解密过程, M = 195 mod 21 = 2476099 mod 21 = 10
4.3、解密推导过程
已知 Me mod n = C,求 Cd mod n = M 是否成立?
解:
- 先把 Me mod n = C 带入到 Cd mod n = M
- 可以得到(Me mod n)d mod n = M
- 根据模运算的性质,我们可以将其进一步简化
- Med mod n = M
- ed mod φ(n) = 1 , 所以有 ed = 1 + k*φ(n)
- 代入 Med mod n = M
- 得 M1 + k*φ(n) mod n = M
- M * (Mφ(n))k mod n = M
- 根据乘法模运算得 ( (M mod n) * ((Mφ(n))k mod n) ) mod n = M
- M 是一个明文,比如 ASCII 256, 由于 p, q 都是一个很大的质数,所以 n 也是一个远大于 M 的数据。
- 所以只用证明 (Mφ(n))k mod n = 1 就行了
- 根据指数模运算得 (Mφ(n))k mod n = (Mφ(n) mod n )k mod n
- M < p and M < q and n = pq 的话,那 M 就一定跟 n 互质了
- 根据欧拉定理我们知道,M 和 n 互质的话就有: Mφ(n) ≡ 1 (mod n)
- 所以 Mφ(n) mod n = 1
- 所以 (1)k = 1
- 所以 (Mφ(n))k mod n = 1
五、哈希算法
哈希函数(Hash Function)是一种算法,它将任意长度的输入数据(通常称为消息)转换为固定长度的输出数据(通常称为哈希值或摘要)。哈希函数在计算机科学和密码学中有广泛的应用,主要用于数据完整性验证、数字签名、加密、数据检索等。
哈希函数的特点
- 固定长度输出:无论输入数据的长度是多少,哈希函数都生成固定长度的输出。例如,
SHA-256总是生成256位(32字节)的哈希值。 - 快速计算:哈希函数的计算应该是快速高效的,能够在短时间内处理大量数据。
- 不可逆性:哈希函数应具有单向性,即很难从哈希值反推出原始输入数据。
- 抗碰撞性:不同的输入数据应该生成不同的哈希值,找到两个不同的输入数据具有相同哈希值的可能性应该非常低。
- 雪崩效应:输入数据的微小变化(如改变一个比特)应导致输出哈希值的显著变化。
哈希函数的应用
- 数据完整性验证:哈希函数用于验证数据在传输或存储过程中是否被篡改。例如,文件下载后可以通过计算哈希值与提供的哈希值进行比较,验证文件的完整性。
- 数字签名:在数字签名中,哈希函数用于生成消息的摘要,签名者对摘要进行签名,接收者可以验证签名的真实性和消息的完整性。
- 密码学:哈希函数在密码学中用于生成密钥、密码存储等。例如,用户密码通常通过哈希函数处理后存储在数据库中,以提高安全性。
- 数据检索:哈希函数用于快速数据检索和查找,如哈希表(
Hash Table)和哈希集合(Hash Set)。 - 区块链:在区块链技术中,哈希函数用于生成区块的哈希值,确保区块链的安全性和不可篡改性。
常见的哈希函数
- MD5(Message Digest Algorithm 5):生成
128位(16字节)的哈希值,已被证明存在安全漏洞,不推荐用于安全性要求高的场景。 - SHA-1(Secure Hash Algorithm 1):生成
160位(20字节)的哈希值,1995年发布,SHA-1在许多安全协议中广为使用,包括TLS、GnuPG、SSH、S/MIME和IPsec,是MD5的后继者。但SHA-1的安全性在2010年以后已经不被大多数的加密场景所接受。2017年荷兰密码学研究小组CWI和Google正式宣布攻破了SHA-1[1]。 - SHA-2(Secure Hash Algorithm 2):
2001年发布,包括SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。SHA-2目前没有出现明显的弱点。虽然至今尚未出现对SHA-2有效的攻击,但它的算法跟SHA-1基本上仍然相似。 - SHA-3(Secure Hash Algorithm 3):基于
Keccak算法设计,提供更高的安全性,适用于高安全性需求的应用。2015年正式发布,由于对MD5出现成功的破解,以及对SHA-0和SHA-1出现理论上破解的方法,NIST感觉需要一个与之前算法不同的,可替换的加密散列算法,也就是现在的SHA-3。
5.1、MD5
MD5(Message Digest Algorithm 5)是一种广泛使用的哈希函数,用于生成数据的128位(16字节)哈希值。尽管MD5已经被证明存在安全漏洞,不再适合用于安全性要求高的场景,但它在数据完整性验证等领域仍然有一定的应用。以下是MD5算法的工作原理和操作步骤。
- 消息填充
消息填充的目的是使消息的长度满足特定条件,即长度模512等于448。这是为了确保消息长度(以位为单位)加上填充后的长度是512的倍数。
- 添加一个’1’位:在消息末尾添加一个
'1'位(即0x80)。 - 填充’0’位:在
'1'位之后添加足够的'0'位,使得消息长度模512等于448。 - 附加消息长度:在填充后的消息末尾附加消息的原始长度(以位为单位),长度为
64位。
- 初始化MD缓冲区
MD5算法使用四个32位的寄存器(A、B、C、D)作为缓冲区,并将其初始化为特定的常量:
A = 0x67452301
B = 0xefcdab89
C = 0x98badcfe
D = 0x10325476
- 处理消息块
消息被分成512位(64字节)的块,每个块进一步划分为16个32位的子块。MD5算法对每个512位的消息块进行四轮变换,每轮包括16次操作。每次操作使用一个非线性函数、一个常量和当前消息块中的一个子块。
-
四个非线性函数:
F(X, Y, Z) = (X & Y) | (~X & Z)G(X, Y, Z) = (X & Z) | (Y & ~Z)H(X, Y, Z) = X ^ Y ^ ZI(X, Y, Z) = Y ^ (X | ~Z)
-
每轮变换:
- 第一轮:使用函数
F和消息块中的子块。 - 第二轮:使用函数
G和消息块中的子块。 - 第三轮:使用函数
H和消息块中的子块。 - 第四轮:使用函数
I和消息块中的子块。
- 第一轮:使用函数
每轮变换包括以下步骤:
- 将当前的缓冲区值(
A、B、C、D)与非线性函数的结果、消息块中的一个子块和一个常量进行加法运算。 - 将结果进行左循环移位。
- 将移位后的结果加到当前缓冲区值上。
- 输出最终哈希值
所有消息块处理完毕后,缓冲区中的四个寄存器(A、B、C、D)包含了最终的哈希值。将这四个寄存器按小端序连接起来,得到128位的哈希值。
以下是MD5算法的详细计算过程:
-
消息填充:
- 假设原始消息长度为
L位。 - 在消息末尾添加一个
'1'位(即0x80)。 - 添加
k个'0'位,使得L + 1 + k ≡ 448 (mod 512)。 - 在填充后的消息末尾附加消息的原始长度
L,长度为64位。
- 假设原始消息长度为
-
初始化MD缓冲区:
A = 0x67452301B = 0xefcdab89C = 0x98badcfeD = 0x10325476
-
处理消息块:
- 将消息分成若干个
512位(64字节)的块,每个块进一步划分为16个32位的子块。 - 对每个消息块,执行四轮变换,每轮包括
16次操作。
- 将消息分成若干个
-
四轮变换:
- 第一轮:使用函数
F和消息块中的子块。 - 第二轮:使用函数
G和消息块中的子块。 - 第三轮:使用函数
H和消息块中的子块。 - 第四轮:使用函数
I和消息块中的子块。
- 第一轮:使用函数

伪代码:
//Note: All variables are unsigned 32 bits and wrap modulo 2^32 when calculating
var int[64] r, k
//r specifies the per-round shift amounts
r[ 0..15]:= {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22}
r[16..31]:= {5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20}
r[32..47]:= {4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23}
r[48..63]:= {6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}
//Use binary integer part of the sines of integers as constants:
for i from 0 to 63
k[i] := floor(abs(sin(i + 1)) × 2^32)
//Initialize variables:
var int h0 := 0x67452301
var int h1 := 0xEFCDAB89
var int h2 := 0x98BADCFE
var int h3 := 0x10325476
//Pre-processing:
append "1" bit to message
append "0" bits until message length in bits ≡ 448 (mod 512)
append bit length of message as 64-bit little-endian integer to message
//Process the message in successive 512-bit chunks:
for each 512-bit chunk of message
break chunk into sixteen 32-bit little-endian words w[i], 0 ≤ i ≤ 15
//Initialize hash value for this chunk:
var int a := h0
var int b := h1
var int c := h2
var int d := h3
//Main loop:
for i from 0 to 63
if 0 ≤ i ≤ 15 then
f := (b and c) or ((not b) and d)
g := i
else if 16 ≤ i ≤ 31
f := (d and b) or ((not d) and c)
g := (5×i + 1) mod 16
else if 32 ≤ i ≤ 47
f := b xor c xor d
g := (3×i + 5) mod 16
else if 48 ≤ i ≤ 63
f := c xor (b or (not d))
g := (7×i) mod 16
temp := d
d := c
c := b
b := leftrotate((a + f + k[i] + w[g]),r[i]) + b
a := temp
Next i
//Add this chunk's hash to result so far:
h0 := h0 + a
h1 := h1 + b
h2 := h2 + c
h3 := h3 + d
End ForEach
var int digest := h0 append h1 append h2 append h3 //(expressed as little-endian)
5.2、SHA-256
SHA-256(Secure Hash Algorithm 256-bit)是SHA-2(Secure Hash Algorithm 2)家族中的一种哈希函数,广泛应用于密码学、数据完整性验证和数字签名等领域。SHA-256将任意长度的输入数据转换为固定长度的256位(32字节)哈希值。以下是SHA-256算法的工作原理和具体步骤。
SHA-256 算法原理
SHA-256通过一系列的位操作、逻辑运算和非线性函数,将输入数据转换为固定长度的哈希值。其主要步骤包括消息填充、初始化哈希值、消息分块处理和输出最终的哈希值。
- 消息填充
消息填充的目的是使消息的长度满足特定条件,即长度模512等于448。这是为了确保消息长度(以位为单位)加上填充后的长度是512的倍数。
- 添加一个’1’位:在消息末尾添加一个
'1'位(即0x80)。 - 填充’0’位:在
'1'位之后添加足够的'0'位,使得消息长度模512等于448。 - 附加消息长度:在填充后的消息末尾附加消息的原始长度(以位为单位),长度为
64位。
- 初始化哈希值
SHA-256算法使用8个32位的寄存器作为初始哈希值,这些寄存器被初始化为特定的常量:
H0 = 0x6a09e667
H1 = 0xbb67ae85
H2 = 0x3c6ef372
H3 = 0xa54ff53a
H4 = 0x510e527f
H5 = 0x9b05688c
H6 = 0x1f83d9ab
H7 = 0x5be0cd19
- 处理消息块
消息被分成512位(64字节)的块,每个块进一步划分为16个32位的子块。SHA-256算法对每个512位的消息块进行64轮变换,每轮使用一个非线性函数、一个常量和当前消息块中的一个子块。
- 消息扩展:将
16个32位的子块扩展为64个32位的字。 - 循环变换:对每个消息块执行
64轮变换,每轮使用一个非线性函数、一个常量和当前消息块中的一个字。


每一轮都把256位缓冲区的值abcdefgh作为输入,并更新缓冲区的值。每一轮会用到一个32位的值W,该值由当前被处理的512比特的消息分组M导出,导出算法是消息扩展算法。每一轮还将使用附加的常数K,其中 0<t≤64,用来使每轮的运算不同。
- 轮函数
SHA-256使用以下非线性函数和常量进行变换:
- 选择函数(Ch):
Ch(x, y, z) = (x AND y) XOR (~x AND z) - 多数函数(Maj):
Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z) - Σ0函数:
Σ0(x) = ROTR(2, x) XOR ROTR(13, x) XOR ROTR(22, x) - Σ1函数:
Σ1(x) = ROTR(6, x) XOR ROTR(11, x) XOR ROTR(25, x) - σ0函数:
σ0(x) = ROTR(7, x) XOR ROTR(18, x) XOR SHR(3, x) - σ1函数:
σ1(x) = ROTR(17, x) XOR ROTR(19, x) XOR SHR(10, x)
- 常量表(K)
SHA-256使用64个32位的常量表,这些常量是前64个素数的立方根的小数部分:
K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
...
0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c,
0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814,
0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
0xc67178f2
]
- 轮函数计算
每个轮次的计算如下:
for i in range(64):
T1 = H + Σ1(E) + Ch(E, F, G) + K[i] + W[i]
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

- 更新哈希值
每处理完一个消息块后,更新哈希值:
H0 = H0 + A
H1 = H1 + B
H2 = H2 + C
H3 = H3 + D
H4 = H4 + E
H5 = H5 + F
H6 = H6 + G
H7 = H7 + H
- 输出最终哈希值
所有消息块处理完毕后,连接8个32位的寄存器,得到256位的哈希值。
伪代码:
初始化
(以下是前8個質數2..19平方根小數部分的前32位元):
h0 := 0x6a09e667
h1 := 0xbb67ae85
h2 := 0x3c6ef372
h3 := 0xa54ff53a
h4 := 0x510e527f
h5 := 0x9b05688c
h6 := 0x1f83d9ab
h7 := 0x5be0cd19
初始化每輪用的常數
(前64個質數2..311的立方根小數部分的前32位元):
k[0..63] :=
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
預處理:
訊息後接上一個位元'1'
再接上k個'0',其中k為最小的非負整數,使所得的訊息長度(位元數)同余於448(mod 512)
將預處理前訊息的長度(位元數)寫成64位元大端序整數,接在最尾
將訊息分成若干連續段處理,每段512位元:
將訊息分成512位元的分段
for 每段
將該段再分成十六個32位元的字組,看成大端序的整數w[0..15]
從該十六個字組,計算多四十八個同樣長度的字組,得到總共六十四個32位元字組:
for i from 16 to 63
s0 := (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
初始化此段的雜湊值:
a := h0
b := h1
c := h2
d := h3
e := h4
f := h5
g := h6
h := h7
主迴圈:
for i from 0 to 63
s0 := (a rightrotate 2) xor (a rightrotate 13) xor(a rightrotate 22)
maj := (a and b) xor (a and c) xor(b and c)
t2 := s0 + maj
s1 := (e rightrotate 6) xor (e rightrotate 11) xor(e rightrotate 25)
ch := (e and f) xor ((not e) and g)
t1 := h + s1 + ch + k[i] + w[i]
h := g
g := f
f := e
e := d + t1
d := c
c := b
b := a
a := t1 + t2
將此段的雜湊值加進總和:
h0 := h0 + a
h1 := h1 + b
h2 := h2 + c
h3 := h3 + d
h4 := h4 + e
h5 := h5 + f
h6 := h6 + g
h7 := h7 + h
輸出最總的雜湊值(大端序):
digest = hash = h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7
六、benchmarks

七、参考文章
Advanced Encryption Standard (AES)
HMAC | MAC | 基于哈希函数的消息认证码| 消息认证码 |HMAC-MD5