密码加密之消息摘要算法

178 阅读5分钟

如果直接将用户填写的明文密码(原始密码)存储到数据库中,当出现数据库泄密,用户的账号安全就无法保障!所以,需要将明文密码进行加密,在数据库中,实际存储的会是密文(加密后的结果),即使数据库泄密,被看到也只是密文,如果无法通过密文还原出原文(原始密码),则不会影响账号安全。

假设,某用户的原始密码是1234,假设使用非常简单的规则:“将每位数字增加1”,就可以得到2345,最终,会将2345存储到数据库中,即使出现数据库泄密,别人能看到的也只是2345,如果这个规则设计得更加复杂,无法将2345这种密文还原成1234这种原文,至少能保障用户账号安全(不被他人登录等)。

当使用了这样的加密处理后,并不影响程序的正常使用,例如,用户注册时进行了以上的加密处理,在后续登录时,用户依然提交1234这种原始密码,在验证登录时,程序会对1234使用相同的规则进行运算,得到2345,然后,再与数据库中记录的2345进行对比即可。

在处理加密时,加密规则越复杂越好,或者说,根据密文还原出原文的难度越大越好!一般来说,都应该使用成熟的算法,而不需要自行设计算法!

在实现密码加密时,不要使用任何加密算法,因为所有加密算法在设计时就已经决定了它是可逆向运算的,也就是说“所有的加密算法都可以解密”!加密算法的主要应用领域只是“保障传输过程的安全”,并不保证“存储数据的安全”。

推荐使用消息摘要算法对密码进行加密并存储,因为所有的消息摘要算法都是不可逆向运算的。

常用的消息摘要算法有MD(Message Digest)系列和SHA(Secure Hash Argorithm)家族算法,例如MD2、MD4、MD5、SHA-1、SHA-256、SHA-384、SHA-512等。

Java语言本身就可以支持各种算法进行运算,原生API的使用相对麻烦,Spring框架默认就提供了MD5运算的API:

@Test
public void md5() {
    String password = "123456";
    String digest = DigestUtils.md5DigestAsHex(password.getBytes());
    System.err.println(digest);
}

关于消息摘要算法,有几个特点:

  • 使用固定的算法,消息相同时,摘要必然相同;
  • 使用固定的算法,无视消息的长度,摘要的长度固定;
  • 使用固定的算法,消息不同时,摘要几乎不会相同。

例如:

消息摘要
123202cb962ac59075b964b07152d234b70
123456e10adc3949ba59abbe56e057f20f883e
1234567890e807f1fcf82d132f9bb018ca6738a19f

因为消息的长度是没有限制的,所以,消息的种类可以是无限种!但是,摘要的长度是固定的,所以,摘要的种类就是有限的!综合来看,理论上来说,可以有N个不同的消息对应相同的摘要!如果找到2个完全不同的消息,运算得到的摘要却完全相同,则称之为发生了“碰撞”!

但是,虽然存在碰撞概率,但是,概率却不一定高,以MD5为例,摘要的长度固定为32位,其本质是32个十六进制数,如果还原成二进制数,将是一个128位长度的二进制数,所以,MD5算法是128位算法。理论上来说,发生碰撞的概率是3.40282E+38分之一。

同时,在实际应用中,如果是使用消息摘要算法对密码进行“加密”时,用户提交的原始密码其实是有限的种类(允许使用的只有数字、字母、符号,且通常会限制长度),就不存在上述的“无限对应有限”的现象,在“有限对应有限”的应用场景中,发生碰撞的概率将更低,甚至根本就不存在!这样来看,使用消息摘要算法用于“密码加密”的数据处理是安全有效的!

关于消息摘要的破解,首先,学术上的“破解”指的是“研究某种消息摘要算法的碰撞概率”,并不是讨论所谓的“逆向运算”,只要是消息摘要算法,都是不可以被逆向运算的!

另外,在网络上,还有一些网站提供了“在线破解”,其实,这些网站是在数据库中收录了大量消息与摘要的对应结果,当网友尝试破解时,这些网站是“通过网友提供的摘要查询对应的原文”,并不是真正意义的“破解”!由于这些网站收录的消息与摘要的对应结果是有限的,所以,相当复杂的密码都不会被这些网站破解!

在实际设计项目时,为了最大化保障用户密码的安全,应该:

  • 要求用户使用安全强度更高的密码;
  • 对密码进行循环加密;
  • 对密码进行“加盐”处理;
  • 选取位数更长的摘要算法;
  • 综合以上做法。