哈希函数(Hash Functions - 散列函数)的基本介绍(SHA-2,SHA-256,MD-5,Scrypt,BCrypt等)

Table of Contents

哈希函数的基本介绍(SHA-256,MD-5等)

为什么要使用哈希函数?

确定性地加扰数据

输入无关紧要,输出大小相同

它们如何工作?

免责声明

什么是MD5哈希?

SHA-2的工作原理(SHA-256)

什么是哈希函数?

SHA-2和SHA-256

SHA-256“ hello world”;步骤1 –预处理

步骤2 –初始化哈希值(h)

步骤3 –初始化舍入常数(k)

步骤4 –区块循环

步骤5 –建立邮件时间表(w)

步骤6 –压缩

步骤7 –修改最终值

第8步-连接最终哈希

伪码

Scrypt哈希的基本介绍

什么是Scrypt?

为什么不使用密码直接加密?

加密属性

什么是BCrypt?

OWASP - 开放式Web应用程序安全项目

相关文章


 

Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

哈希函数根据选择的算法返回输入数据的128位,160位,256位或512位哈希。

表示要散列的字符串值的表达式。该表达式可以返回任何内置或不同的数据类型。唯一类型被视为其源数据类型。如果值为数字或日期时间,则在评估函数之前,将其隐式转换为VARCHAR。如果值为XML,则在对函数求值之前执行隐式的XMLSERIALIZE到CLOB(2G)CCSID 1208。

表1.哈希算法
功能名称算法结果大小返回值数结果长度HASH函数中使用的对应算法值
HASH_MD5MD5128位2^128160
HASH_SHA1SHA1160位2^160201
HASH_SHA256SHA-256256位2^256322
HASH_SHA512SHA-512512位2^512643

空格会影响哈希;带有尾随空格的固定长度字符串将产生与没有尾随空格的可变长度字符串不同的结果。表达式的CCSID 可以使比较相等的字符串生成不同的结果值。

SHA1和MD5算法均已发现安全漏洞。您可以在适用的法规遵从性文档中找到可接受的哈希算法,例如美国国家标准技术研究院(NIST)特殊出版物800-131A。

语法替代:具有单个参数的HASH函数类似于HASH_MD5。可以为HASH指定第二个参数,以指示要使用的算法。算法值显示在表1中。第二个参数可以是一个表达式,该表达式必须返回任何内置数字,字符串或图形字符串数据类型的值。在评估函数之前,将字符串参数转换为整数。HASH的结果数据类型为VARBINARY。如果只有一个参数,则结果的length属性为16。如果第二个参数由整数常量指定,则结果的length属性为表1中所示的结果长度。。否则,结果的length属性为64。如果任一参数可以为null,则结果可以为null。如果任何一个参数为null,则结果为null值。

例子

使用MD5算法生成哈希数据。

