软件的密码保护原理

加密相关笔记

Posted by John Mactavish on February 4, 2021

我们有一些数据或信息不想让别人知道,该怎么办呢。很多软件都会提供密码保护 功能,比如 word 可以加密你的文档,压缩包也可以加密,但它们是如何工作的呢?

我们以文档加密为例。

一个很笨的想法是保存用户设置的密码,以后使用是对输入的密码用 if-else 判断一下, 仅当密码正确时打开文档。但是,第一,我们竟然保存了用户密码;第二,我们的文档还是 明文保存的。倘若保存这两者的地方仅提供有限的访问手段,比如云端服务器,说不定 也能起到保护作用。但如果软件把它们保存在用户的本地终端(电脑、手机)上, 恶意软件或者能查看你的终端的别有用心之人(比如你妈)就可以轻易绕过软件访问文档 甚至找到密码文件。

1

2

事实上,即使是安全措施较为完备的服务器也可能被攻破,因而明智的软件根本不会这么做。

那么,我们要做的就是利用用户密码加密数据,然后仅保存加密后的密文(cipher),加密前的明文(plain) 与密码不保存。当然,如果用户起的是 12345、asdf 这种傻瓜密码就很糟糕了。 所以我们加密时用的密钥(key)要在用户提供的密码(password)基础上加盐(salt)以保证 随机性。显然 salt 应该用随机数生成器生成。这种基于用户密码生成密钥的算法 PBKDF(Password-Based Key Derivation Function)会将密码转换成很长又完全随机的安全密钥。 推荐的算法有 PBKDF2、brcypt、scrypt 等。密码学算法都是公开的,只要不停尝试(如果放在 服务器端,可以限制尝试频率),终究可以算出密钥 key为应对暴力破解,PBKDF 算法 并不像普通的 Hash 算法那样快,相反它被故意设计成很慢(或者需要消耗较多计算资源)。 PBKDF 还可以设置迭代次数 count,开发者能根据当前计算机硬件水平选择合适的迭代次数, 提高了暴力破解的难度。

表示下来就是:

加密:

INPUT password, plain
salt = Random.next()
key = hash(salt, password) // with PBKDF algorithm
cipher = encrypt(key, plain) // such as AES algorithm
DEL key, plain
SAVE cipher, salt

解密:

INPUT password, cipher, salt
key = hash(salt, password) // with PBKDF algorithm
plain = decrypt(key, cipher)
DEL key
OUTPUT plain

这样也可以轻易实现加密文件的云同步,同步时仅在网络上传输 ciphersalt, 即使它们被黑客拦截,plain 也不会暴露。

那么问题来了,既然我们不保存密码,那如果解密时 password 不对,软件怎么知道输出的 plain 对不对。 答案是软件不需要知道,它仅需把解密结果给用户。用户发现解密的文档是乱码, 自然就知道密码不对了。

但是如果软件要做的不是数据加密而是用户认证呢?那软件必须知道密码对不对, 同时它还是不能保存密码。比如,我们设计一个网站,要记录用户账号信息,数据库中 的用户名与密码两项要怎么保存。答案还是哈希(hash)。

注册:

INPUT name, password
salt = Random.next()
key = hash(salt, password) // with PBKDF algorithm
database[name] = (key, salt)

登录:

INPUT name, password
(key, salt) = database[name]
if hash(salt, password) == key
    PASS
else 
    DENY

注意,这里只有哈希操作而不涉及加密操作,这里的 key 也不是什么密钥。 这里利用的仅仅是 hash 正向简单而反向困难的特性。

顺带对比一下加密与哈希:

3

在数字签名中就同时用到了上述两种算法。我们知道加密可用于认证,那如何确定某文件 来自某人且文件完整、未被篡改?在 RSA 体系下,可以用自己的私钥为文件加密,其他人 用公开的公钥即可解密文件。但是文件很大的话,加密解密会很耗时,所以可以改为加密文件 摘要。加密的摘要即为数字签名。


如果你喜欢我的文章,请我吃根冰棒吧 (o゜▽゜)o ☆

contribution

最后附上 GitHub:https://github.com/gonearewe