您在这里所做的实际上是定义一个哈希函数:给定一个加密函数 Encrypt(K, M),其中 K 是密钥,M 是要加密的明文,您定义 Hash(P) = Encrypt(P, P)。因此,您正在发明一种新的加密功能。
首先,对于密码哈希,您不需要任何旧的哈希函数,因为它需要抵抗暴力破解尝试,攻击者猜测密码可能是什么,计算哈希(猜测)并将结果与存储的结果进行比较哈希。无论您使用哪种哈希算法,您都需要加强它以包含盐(这样攻击者必须单独破解每个哈希,而不是一次访问所有帐户并使较弱的密码失效)并使哈希函数变慢(因为这会伤害攻击者需要做出很多错误的猜测,而不是防御者,后者将主要验证正确的尝试)。请参阅如何安全地散列密码?以获得更详细的解释。
您可以从普通哈希函数构建一个缓慢的、加盐的哈希函数,其结构类似于 SSH(P, S) = Hash(Hash(...Hash(P + S)...)) (其中 P + S 是连接)。这不一定是最好的方法——例如,一个好的密码散列函数用于典型用途应该需要大量内存,因为服务器比专门的密码破解硬件有更多的内存。但这是一个好的开始。
问题仍然是 Hash(P) = Encrypt(P, P) 是否是一个好的散列函数。这不是自动的;请注意,对密码算法安全性的一些分析依赖于密钥和明文是独立的。
一个主要困难是大多数加密算法中的密钥大小受到严格限制,通常是恒定的。例如,标准加密算法 AES 仅接受三种密钥大小(8、12 或 16 字节)。如果密码(加盐)太短,可以用无效字符填充,但如果太长怎么办?基于长于密钥大小的材料使用密钥的常用方法是……对材料应用散列函数(接受任意输入长度)。
如果要从加密算法中推导出哈希函数,其实有一个更简单的方法:不是Hash(P) = Encrypt(P, P),而是定义Hash(P) = Encrypt(P, 0),即加密一个all -zeros 明文块(或其他一些众所周知的明文块)。这就是原始的 Unix 密码散列函数所做的。这种方法优于 Encrypt(P, P) 的优点是您可以从现有的算法分析中受益:加密算法旨在抵抗已知明文攻击。密码和盐大小的限制仍然存在。