VALUES HEX(HASH_MD5('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))

返回以下值:

5156BECBC019E3F0F9520B143435427E

使用SHA1算法生成哈希数据。

VALUES HEX(HASH_SHA1('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))

返回以下值:

55324E3DD1FA95040F65709D193E82575237EF86

使用SHA-256算法生成哈希数据。

VALUES HEX(HASH_SHA256('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))

返回以下值:

011F7AD1ECD8E5A4CC8533D1ECD497DC5D95E848B14F8BCFD56A73D7F41843E2

使用SHA-512算法生成哈希数据。

VALUES HEX(HASH_SHA512('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))

返回以下值:

D8AC4B838921A83C4207B62B8B63628F8FBE836EB012167310331FFC070FC977D224F39148
  8806CB1FE2AA9C8C739E5104CAD1C4C6E97967DA6223D657CD9295

 

哈希函数的基本介绍(SHA-256,MD-5等)

https://dev.to/wagslane/very-basic-intro-to-hash-functions-sha-256-md-5-etc-399j


这将是哈希函数的基本介绍。我将假定我的大多数读者都在这里,以了解为什么使用哈希函数以及它们为什么起作用的基本概念。我的目标是从一般意义上解释它,我将省略证明和实现细节,而将重点放在高级原则上。

为什么要使用哈希函数?

哈希函数在整个Internet上使用,以安全地存储密码,查找重复记录,快速存储和检索数据等等。例如,Qvault使用哈希将主密码扩展为专用加密密钥。

  1.  https://en.wikipedia.org/wiki/Hash\_function#Uses
  2. https://en.wikipedia.org/wiki/Hash_function#Uses

我想关注散列函数的几个重要功能,可以说是最重要的功能。

  • 哈希函数确定性地加扰数据
  • 无论输入如何,哈希函数的输出始终具有相同的大小
  • 无法从加扰数据中检索原始数据(单向功能)

确定性地加扰数据

想想一个魔方。

我从无杂乱无章的多维数据集开始。如果我开始随机扭曲,到最后,我将得到的东西与我刚开始的东西几乎没有任何相似之处。另外,如果我要重新开始并做完全相同的一系列动作,我将能够反复获得完全相同的结果。即使结果may_出现_random,也完全不是。这就是确定性*的意思。

确定性对于安全存储密码很重要。例如,假设我的密码是“ iLoveBitcoin”

我可以使用哈希函数对其进行加扰:

iLoveBitcoin→“ 2f5sfsdfs5s1fsfsdf98ss4f84sfs6d5fs2d1fdf15”

现在,如果有人要查看加密版本,他们将不知道我的原始密码!这很重要,因为这意味着作为网站开发人员,我只需要存储用户密码的哈希(加密数据)即可进行验证。当用户注册时,我对密码进行哈希处理并将其存储在数据库中。当用户登录时,我只是对他们键入的内容进行哈希处理,然后比较两个哈希值。由于给定的输入始终会产生相同的哈希,因此每次都有效。

如果网站以纯文本形式(而不是散列的形式)存储密码,则将严重违反安全性。如果有人要入侵该站点的数据库并找到所有使用纯文本密码存储的电子邮件,则他们可以使用这些组合并在其他网站上尝试使用。

输入无关紧要,输出大小相同

如果我对单个单词进行哈希处理,则输出将具有一定的大小(对于SHA-256,则为特定的哈希函数,大小为256位)。如果我对一本书进行哈希处理,输出将是相同大小

这是另一个重要功能,因为它可以节省我们的计算时间。一个经典的例子是使用哈希作为数据映射中的键。数据映射是计算机科学中用于存储数据的简单结构。

当程序将数据存储在映射中时,键和值将被赋予映射。当程序要访问该值时,它可以将适当的键提供给映射并接收相应的值。数据映射之所以不错,是因为它们可以立即找到数据。_键用作计算机可以立即找到的地址,而不是花费数小时来搜索数百万条记录。

因为键就像地址,所以不能太大。如果我想将书籍存储在数据映射中,则可以对书籍的内容进行哈希处理并将哈希值用作键。作为程序员,我可以简单地使用哈希来查找书中的内容,而不必尝试按标题,作者等对数千条记录进行排序。

它们如何工作?

这是撰写本文的真正挑战。我将使其保持极其简单,并省略实际的实现细节,同时让您基本了解计算机在处理某些数据时的实际工作。

让我们来看一下我正在为这个演示动态编写的示例算法 LANEHASH:

  • 我们从一些要散列的数据开始

iLove比特币

  • 我将字母和数字转换为1和0(计算机中的所有数据都存储在1和0中,不同的1和0表示不同的字母)

iLoveBitcoin→100010100000101111

  • 在这一点上,我们经历了各种预定步骤来转换我们的数据。这些步骤可以是任何步骤,重要的是,每当我们使用LANEHASH时,我们都需要使用相同的步骤,以便我们的算法具有确定性。
  • 我们将前四位从左侧移到右侧

1000 10100000101111→10100000101111 1000

  • 我们隔着一点

1 0 1 0 0 0 0 0 1 0 1 1 1 1 1 0 0 0→110011110&000001100

  • 我们将这两部分转换为以10为底的数字。以10为基数是我们在学校都学过的“正常”数字系统。(所有二进制数据实际上只是数字,您可以在其他地方在线查找如何将二进制轻松转换为以10为基数)

110011110→414

000001100→12

  • 我们将两个数字相乘

414 * 12 = 4968

  • 我们对该数字求平方

4968 ^ 2 = 24681024

  • 我们将该数字转换回二进制

24681024→1011110001001101001000000

  • 我们从右侧切掉9位以得到16位

1011110001001101 001000000 →1011110001001101

  • 我们将二进制数据转换回英语

1011110001001101→“ 8sj209dsns02k2”

如您所见,如果在开头以相同的单词开头,那么在结尾始终将得到相同的输出。但是,即使您只更改一个字母,结果也将发生巨大变化。

免责声明

在我从英语转换为二进制,以及从二进制转换为英语的步骤中,我没有遵循任何模式。不要让那让您感到困惑。有很多不同的方法可以将二进制数据转换为英语,然后再转换回英语,我只是不想在本文中对此感到困惑。以下是有关该主题的一些参考资料:

https://zh.wikipedia.org/wiki/ASCII

https://zh.wikipedia.org/wiki/Unicode

 

什么是MD5哈希?

https://www.md5hashgenerator.com/


通过采用任意长度的字符串并将其编码为128位指纹来创建MD5哈希。使用MD5算法编码相同的字符串将始终导致相同的128位哈希输出。当在数据库(例如流行的MySQL)中存储密码,信用卡号或其他敏感数据时,MD5哈希通常与较小的字符串一起使用。该工具提供了一种快速简便的方法,可以从长度最多为256个字符的简单字符串中编码MD5哈希。

MD5哈希还用于确保文件的数据完整性。由于MD5哈希算法始终为相同的给定输入产生相同的输出,因此用户可以将源文件的哈希值与目标文件的新创建的哈希值进行比较,以检查其是否完整且未修改。

MD5哈希不是加密的。它只是给定输入的指纹。但是,这是单向事务,因此几乎不可能对MD5哈希进行反向工程以检索原始字符串。

 

 

SHA-2的工作原理(SHA-256)

https://dev.to/wagslane/how-sha-2-works-step-by-step-sha-256-11ci

https://qvault.io/2020/07/08/how-sha-2-works-step-by-step-sha-256/


 

SHA-2(安全哈希算法2)是SHA-256的一部分,是其中最流行的哈希算法之一。在本文中,我们将尽可能简单地分解算法的每个步骤,并手工完成一个真实的示例。

SHA-2以其安全性(它没有像SHA-1一样崩溃)和速度而闻名。在未生成密钥的情况下(例如挖矿比特币),像SHA-2这样的快速哈希算法通常占据上风。

什么是哈希函数?

如果您想全面了解散列函数,请在此处阅读。也就是说,为了前进,让我们回顾一下哈希函数的三个主要目的:

  • 确定性地加扰数据
  • 接受任意长度的输入并输出固定长度的结果
  • 不可逆地操纵数据。输入不能从输出派生

SHA-2和SHA-256

SHA-2是一种算法,是有关如何对数据进行哈希处理的通用思想。SHA-256设置其他常量,这些常量定义SHA-2算法的行为。这样的常数之一就是输出大小。“ 256”和“ 512”以比特为单位表示它们各自的输出摘要大小。

让我们逐步介绍一下SHA-256的示例。

SHA-256“ hello world”;步骤1 –预处理

将“ hello world”转换为二进制:

01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111
01110010 01101100 01100100

附加一个1:

01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111
01110010 01101100 01100100 1

用0填充,直到数据为512的整数倍,然后减去64位(本例中为448位):

01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111
01110010 01101100 01100100 10000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

在末尾附加64位,其中64位是big-endian整数,表示二进制原始输入的长度。在我们的例子中,为88,或二进制为“ 1011000”。

01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111
01110010 01101100 01100100 10000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 01011000

现在我们有了输入,它将始终被512平均整除。

步骤2 –初始化哈希值(h)

现在,我们创建8个哈希值。这些是硬编码的常数,代表前8个素数的平方根的小数部分的前32位:2、3、5、7、11、13、17、19

h0 := 0x6a09e667
h1 := 0xbb67ae85
h2 := 0x3c6ef372
h3 := 0xa54ff53a
h4 := 0x510e527f
h5 := 0x9b05688c
h6 := 0x1f83d9ab
h7 := 0x5be0cd19

步骤3 –初始化舍入常数(k)

与第2步类似,我们正在创建一些常量(在此处了解有关常量以及何时使用它们的更多信息)。这次有64个。每个值(0-63)是前64个素数(2 – 311)的立方根的小数部分的前32位。

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

步骤4 –区块循环

对于来自我们输入的每个512位“块”数据,将执行以下步骤。在我们的案例中,由于“ hello world”非常短,因此我们只有一个块。在循环的每次迭代中,我们将使哈希值h0-h7突变,这将是最终输出。

步骤5 –建立邮件时间表(w)

将步骤1中的输入数据复制到一个新数组中,其中每个条目都是一个32位字:

01101000011001010110110001101100 01101111001000000111011101101111
01110010011011000110010010000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000001011000

再添加48个初始化为零的单词,这样我们就有了一个数组w [0…63]

01101000011001010110110001101100 01101111001000000111011101101111
01110010011011000110010010000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000001011000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
...
...
00000000000000000000000000000000 00000000000000000000000000000000

使用以下算法在数组末尾修改零位索引:

对于从W [16 ... 63]:

s0 =(w [i-15]右旋7)xor(w [i-15]右旋18)xor(w [i-15]右移3)

s1 =(w [i-2]右旋17)xor(w [i-2]右旋19)xor(w [i-2]右移10)

w [i] = w [i-16] + s0 + w [i-7] + s1

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

让我们做w [16]以便我们看一下它是如何工作的:

w[1] rightrotate 7:
  01101111001000000111011101101111 -> 11011110110111100100000011101110
w[1] rightrotate 18:
  01101111001000000111011101101111 -> 00011101110110111101101111001000
w[1] rightshift 3:
  01101111001000000111011101101111 -> 00001101111001000000111011101101

s0 = 11011110110111100100000011101110 XOR 00011101110110111101101111001000 XOR 00001101111001000000111011101101

s0 = 11001110111000011001010111001011

w[14] rightrotate 17:
  00000000000000000000000000000000 -> 00000000000000000000000000000000
w[14] rightrotate19:
  00000000000000000000000000000000 -> 00000000000000000000000000000000
w[14] rightshift 10:
  00000000000000000000000000000000 -> 00000000000000000000000000000000

s1 = 00000000000000000000000000000000 XOR 00000000000000000000000000000000 XOR 00000000000000000000000000000000

s1 = 00000000000000000000000000000000

w[16] = w[0] + s0 + w[9] + s1

w[16] = 01101000011001010110110001101100 + 11001110111000011001010111001011 + 00000000000000000000000000000000 + 00000000000000000000000000000000

// addition is calculated modulo 2^32

w[16] = 00110111010001110000001000110111

这使我们在消息时间表(w)中留下了64个单词:

01101000011001010110110001101100 01101111001000000111011101101111
01110010011011000110010010000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000000000000
00000000000000000000000000000000 00000000000000000000000001011000
00110111010001110000001000110111 10000110110100001100000000110001
11010011101111010001000100001011 01111000001111110100011110000010
00101010100100000111110011101101 01001011001011110111110011001001
00110001111000011001010001011101 10001001001101100100100101100100
01111111011110100000011011011010 11000001011110011010100100111010
10111011111010001111011001010101 00001100000110101110001111100110
10110000111111100000110101111101 01011111011011100101010110010011
00000000100010011001101101010010 00000111111100011100101010010100
00111011010111111110010111010110 01101000011001010110001011100110
11001000010011100000101010011110 00000110101011111001101100100101
10010010111011110110010011010111 01100011111110010101111001011010
11100011000101100110011111010111 10000100001110111101111000010110
11101110111011001010100001011011 10100000010011111111001000100001
11111001000110001010110110111000 00010100101010001001001000011001
00010000100001000101001100011101 01100000100100111110000011001101
10000011000000110101111111101001 11010101101011100111100100111000
00111001001111110000010110101101 11111011010010110001101111101111
11101011011101011111111100101001 01101010001101101001010100110100
00100010111111001001110011011000 10101001011101000000110100101011
01100000110011110011100010000101 11000100101011001001100000111010
00010001010000101111110110101101 10110000101100000001110111011001
10011000111100001100001101101111 01110010000101111011100000011110
10100010110101000110011110011010 00000001000011111001100101111011
11111100000101110100111100001010 11000010110000101110101100010110

步骤6 –压缩

初始化变量a,b,c,d,e,f,g,h,并将它们分别设置为等于当前的哈希值。h0,h1,h2,h3,h4,h5,h6,h7

运行压缩循环。压缩循环将使a…h的值发生变化。压缩循环如下:

我从0到63

S1 =(e右旋转6)xor(e右旋转11)xor(e右旋转25)

ch =(e和f)xor((not e)和g)

temp1 = h + S1 + ch + k [i] + w [i]

S0 =(右旋转2)xor(右旋转13)xor(右旋转22)

maj =(a和b)xor(a和c)xor(b和c)

temp2:= S0 + maj

h =克

g = f

e = d +温度1

d = c

c = b

b = a

a = temp1 + temp2

S1 = (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
ch = (e and f) xor ((not e) and g)
temp1 = h + S1 + ch + k[i] + w[i]
S0 = (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
maj = (a and b) xor (a and c) xor (b and c)
temp2 := S0 + maj
h = g
g = f
e = d + temp1
d = c
c = b
b = a
a = temp1 + temp2

让我们进行第一次迭代,所有加法都是以2 ^ 32模的

a = 0x6a09e667 = 01101010000010011110011001100111
b = 0xbb67ae85 = 10111011011001111010111010000101
c = 0x3c6ef372 = 00111100011011101111001101110010
d = 0xa54ff53a = 10100101010011111111010100111010
e = 0x510e527f = 01010001000011100101001001111111
f = 0x9b05688c = 10011011000001010110100010001100
g = 0x1f83d9ab = 00011111100000111101100110101011
h = 0x5be0cd19 = 01011011111000001100110100011001

e rightrotate 6:
  01010001000011100101001001111111 -> 11111101010001000011100101001001
e rightrotate 11:
  01010001000011100101001001111111 -> 01001111111010100010000111001010
e rightrotate 25:
  01010001000011100101001001111111 -> 10000111001010010011111110101000
S1 = 11111101010001000011100101001001 XOR 01001111111010100010000111001010 XOR 10000111001010010011111110101000
S1 = 00110101100001110010011100101011

e and f:
    01010001000011100101001001111111
  & 10011011000001010110100010001100 =
    00010001000001000100000000001100
not e:
  01010001000011100101001001111111 -> 10101110111100011010110110000000
(not e) and g:
    10101110111100011010110110000000
  & 00011111100000111101100110101011 =
    00001110100000011000100110000000
ch = (e and f) xor ((not e) and g)
   = 00010001000001000100000000001100 xor 00001110100000011000100110000000
   = 00011111100001011100100110001100

// k[i] is the round constant
// w[i] is the batch
temp1 = h + S1 + ch + k[i] + w[i]
temp1 = 01011011111000001100110100011001 + 00110101100001110010011100101011 + 00011111100001011100100110001100 + 1000010100010100010111110011000 + 01101000011001010110110001101100
temp1 = 01011011110111010101100111010100

a rightrotate 2:
  01101010000010011110011001100111 -> 11011010100000100111100110011001
a rightrotate 13:
  01101010000010011110011001100111 -> 00110011001110110101000001001111
a rightrotate 22:
  01101010000010011110011001100111 -> 00100111100110011001110110101000
S0 = 11011010100000100111100110011001 XOR 00110011001110110101000001001111 XOR 00100111100110011001110110101000
S0 = 11001110001000001011010001111110

a and b:
    01101010000010011110011001100111
  & 10111011011001111010111010000101 =
    00101010000000011010011000000101
a and c:
    01101010000010011110011001100111
  & 00111100011011101111001101110010 =
    00101000000010001110001001100010
b and c:
    10111011011001111010111010000101
  & 00111100011011101111001101110010 =
    00111000011001101010001000000000
maj = (a and b) xor (a and c) xor (b and c)
    = 00101010000000011010011000000101 xor 00101000000010001110001001100010 xor 00111000011001101010001000000000 
    = 00111010011011111110011001100111

temp2 = S0 + maj
      = 11001110001000001011010001111110 + 00111010011011111110011001100111
      = 00001000100100001001101011100101

h = 00011111100000111101100110101011
g = 10011011000001010110100010001100
f = 01010001000011100101001001111111
e = 10100101010011111111010100111010 + 01011011110111010101100111010100
  = 00000001001011010100111100001110
d = 00111100011011101111001101110010
c = 10111011011001111010111010000101
b = 01101010000010011110011001100111
a = 01011011110111010101100111010100 + 00001000100100001001101011100101
  = 01100100011011011111010010111001

整个计算又进行了63次,从而始终修改了变量ah。我们不会手工完成,但是我们将提供ender:

h0 = 6A09E667 = 01101010000010011110011001100111
h1 = BB67AE85 = 10111011011001111010111010000101
h2 = 3C6EF372 = 00111100011011101111001101110010
h3 = A54FF53A = 10100101010011111111010100111010
h4 = 510E527F = 01010001000011100101001001111111
h5 = 9B05688C = 10011011000001010110100010001100
h6 = 1F83D9AB = 00011111100000111101100110101011
h7 = 5BE0CD19 = 01011011111000001100110100011001

a = 4F434152 = 001001111010000110100000101010010
b = D7E58F83 = 011010111111001011000111110000011
c = 68BF5F65 = 001101000101111110101111101100101
d = 352DB6C0 = 000110101001011011011011011000000
e = 73769D64 = 001110011011101101001110101100100
f = DF4E1862 = 011011111010011100001100001100010
g = 71051E01 = 001110001000001010001111000000001
h = 870F00D0 = 010000111000011110000000011010000

步骤7 –修改最终值

在压缩循环之后,但仍然在循环中,我们通过将哈希值添加到变量ah中来修改哈希值。像往常一样,所有加法都是模2 ^ 32

h0 = h0 + a = 10111001010011010010011110111001
h1 = h1 + b = 10010011010011010011111000001000
h2 = h2 + c = 10100101001011100101001011010111
h3 = h3 + d = 11011010011111011010101111111010
h4 = h4 + e = 11000100100001001110111111100011
h5 = h5 + f = 01111010010100111000000011101110
h6 = h6 + g = 10010000100010001111011110101100
h7 = h7 + h = 11100010111011111100110111101001

第8步-连接最终哈希

最后但并非最不重要的一点是,将它们全部拍打!

digest = h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7
       = B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9

做完了!我们已经仔细研究了SHA-256的每个步骤(没有重复),🙂

 

伪码

如果您想以伪代码形式查看上面刚刚完成的所有步骤,那么直接来自WikiPedia

Note 1: All variables are 32 bit unsigned integers and addition is calculated modulo 232
Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 ≤ i ≤ 63
Note 3: The compression function uses 8 working variables, a through h
Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
    and when parsing message block data from bytes to words, for example,
    the first word of the input message "abc" after padding is 0x61626380

Initialize hash values:
(first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
h0 := 0x6a09e667
h1 := 0xbb67ae85
h2 := 0x3c6ef372
h3 := 0xa54ff53a
h4 := 0x510e527f
h5 := 0x9b05688c
h6 := 0x1f83d9ab
h7 := 0x5be0cd19

Initialize array of round constants:
(first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
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

Pre-processing (Padding):
begin with the original message of length L bits
append a single '1' bit
append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512
append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits

Process the message in successive 512-bit chunks:
break message into 512-bit chunks
for each chunk
    create a 64-entry message schedule array w[0..63] of 32-bit words
    (The initial values in w[0..63] don't matter, so many implementations zero them here)
    copy chunk into first 16 words w[0..15] of the message schedule array

    Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
    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

    Initialize working variables to current hash value:
    a := h0
    b := h1
    c := h2
    d := h3
    e := h4
    f := h5
    g := h6
    h := h7

    Compression function main loop:
    for i from 0 to 63
        S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
        ch := (e and f) xor ((not e) and g)
        temp1 := h + S1 + ch + k[i] + w[i]
        S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
        maj := (a and b) xor (a and c) xor (b and c)
        temp2 := S0 + maj
 
        h := g
        g := f
        f := e
        e := d + temp1
        d := c
        c := b
        b := a
        a := temp1 + temp2

    Add the compressed chunk to the current hash value:
    h0 := h0 + a
    h1 := h1 + b
    h2 := h2 + c
    h3 := h3 + d
    h4 := h4 + e
    h5 := h5 + f
    h6 := h6 + g
    h7 := h7 + h

Produce the final hash value (big-endian):
digest := hash := h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7

 

Scrypt哈希的基本介绍

https://dev.to/wagslane/very-basic-intro-to-the-scrypt-hash-7l5


这将是Scrypt哈希函数或更准确地说是KDF函数的基本介绍。我将假定我的大多数观众都在这里,以了解使用Scrypt原因以及其工作原理基础。我的目标是从一般意义上解释它,我将省略证明和实现细节,而将重点放在高级原则上。

密码学中scrypt(发音为“ ess crypt” [1])是由Colin Percival创建的基于密码的密钥派生功能,最初用于Tarsnap在线备份服务。[2] 该算法经过专门设计,使其需要大量内存,因此执行大规模自定义硬件攻击的成本很高。IETF在2016年将scrypt算法发布为RFC 7914。scrypt的简化版本被许多加密货币用作工作量证明方案,首先由位于Tenebrix的名为ArtForz的匿名程序员实现,随后不久又由Fairbrix和Litecoin实现[3] -- https://en.wikipedia.org/wiki/Scrypt

什么是Scrypt?

Scrypt是一种设计缓慢的 哈希函数。其目的是获取一些输入数据,并为该数据创建指纹,但是要非常缓慢地进行。Qvault如何使用其真实案例的最佳示例之一。即,使用密码并创建256位私有密钥

例如,假设您的密码为password1234。通过使用scrypt,我们可以确定性地将其扩展为256位密钥:

password1234- >
AwEEDA4HCwQFAA8D
AwwHDQwPDwUOBwoO
CQACAgUJBQ0JAAYN
BAMCDQ4JCQgLDwcG
DQMDDgMKAQsNBAkL
AwsACA ==

现在可以将该长256位密钥用作私有密钥,以使用AES-256密码对数据进行加密和解密。

为什么不使用密码直接加密?

大多数加密算法,包括AES-256,都要求使用足够长的密钥。通过对密码进行哈希处理,我们得到了更长且固定大小的密钥。

此外,出于两个原因,我们选择使用scrypt算法,而不是使用SHA-256之类的更快的哈希值:

  • 慢一点
  • 它使用内存以及CPU资源

我们想要慢速散列的原因是,攻击者很难猜测用户的密码。如果攻击者试图强行闯入金库,则意味着他们只是反复猜测密码以进行破解。AES-256速度非常快,因此,攻击者将能够尝试许多密码在现代计算机上每秒传输一次。

由于攻击者必须先对每个密码运行一个scrypt哈希,然后才能尝试对库进行解密,因此攻击变得如此缓慢,几乎不可能猜出密码。在功能相对强大的台式计算机上,散列Qvault密码大约需要1.5秒,因为我们将内存和计算要求设置得很高。

加密属性

像所有散列函数一样,scrypt具有以下属性:

  • 确定性的(每次相同的输入都会产生相同的输出)
  • 固定尺寸的输出
  • 不可逆的(通过使用输出,攻击者无法找到输入)

此外,Scrypt具有以下属性:

  • 计算昂贵且速度慢(计算机运行哈希需要很长时间)
  • 内存密集型(可能使用数GB的RAM来运行哈希)

 

什么是BCrypt?

https://dev.to/sylviapap/bcrypt-explained-4k5c


BCrypt是由OpenBSD项目的Niels Provos和DavidMazières在1999年设计的一种哈希算法。B代表...

如果您是初学者密码学的专家,那么您来对地方了。也许您只是进入Rails并想添加用户登录/注销功能。也许您真的很喜欢加密哈希算法。我肯定是!如果您真的是一个初学者,那么一个名为Khan Academy的小型网站上有一些关于加密的特别出色的视频

这并不是真正的教程,但是在使用BCrypt时,请始终记住取消注释Rails Gemfile中的gem。

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.1'

...

# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
gem 'bcrypt', '~> 3.1.7'

...

 

现在,您可以做一些很酷的事情:

# Encrypts password
my_password = BCrypt::Password.create('iLOVEdogs123')
 => "$2a$10$kypbnGGCpJ7UQlysnqzJG.6H.dUewn7UPVWA3Ip.E.8U4jlVnFNnu"

# Tests if input matches
my_password == 'iLOVEdogs123'
 => true 

my_password == 'ilovedogs12'
 => false

# Not super important for this post, but this is why the above crypt begins with "$2a$"
my_password.version
 => "2a" 

# "Cost" factor - how quickly the password is encrypted
my_password.cost
 => 10 

现在,您可以使用BCrypt对应用程序执行很多操作-身份验证,授权,登录,注销等!但是,已经有很多博客文章和有关如何编写代码的有用文档。我对幕后动作更感兴趣。

Bcrypt是怎么加密的?

Bcrypt有四个变量:

  1. saltRounds: 正数,代表hash杂凑次数,数值越高越安全,默认10次。
  2. myPassword: 明文密码字符串。
  3. salt: 盐,一个128bits随机字符串,22字符
  4. myHash: 经过明文密码password和盐salt进行hash,个人的理解是默认10次下 ,循环加盐hash10次,得到myHash

每次明文字符串myPassword过来,就通过10次循环加盐salt加密后得到myHash, 然后拼接BCrypt版本号+salt盐+myHash等到最终的bcrypt密码 ,存入数据库中。

这样同一个密码,每次登录都可以根据自省业务需要生成不同的myHash, myHash中包含了版本和salt,存入数据库。
bcrypt密码图解:(此部分来源:https://www.jianshu.com/p/2b131bfc2f10

如Spring Security crypto 项目中实现的BCrypt方法加密:BCrypt.hashpw(myPassword, BCrypt.gensalt())

那即使黑客得到了bcrypt密码,他也无法转换明文,因为之前说了bcrypt是单向hash算法

 

OWASP - 开放式Web应用程序安全项目


开放式Web应用程序安全项目(OWASP,Open Web Application Security Project)是一个组织,它提供有关计算机和互联网应用程序的公正、实际、有成本效益的信息。其目的是协助个人、企业和机构来发现和使用可信赖软件。

OWASP Top 10是针对开发人员和Web应用程序安全性的标准意识文档。它代表了对Web应用程序最严重的安全风险的广泛共识。

 

相关文章


 

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值