你从互联网上下载了某个文件(比如安装程序、ISO映像文件或压缩文件)后,该文件可能因各种错误情形而遭到损坏,比如由于网络连接上的传输错误、下载受到中断、存储硬件有故障、文件系统错误等。抛开此类错误情形不说,文件还有可能被攻击者在下载过程中或下载前恶意篡改。比如说,攻击者对认证中心做手脚后,就能发动中间人(MITM)攻击,引诱你从假冒的HTTPS网站下载感染了恶意软件的文件。
为了保护自己远离这些种类的问题,你从互联网下载文件时,我们常常建议验证文件的真实性和完整性。尤其是你下载了相当敏感的文件(比如操作系统映像文件、应用程序二进制代码和可执行的安装程序等)后,盲目地相信已下载文件可不是一个好习惯。
想验证已下载文件的完整性,一个快速而简单的办法就是,使用各种校验和工具(比如md5sum、sha256sum和cksum),计算并比较校验和(比如MD5、SHA或CRC)。不过,校验和很容易遭到碰撞攻击,而且无法用来验证文件的真实性(即所有者)。
如果你既想验证已下载文件的真实性(所有者),又想验证文件的完整性(内容),就要改而依赖加密签名。我在本教程中将介绍如何使用GnuPG(GNU隐私保护),检验文件的真实性和完整性。
在这个例子中,我要验证可从https://onionshare.org下载的一个磁盘映像文件。在该网站上,文件发布者提供了官方公钥,还提供了用于验证的指纹。
至于有待下载的文件,发布者还提供了相应的PGP签名。
安装GnuPG,生成密钥对
我们不妨先将GnuPG安装到你的Linux系统上。
在Debian、Ubuntu及其他Debian衍生版上:
$ sudo apt-get install gnupg
在Fedora、CentOS或RHEL上:
$ sudo yum install gnupg
安装完毕后,生成密钥对,你在本教程中要用到该密钥对。
$ gpg --gen-key
密钥生成过程中,会要求你提供姓名和电子邮件地址,以及保护你私钥的密码。你还可以选择密钥对何时到期(默认情况下没有到期日)。密钥生成过程可能需要几分钟或更久,长短取决于你选择的密钥大小(1024位到4096位),因为它需要收集足够数量的随机性数据,这些数据来自你的桌面活动(比如键盘键入、鼠标移动、磁盘访问)。
密钥生成完毕后,公钥和私钥都将存储在~/.gnupg目录中,供之后使用。
导入文件所有者的公钥
验证已下载文件的第一步是,导入文件所有者的公钥,与所有者建立信任关系则是可选步骤。
首先,下载文件所有者的公钥:
$ wget https://onionshare.org/signing-key.asc
然后继续使用gpg命令,将公钥导入到你的密钥环:
$ gpg --import signing-key.asc
一旦所有者的公钥导入完毕,它会输出一个密钥编号(比如“EBA34B1C”),如上所示。记下这个密钥编号。
现在,运行这个命令,检查已导入公钥的指纹:
$ gpg --fingerprint EBA34B1C
你会看到公钥的指纹串。将该指纹串与网站上显示的指纹进行比对,看看两者是否匹配。
通常来说,验证公钥的所有者确实是他或她声称的那个人必须经历比简单的指纹比对更严格的过程,因为网站有可能经过伪造,以便与指纹匹配。只有密钥已经过了全面审查,确实属于某个人(比如你见过那个人,通过电话聊了聊,以证实对方的身份,等等),才可以信任对方的公钥。
一旦你通过另外的一些手段验证了公钥的有效性,并决定信任公钥,就可以明确将你的信任赋予该密钥,如下所示。要注意:这一步是可选的,不需要你在使用前赋予明确的信任。
$ gpg --edit-key EBA34B1C
这个命令会显示GPG提示符:
在GPG提示符下键入“trust”,这会让你可以选择该密钥的信任级别:从1到5。
在这里,我决定赋予信任“4”。之后,键入“sign”,用你自己的私钥来对它签名,然后在GPG提示符下键入“save”:
同样,这种明确将信任赋予公钥的方式不是必需的;通过仅仅导入密钥表明绝对信任常常就够了。
将“完全”信任赋予密钥带来的影响是,如果另一个密钥X用这个完全信任的密钥来签名,密钥X也被你认为是有效的。通常而言,密钥验证依赖一种名为“信任网络”(web of trust)的复杂机制。
回到本教程,现在不妨检验已导入密钥列表。
$ gpg --list-keys
你应该至少会看到两个密钥:一个是深度为0、终极信任(“1u”)的密钥;另一个是深度为1、完全信任(“1f“)的密钥,后者是由你自己早些时候签名的密钥。
验证文件的真实性/完整性
一旦你使用文件所有者的公钥与对方建立起了信任关系,我们现在就可以准备验证你从所有者那里下载的某个文件的真实性和完整性了。
在我们这个例子中,文件所有者分开发布了文件和相应的PGP签名(*.asc)。签名的作用就是验证文件,并给文件加上时间戳。
一个典型的签名(*.asc)看起来如下。
-----BEGIN PGP SIGNATURE-----
iQIcBAABCgAGBQJUJGhsAAoJEP1yCtnro0sc1jUP/ixNY/lKdrcMIAUoqlWKNE8f
sj4SFiwREMew76w66GASDF03fa5zPX6EsS2kucgx8ZsfEiSmN5T0y2P/aSaXwZqF
kywZVEzirKtca5AJ4DBzu6qrt9GgSw6JBJVv1oBJCMNyO+eAj341paR3MudvnyQz
H/N5tc4Qcilzy6M184opGIzy4ipEmMXfLHsd7WJpAyn+tO/z3uhh9NkNuygZpaFr
olpSWPE8revdDJyfMfSmb3ZrFmhLn7FCEltOi+a7SluvrMclizfnbec9rgLJtjo0
CPDZY7tsWmmL0DA3VvpMVqGvkg/Dyhpn2IIDrNaLAlvGQ5aovf+4tjad5IHvyaWx
4Gds93G6Hqvv5RwGx7OR3hgt2o0Y+qFsVDxVnPxerGhXeJXHzSDwLQMpdj9IoSU
Ae/53XXnxqSN6POZcwHiHvbsv0pdlg0Ea0dDAAN0ZeINNyZf1R0tLjWkcgpvGCtv
qkJuYFF9W9cWHraPY2ov5Hs/JZzPcG0eVpnDdzfOOH1gDKADq9A5D2X5QJCulsh9
WwU3X+E43OqIsoRzBucItD9HhZbEH7t8Q0xAqnAkgU3hriZp3dN4cnMfhM6I9hli
EmpSpLKCceMexu2o9QgzGXVm+AGZJe4QkuwAhRIccp5JDMVny61UlKTasjy6co8h
5GBhhYybPEFM+G1BODMd
=c9wo
-----END PGP SIGNATURE-----
我们不妨下载文件及其签名:
$ wget https://onionshare.org/files/0.6/OnionShare.dmg
$ wget https://onionshare.org/files/0.6/OnionShare.dmg.asc
现在,验证已下载文件的PGP签名。
$ gpg --verify OnionShare.dmg.asc OnionShare.dmg
如果该命令的输出里面含有“Good signature from ”,这表明已下载的.dmg文件已成功通过了验证。要是已下载文件在签名生成后以任何一种方式而遭到篡改,验证就会失败。
现在你大可放心,可以信任那个已下载文件了。