密码学安全强随机数生成器
但是首先,当我说“强密码学”时,我到底指的是什么?
强密码学或强密码学是应用于密码系统或组件的通用术语,被认为对密码分析具有很高的抵抗力。
http://en.wikipedia.org/wiki/Strong_cryptography
因此,“强密码术”并不是一个您并不陌生的神秘概念:“强密码术”只是一组定义和算法,已经由专家 , 秘密政府机构和第三方组织进行了审查,发现很难破解。
我反复看到的一件事是,开发人员出于特定目的“发明”了一种加密方案。 事情就是这样,加密技术已有数千年的历史了。 如果您曾经“发明” 自己的方法来“加密”数据,那么您很有可能只是重新发明了几千年前发现的东西。 如果您想避免WEP在无线上犯的错误,微软在XBox上犯的错误或索尼在PS3上犯的错误,那么本博客系列应该可以帮助您避免尴尬,并在下一次鸡尾酒会上给您留下深刻的印象。
最后,我只想提一下,这实际上是我非常悠久的个人主题。 我发现加密的第一个需求是在我们附近玩“间谍”时将笔记传递给我的朋友,并且需要确保秘密要塞的位置安全。 不幸的是,当我们的树屋在那个夏天被摧毁时,我的单字母替换密码一定已经被一个神童打断了……阅读了《 阿尔文的秘密密码》之后 ,我们创造了2-3套凯撒轮 ,再也没有失去一个秘密堡垒!
下面,我们将介绍最有用的“强密码”原语之一:哈希函数。 这些小野兽令人惊叹,并具有多种用途!
那么,什么是哈希函数? 您很高兴,让我们引述不减的Wikipedia:
散列函数是任何定义明确的过程或数学函数,可将大量(可能大小可变的)数据转换成小的数据,通常是单个整数,可以用作数组的索引(参见关联数组)。 哈希函数返回的值称为哈希值,哈希码,哈希和,校验和或简称为哈希。http://zh.wikipedia.org/wiki/哈希函数
igh,维基百科曾经为普通人提供了很好的用户友好定义,因为事实并非如此,让我给出自己的定义:
(非常糟糕的)哈希函数可能很简单,例如“将所有内容一分为二,直到剩下一位为止,然后四舍五入。” 我们可以使用此函数ƒ(ƒ表示哈希函数的失败),因此ƒ(48)= 6或ƒ(451)= 8。
使用此功能ƒ的一种简单方法是保护网站。 用户创建帐户时,他们选择一个数字作为密码(p)。 在帐户创建过程中,我们计算一个新数字(c =ƒ(p))并将其存储给用户。 在每个用户的数据库中,我们最终得到(c)数字0-9。
为了对照数据库检查提供的密码,我们只需评估(c ==ƒ(s))。
这个散列函数ƒ在现实世界中并不是真正有用,因为我们的键空间只有9种可能性,它的缩放比例惊人,并且反向工程是微不足道的,但是如果用失效函数ƒ替代更好的东西,我们最终得到一个安全系统!
理想哈希算法的技术特性
- 它应该代表一个真正的多对一功能 。 它应该接受一个输入,并生成一个身份输出,该身份输出可以每次精确地复制。 对于每个明文,只有一个哈希值。 对于每个哈希值,都有无限多个纯文本。
- 输入的明文可以是任何长度。 是的,但请看下一点:
- 输出哈希值始终是相同大小,这意味着它是固定长度。 因此,如果为该算法提供50字节或765kbytes的明文,则哈希值(输出)的长度将为16个字节。 输出的确切长度取决于算法设计。
- 对于相同的明文(输入),总是产生相同的哈希值(输出)。 因此,如果您对报价单进行散列, “我坚持公共债务是公共诅咒的原则”,那么您应该每次获得值vjPWnnLiB0BLrqUuJjtpEM5KClg = 。 这称为识别,指纹化或对纯文本进行哈希处理。
- 明文中的微小变化将导致哈希值发生巨大变化。 因此,如果您将麦迪逊的报价改为“ 我坚持公共债务不是公共诅咒的原则”,则哈希值现在为: g + 8o7vlQAGjvlst + XsOEwIzF0Qc = 。
- 永久且“单向”,这意味着hashValue不能反转为纯文本。
- 在给定特定的hashValue的情况下,很难找到将产生所需hashValue的纯文本。 最后两点是一起运行的……基本上,我们的功能ƒ远非理想。 假设您有(p = 5)。 计算将产生(5 =ƒ(s))的输入非常容易。 您可以使用(s = {10,20,40})中的任何一个。 理想的功能应该使此操作极其困难!!!
SHA1,标准哈希算法
SHA-1 (安全哈希算法– 1)是一种非常复杂的哈希算法,在撰写本文时,它对于大多数应用程序是安全的(偏执狂应该使用SHA-256或SHA-512)。 我们可以向SHA-1提供任何长度的纯文本,并作为回报接收一致的20字节哈希值。 这是使用Ruby的方法:
exabrial@arclight:~$ irb
>> require 'base64'
=> true
>> require 'openssl'
=> true
>> shaBytes = Digest::SHA1.digest('Give me liberty, or give me death!')
=> "\367\241\266`30 \2656\214G343\266\276\204\225cB\370\r"
>> puts Base64.encode64(shaBytes)
96G2YBggtTaMRxwztr6ElWNC+A0=
=> nil
>> exit
您可以使用在线哈希计算器来检查Patrick Henry的报价是否正确哈希。 来吧,我等...
SHA-1(Base64) : 96G2YBggtTaMRxwztr6ElWNC+A0=
如果我们对纯文本进行一些更改,请说:“ 给我自由,或者让我吃蛋糕! ”,我们将获得截然不同的输出:
SHA-1(Base64) : M2vPzwTPc7ALM+OkiGAwCkS1DY4=
实际上,只需更改第一个字符的大小写“ 给我自由,否则就给我死亡! ”将产生完全不同的hashValue:
SHA-1(Base64) : g1UFdWJfXWfkIVz42uLLxrJv58g=
哈希算法输入确实是无限的
还记得我们怎么说哈希函数的输入可以是无限的吗? 如何对整个文本独立(12KB)的宣言? 如果将文件上传到我们一直在使用的在线计算器 ,结果仍然是20个字节:
SHA-1(Base64): mGnTR5dnrXrMqEMVoLKCMzWEHjU=
这是处理相同计算的快速Java程序:
import java.io.InputStreamReader;
import java.nio.CharBuffer;
import java.security.MessageDigest;
import org.apache.commons.codec.binary.Base64;
public class SHA1Hasher {
public static void main(String[] args) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA1");
InputStreamReader insr = new InputStreamReader(SHA1Hasher.class.getResourceAsStream("/usdeclar.txt"));
CharBuffer charBuff = CharBuffer.allocate(16000);
String plainText;
byte[] hashBytes;
String hashValue;
insr.read(charBuff);
plainText = charBuff.flip().toString();
hashBytes = md.digest(plainText.getBytes());
hashValue = Base64.encodeBase64String(hashBytes);
System.out.println("SHA-1(Base64): " + hashValue);
}
}
散列算法的实际用法,场景1:以安全的方式将帐户密码存储在数据库中
您的任务是创建一个针对美国殖民抵抗运动的网站。 英国人比比皆是,并且正在随机检查每个人的数据库是否有可疑活动。 开国元勋们习惯选择“ I <3America”这样的爱国密码。 如何掩盖英国巡逻队的密码,从而使谁忠于王室和谁是革命者的秘密保密?
最好的办法是创建一个“盐密码表”,如下所示:
用户名 | PASSWORD_HASH | 盐 |
---|---|---|
tjefferson43 | HiE0AZhRWAs6Mmd7dVqppM1WtJc = | WTg68cVxPI |
我们将要做的是存储密码哈希,再加上一些随机的文本,称为盐(salt)(以增加用户密码的熵)。 因此,托马斯·杰斐逊的原始密码: I <3America现在变成了I <3AmericaWTg68cVxPI ,并被永久哈希为: HiE0AZhRWAs6Mmd7dVqppM1WtJc =。
那么,既然明文不见了,您如何处理登录? 您执行相同的计算:从登录表单中获取用户名,然后查找salt。 然后从登录表单中获取纯文本密码,并从数据库中连接盐,以重新形成原始字符串。 哈希该字符串,如果匹配数据库哈希,则您知道提供了正确的密码! 杰斐逊的原始爱国密码已被完全掩盖,因此那些讨厌的保守党永远不会知道他的真正忠诚! 耶!
但是,如果另一个爱国者(Paul Revere)选择了完全相同的密码却被英国人抓住了怎么办? 难道英国正规军不能只是推断出具有相同密码哈希的任何人也是爱国者(并被吊死)?
因为我们在纯文本密码中添加了盐(熵),所以我们保证在数据库中永远不会有相同的哈希值两次。 可以说,保罗·里维尔(Paul Revere)使用相同的密码创建了一个帐户: I <3America ,但是我们遵循了使用SecureRandom创建新盐的要求:
用户名 | PASSWORD_HASH | 盐 |
---|---|---|
tjefferson43 | HiE0AZhRWAs6Mmd7dVqppM1WtJc = | WTg68cVxPI |
1776年 | aktJ / 0cn69Y41vssNfZDHY1CsdE = | sWVUaTGa6e |
因此,保罗·里维尔(Paul Revere)的密码I <3America变为I <3AmericasWVUaTGa6e ,该密码被哈希化为 : aktJ / 0cn69Y41vssNfZDHY1CsdE =并存储。 后来,当保罗·里维尔(Paul Revere)被捕并被证明是爱国者时,杰斐逊的秘密通过撒盐和哈希来保持安全。
总结好处:
- 违反数据库意味着您的用户帐户仍然安全。
- 无法确定两个人是否使用相同的密码。
- 用户密码的长度没有限制。
- 您的数据库列可以是固定长度,而不是varchar。
- SHA1是由比您(和我)更聪明的人创建的。 您可以放低自我,利用他们的大脑。
是的,诺亚·韦伯斯特(Noah Webster)是一位美国革命者:他是康涅狄格州民兵的一员,《联邦主义者》报纸的编辑,美国版权的作者,等等。 他后来生活着写一些字典的东西,但是重要的是在教学上他是一个真正的硬汉。
可以这么说,诺亚·韦伯斯特(Noah Webster)雇用您编写了一个程序来测试他的学生的拼写。 因为他坚信正确的拼写,所以每个学生获得100%的分数很重要。 知道这有点不切实际,他将允许学生根据需要多次重新参加测试,直到获得100%为止。 该测试由Webster先生规定,该文本规定要向班级发送一小段文本,并让他们正确地将其输入到程序中。 该程序应告诉他们是否正确拼写了所有内容。 这将使他们在上交最终答案之前先找出正确的答案。
美国历史上的另一个重要方面是,诺亚·韦伯斯特(Noah Webster)是壁橱里的Ruby迷。 您必须用Ruby编写。 这就提出了一个问题,因为学生将要运行您的程序,您不能只是在程序中嵌入正确的答案,否则他们可以查看源代码。
通过使用哈希,您可以在程序中嵌入正确文本的哈希,然后将学生输入的内容与哈希进行比较。
韦伯斯特先生对学生说:
在常备军统治之前,必须解除人民的武装; 就像他们在几乎每个欧洲王国中一样 美国的最高权力不能用剑来执行不公正的法律。 因为整个人民的武装,并构成了一支可以在美国假装以任何名义升格的正规军。
正确的哈希值为:6 + IHAAJ1i0KmoPaowU1i0xSjcO4 =
知道您现在知道的知识后,您可以为韦伯斯特先生完成课程吗? 在评论中发布您的答案!
今天,我们将深入探讨一些哈希主题。 我也在帖子中为主题提供建议……上次是美国开国元勋,这周是悲剧。
我们在上一篇文章中了解了什么是哈希函数,以及对于每个输入如何恰好有一个对应的输出。 我们将把这个概念应用于需要保密的两种常见情况。
常见情况1:维罗纳的情况
假设您有罗密欧,他需要通过电子邮件将以下消息发送给Mercutio: “我今晚做梦了。” 对于罗密欧来说,不幸的是,维罗纳的所有ISP都是Montague家族所有的(比Comcast更好)。 罗密欧不介意蒙塔古斯能否读懂他的信息,但他当然有一个问题,他们会改变他的信息。 罗密欧如何确定他的信息没有被蒙塔古家族修改?
好了,您可以通过哈希函数运行消息,并将哈希值附加到消息中。 但是,Montagues可以简单地修改消息,并附加新的哈希值。 为了解决这个问题,我认为Romeo可以计算哈希值,然后私下告诉Mercutio哈希值是什么。 但是,这会打消给他发电子邮件的目的,因为私下里他只能告诉Mercutio邮件的内容。
有任何想法吗?
引入新的MAC(不是您的想法)
幸运的是,有一个简单的解决方案:消息验证码或MAC。 MAC函数是“键控哈希函数”,因此您可以将其称为普通哈希函数的表亲。 实际上,MAC函数和哈希函数几乎可以互换! 在上一篇文章中 ,我们介绍了哈希函数的属性。 那么,什么使MAC功能与哈希功能不同呢?
- 您必须拥有一个秘密密钥才能产生哈希值。
- 即使攻击者选择了输入,MAC功能也不应泄露有关密钥的“提示”。
private static final byte[] keybytes = new byte[] { (byte) 0xfc, (byte) 0x4f, (byte) 0xbe, (byte) 0x23,
(byte) 0x59, (byte) 0xf2, (byte) 0x42, (byte) 0x37, (byte) 0x4c, (byte) 0x80, (byte) 0x44, (byte) 0x31,
(byte) 0x20, (byte) 0xda, (byte) 0x20, (byte) 0x0c };
public static void main(String[] args) throws Exception {
Mac mac = Mac.getInstance("HMACSHA1");
Key key = new SecretKeySpec(keybytes, "HMACSHA1");
mac.init(key);
byte[] hashValue = mac.doFinal("The lady doth protest too much, methinks".getBytes());
String encodedHashValue = Base64.encodeBase64String(hashValue);
System.out.println(encodedHashValue);
}
上面的程序产生以下输出:
oOUKVR5hDRh4n0H+beVO4JvMw64=
如果使用其他键(将最后一个字节更改为0x0d ),则会得到完全不同的哈希值:
cDdJwEBm9qIni9A7QfIQ1e9G8qo=
那么如何适用于罗密欧与默尔蒂奥? 首先,他们私下聚会并创建一个秘密密钥。 Romeo和Mercutio都将知道密钥。 接下来,当他们要发送电子邮件时,他们通过MAC功能运行消息并产生签名。 最后,收件人使用相同的密钥通过相同的MAC功能运行消息。 如果计算出的签名与消息上的签名相匹配,则可以确保消息在传输过程中未被篡改。
MAC实际上只是一个包装器
注意上述实现中的以下行:
Mac mac = Mac.getInstance("HMACSHA1");
您可能还记得以前,SHA1是一种相当标准的哈希算法。 好了,这就是整个故事:MAC函数只是标准哈希函数的包装。 HMACSHA1的HMAC部分是包装方法。 还有其他MAC实现,例如VMAC,但不太常用。 VMAC的性能比HMAC好。 由于HMAC和VMAC只是包装器,因此您几乎可以使用任何哈希函数并将它们转换为键控哈希函数。 使用SHA1的哥哥,我们可以通过该程序运行相同的引号:
- HMACSHA256:POP9cefgoEC9pUaWXqQQ8lBbW9CdMi1k3t7LXAGYl87s =
- HMACSHA512:ALFIpPMbphQJ7KZQHacGIFH3T2qI5AKUqaD8lDilNnjajGL29cZdp68sLeQTjDKD + cIAfZN86udfRdecGeTm0A ==
常见情况2:再次将密码存储在数据库中
之前我们决定使用以下表格结构
用户名 | PASSWORD_HASH | 盐 |
---|---|---|
tjefferson43 | HiE0AZhRWAs6Mmd7dVqppM1WtJc = | WTg68cVxPI |
1776年 | aktJ / 0cn69Y41vssNfZDHY1CsdE = | sWVUaTGa6e |
让我们增加一列:
用户名 | PASSWORD_HASH | 盐 | MAC_KEY |
---|---|---|---|
tjefferson43 | RFu51fVI / 0y8gmPXkjo0Op9FRHU = | WTg68cVxPI | KvzpFe690vsVcW1jLqQ1vg == |
1776年 | SmZedewg1JD7kRhndEW2cRmcyGw = | sWVUaTGa6e | FgIpIDFi9kJgXvkp44ua4Q == |
那么我们在这里做了什么? 现在,我们使用HMACSHA1,而不是使用直接sha-1函数来存储用户密码的表示形式。 这对您有什么作用? 不多。 更好的问题是,它对黑客有什么影响……这使他们的生活极为困难!
MAC可以保护您免受黑客攻击(同样,不是您的想法)
在我们原始的表设置中,黑客可以使用两种形式的攻击来破解我们的密码。 他可以使用Rainbow攻击 (是的,这确实叫做……不,我不知道为什么)或蛮力攻击 (不要与Bruce力攻击相混淆)。
Rainbow攻击使用多terrabyte数据库对我们的哈希算法进行反向查找。 该数据库是提前生成的,可能是由合作方和数千台单独的计算机来分配工作负载。 即使付出了共同的努力,仍然需要花费数月的时间来生成有用的表。 但是,使用完成的Rainbow表,攻击者可以查找哈希函数输出以获得任意输入,该输入产生与用户真实密码相同的哈希。 kes! 令人高兴的是,使用HMACSHA1对黑客来说是不可行的。 因为我们为每个用户的密码分别设置了哈希算法密钥,所以他们将不得不为每个单独的条目共同重新生成彩虹表,这使得彩虹在实践中非常非常非常困难!
暴力攻击与彩虹攻击有关。 蛮力攻击会尝试随机组合字母和数字,甚至是常见的英语单词,直到找到产生正确输出的输入。 不幸的是,HMACSHA1几乎没有提供其他针对暴力攻击的安全性。 但是,由于它们需要强大的计算系统才能运行,因此它们并不常见。 只有两种方法可以抵御暴力攻击:
- 围绕密码实施良好的监管:
- 12个字符
- 没有英文单词
- 至少2个数字
- 至少2个符号
- 至少2个大写字母
- 使用SHA-512或SHA-256代替SHA-1。
如果您喜欢这篇文章,请问是否对DZone和HackerNews投票支持我。 直到下一次,谢谢您的阅读!
参考: 强密码学入门– p0 , 强密码学入门– p1.0 –哈希函数,美国爱国者和强密码学入门– p1.1 –来自JCG合作伙伴的 键控哈希,哈希攻击,MAC,莎士比亚 乔纳森·费舍尔(Jonathan Fisher)在《代码机制》博客上。
翻译自: https://www.javacodegeeks.com/2012/02/introduction-to-strong-cryptography-p1.html
密码学安全强随机数生成器