加密算法SHA
需求描述
近期想实现一个用户管理系统,因此希望实现一种将任意长度字符串转为定长字符串的算法,初步搜索发现了 SHA-256 算法。
据说在加密货币中也用到了,这激发了我的兴趣。
SHA-256
介绍
SHA-256(Secure Hash Algorithm 256-bit)能够将任意长度的输入消息转换为**256位(32字节)**的固定长度输出,通常表示为64个十六进制字符。
神秘常数
1 | private static final int[] K = { |
此处的K是用质数的立方根算的,H是用质数的平方根算的(都是小数部分)。似乎这些数的选取可以随意?
但总之,8个H是用于生成最后加密结果的,64个K用于对消息块做64轮加密运算。
预处理:消息分块
我们考虑一串消息由ASCII表示。例如:消息 abc ,转化为ASCII就是: 01100001 01100010 01100011 ( 0x616263 )。
一个目标分块(chunk)具备如下的性质 (16进制数表示,共16个32位Word,64个字节):
1 | 61626380 00000000 00000000 00000000 |
相当于划分成:
1 | W₀ 61626380 |
接下来具体讲解如何得到chunk。
实际上,可以理解为不断向原字符串(abc)不断push_back新字符的过程。具体步骤如下:
push_back(0x80): 向二进制串添加1000 0000添加0:假设原消息有 $oriLen$ 个字节,需要添加$x$个0字节,则:
$$
(oriLen+1+x) % 64 = 56
$$最后添加一个8字节数据,是oriLen的16进制表示(注意补0)
1 | private byte[] chunk(byte[] data){ |
获取64轮加密用Word
接下来讲解如何由 chunk 得到 16 个 Word (使用int存储)。
实际上就是通过位运算,将 int 与 data 的bit流对齐。以下是代码:
1 | private int[] processOneChunkTo16Words(byte[] data){ |
得到 w16 (16个字) ,接下来就是扩展的过程。我们需要通过各种运算得到 w64 (64个字)
这里还需要用到一些辅助函数:
1 | // 循环右移 |
字扩展的核心逻辑:
1 | private int[] expand16WordsTo64Words(int[] w16) { |
主加密逻辑
准备工作已经完备。接下来就是使用上述部件进行一次 64 轮的加密工作。
1 | public String encode(byte[] originalData) { |