1. 定义
单向散列函数(one-way hash function)是指对不同的输入值,通过单向散列函数进行计算,得到固定长度的输出值。这个输入值称为消息(message),输出值称为散列值(hash value)。
单向散列函数也被称为消息摘要函数(message digest function)、哈希函数或者杂凑函数。输入的消息也称为原像(pre-image)。输出的散列值也称为消息摘要(message digest)或者指纹(fingerprint),相当于该消息的身份证。
单向散列函数有多种实现算法,常见的有:MD5、SHA-1、SHA-2和 SHA-3。
2. 特性
通过上面的定义,我们对单向散列函数的了解还是模糊的。下面介绍单向散列函数的特性,加深一下印象。
2.1 散列值长度固定
无论消息的长度有多少,使用同一算法计算出的散列值长度总是固定的。比如 MD5 算法,无论输入多少,产生的散列值长度总是 128 比特(16字节)。
然而比特是计算机能够识别的单位,而我们人类更习惯于使用十六进制字符串来表示(一个字节占用两位十六进制字符)。
2.2 消息不同其散列值也不同
使用相同的消息,产生的散列值一定相同。
使用不同的消息,产生的散列值也不相同。哪怕只有一个比特的差别,得到的散列值也会有很大区别。
这一特性也叫做抗碰撞性,对于抗碰撞性弱的算法,我们不应该使用。
2.3 具备单向性
只能通过消息计算出散列值,无法通过散列值反算出消息。
2.4 计算速度快
计算散列值的速度快。尽管消息越长,计算散列值的时间也越长,但也会在短时间内完成。
3. 常见算法
MD5 与 SHA-1 算法已被攻破,不应该被用于新的用途;SHA-2 与 SHA-3 还是安全的,可以使用。
SHA-2包括:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。
SHA-3包括:SHA3-224、SHA3-256、SHA3-384、SHA3-512。
算法名称 | 散列值长度 | 是否安全 |
---|---|---|
MD5 | 128 | 不安全 |
SHA-1 | 160 | 不安全 |
SHA-224 | 224 | 安全 |
SHA-256 | 256 | 安全 |
SHA-384 | 384 | 安全 |
SHA-512 | 512 | 安全 |
SHA-512/224 | 224 | 安全 |
SHA-512/256 | 256 | 安全 |
SHA3-224 | 224 | 安全 |
SHA3-256 | 256 | 安全 |
SHA3-384 | 384 | 安全 |
SHA3-512 | 512 | 安全 |
4. 应用场景
单向散列函数并不能确保信息的机密性,它是一种保证信息完整性的密码技术。下面来看它的应用场景。
4.1 用户密码保护
用户在设置密码时,不记录密码本身,只记录密码的散列值,只有用户自己知道密码的明文。校验密码时,只要输入的密码正确,得到的散列值一定是一样的,表示校验正确。
为了防止彩虹表破解,还可以为密码进行加盐处理,只要验证密码时,使用相同的盐即可完成校验。
使用散列值存储密码的好处是:即使数据库被盗,也无法将密文反推出明文是什么,使密码保存更安全。
4.2 接口验签
为了保证接口的安全,可以采用签名的方式发送。
发送者与接收者要有一个共享秘钥。当发送者向接收者发送请求时,参数中附加上签名(签名由共享秘钥 +** 业务参数**,进行单向散列函数加密生成)。接收者收到后,使用相同的方式生成签名,再与收到的签名进行比对,如果一致,验签成功。
这样即可以验证业务参数是否被篡改,又能验明发送者的身份。
4.3 文件完整性校验
文件被挂载到网站时,同时也附上其散列值和算法,比如 Tomcat 官网。
用户下载后,计算其散列值,对比结果是否相同,从而校验文件的完整性。
4.4 云盘秒传
当我们将自己喜欢的视频放到网盘上时,发现只用了几秒的时间就上传成功了,而这个文件有几个G大小,是怎么做到的呢?
其实这个“秒传”功能可以利用单向散列函数来实现。
当我们上传一个文件时,云盘客户端会先为该文件生成一个散列值。拿着这个散列值去数据库中匹配,如果匹配到,说明该文件已经在云服务器存在。只需将该散列值与用户进行关联,便可完成本次“上传”。
这样,一个文件在云服务器上只会存一份,大大节约了云服务器的空间。
5. 代码实现
JDK的 java.security.MessageDigest
类为我们提供了消息摘要算法,用于 MD5和SHA的散列值生成。下面代码做了简单的封装,便于直接使用。
public class MDUtil {/** * MD5 加密 * * @param data 要加密的数据 * @return 32位十六进制字符串 */public static String MD5(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("MD5");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/** * MD5 加密 * * @param data 要加密的数据 * @return 32位十六进制字符串 */public static String MD5(String data) {return MD5(data.getBytes());}/** * SHA-1 加密 * * @param data 要加密的数据 * @return 40位十六进制字符串 */public static String SHA1(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-1");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/** * SHA-1 加密 * * @param data 要加密的数据 * @return 40位十六进制字符串 */public static String SHA1(String data) {return SHA1(data.getBytes());}/** * SHA-224 加密 * * @param data 要加密的数据 * @return 56位十六进制字符串 */public static String SHA224(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-224");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/** * SHA-224 加密 * * @param data 要加密的数据 * @return 56位十六进制字符串 */public static String SHA224(String data) {return SHA224(data.getBytes());}/** * SHA-256 加密 * * @param data 要加密的数据 * @return 64位十六进制字符串 */public static String SHA256(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/** * SHA-256 加密 * * @param data 要加密的数据 * @return 64位十六进制字符串 */public static String SHA256(String data) {return SHA256(data.getBytes());}/** * SHA-384 加密 * * @param data 要加密的数据 * @return 96位十六进制字符串 */public static String SHA384(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-384");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/** * SHA-384 加密 * * @param data 要加密的数据 * @return 96位十六进制字符串 */public static String SHA384(String data) {return SHA384(data.getBytes());}/** * SHA-512 加密 * * @param data 要加密的数据 * @return 128位十六进制字符串 */public static String SHA512(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-512");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/** * SHA-512 加密 * * @param data 要加密的数据 * @return 128位十六进制字符串 */public static String SHA512(String data) {return SHA512(data.getBytes());}/** * 将字节数组转换为十六进制字符串 * * @param bytes 字节数组 * @return 十六进制字符串 */private static String bytesToHexString(byte[] bytes) {StringBuilder hexValue = new StringBuilder();for (byte b : bytes) {int val = b & 0xFF;if (val < 16) {hexValue.append("0");}hexValue.append(Integer.toHexString(val));}return hexValue.toString();} }
下面分别使用这些算法计算“123456”的散列值:
public static void main(String[] args) {System.out.println("MD5\t\t" + MDUtil.MD5("123456"));System.out.println("SHA-1\t" + MDUtil.SHA1("123456"));System.out.println("SHA-224\t" + MDUtil.SHA224("123456"));System.out.println("SHA-256\t" + MDUtil.SHA256("123456"));System.out.println("SHA-384\t" + MDUtil.SHA384("123456"));System.out.println("SHA-512\t" + MDUtil.SHA512("123456")); }
输出结果:
MD5e10adc3949ba59abbe56e057f20f883e SHA-17c4a8d09ca3762af61e59520943dc26494f8941b SHA-224f8cdb04495ded47615258f9dc6a3f4707fd2405434fefc3cbf4ef4e6 SHA-2568d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 SHA-3840a989ebc4a77b56a6e2bb7b19d995d185ce44090c13e2984b7ecc6d446d4b61ea9991b76a4c2f04b1b4d244841449454 SHA-512ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413
我用的是Java8,还不支持 SHA-3,所以上面代码只封装了MD5、SHA-1和SHA-2。
从Java9开始支持SHA-3
网络安全入门学习路线
其实入门网络安全要学的东西不算多,也就是网络基础+操作系统+中间件+数据库,四个流程下来就差不多了。
1.网络安全法和了解电脑基础
其中包括操作系统Windows基础和Linux基础,标记语言HTML基础和代码JS基础,以及网络基础、数据库基础和虚拟机使用等...
别被这些看上去很多的东西给吓到了,其实都是很简单的基础知识,同学们看完基本上都能掌握。计算机专业的同学都应该接触了解过,这部分可以直接略过。没学过的同学也不要慌,可以去B站搜索相关视频,你搜关键词网络安全工程师会出现很多相关的视频教程,我粗略的看了一下,排名第一的视频就讲的很详细。 当然你也可以看下面这个视频教程仅展示部分截图:
学到http和https抓包后能读懂它在说什么就行。
2.网络基础和编程语言
3.入手Web安全
web是对外开放的,自然成了的重点关照对象,有事没事就来入侵一波,你说不管能行吗! 想学好Web安全,咱首先得先弄清web是怎么搭建的,知道它的构造才能精准打击。所以web前端和web后端的知识多少要了解点,然后再学点python,起码得看懂部分代码吧。
最后网站开发知识多少也要了解点,不过别紧张,只是学习基础知识。
等你用几周的时间学完这些,基本上算是具备了入门合格渗透工程师的资格,记得上述的重点要重点关注哦! 再就是,要正式进入web安全领域,得学会web渗透,OWASP TOP 10等常见Web漏洞原理与利用方式需要掌握,像SQL注入/XSS跨站脚本攻击/Webshell木马编写/命令执行等。
这个过程并不枯燥,一边打怪刷级一边成长岂不美哉,每个攻击手段都能让你玩得不亦乐乎,而且总有更猥琐的方法等着你去实践。
学完web渗透还不算完,还得掌握相关系统层面漏洞,像ms17-010永恒之蓝等各种微软ms漏洞,所以要学习后渗透。可能到这里大家已经不知所云了,不过不要紧,等你学会了web渗透再来看会发现很简单。
其实学会了这几步,你就正式从新手小白晋升为入门学员了,真的不算难,你上你也行。
4.安全体系
不过我们这个水平也就算个渗透测试工程师,也就只能做个基础的安全服务,而这个领域还有很多业务,像攻防演练、等保测评、风险评估等,我们的能力根本不够看。
所以想要成为一名合格的网络工程师,想要拿到安全公司的offer,还得再掌握更多的网络安全知识,能力再更上一层楼才行。即便以后进入企业,也需要学习很多新知识,不充实自己的技能就会被淘汰。
从时代发展的角度看,网络安全的知识是学不完的,而且以后要学的会更多,同学们要摆正心态,既然选择入门网络安全,就不能仅仅只是入门程度而已,能力越强机会才越多。
尾言
因为入门学习阶段知识点比较杂,所以我讲得比较笼统,最后联合CSDN整理了一套【282G】网络安全从入门到精通资料包,需要的小伙伴可以点击链接领取哦!