
文档教程教育【免费下载链接】missing-semester-cn.github.iothe CS missing semester Chinese version项目地址https://gitcode.com/gh_mirrors/mi/missing-semester-cn.github.io点击查看免费下载本篇文章基于《计算机教育中缺失的一课》missing-semester-cn.github.io2020 年「安全和密码学」章节整理而成。它以散列函数、密钥生成函数、对称/非对称密码体系为主线剖析这些概念如何实际应用于你日常使用的 Git 与 SSH 工具中并在结尾给出密码管理器、两步验证、全盘加密等可直接落地的安全实践。读完后你将能用「比特」量化密码强度、理解 Git 内容寻址与 SSH 挑战应答认证的密码学本质并掌握一套可操作的加密、签名与验证命令流程。说明本文主题对应仓库中的 2020 年安全与密码学讲义同一主题在 2019 年还有一版更侧重「用户视角的隐私保护与安全习惯」的讲稿见 去年的这节课。本课程并非计算机系统安全或密码学专业课程的替代品如果你不是密码学专家请不要试图创造或修改加密算法也不要自行设计安全系统。下面介绍的只是基本概念的简单但实用说明足以让你对正在使用的程序和协议有一个大概了解。熵用比特量化密码强度熵Entropy是不确定性的度量它可以用来决定密码的强度。著名 XKCD 936 漫画《Password Strength》讲了一个反直觉的事实correcthorsebatterystaple这个密码比Tr0ub4dor3更安全。那么熵是如何量化安全性的呢熵的单位是比特bit。对于一个均匀分布的随机离散变量熵等于log_2(所有可能的个数 n)。例如扔一次硬币的熵是 1 比特两种可能log2(2) 1掷一次六面骰子的熵约为 2.58 比特log2(6) ≈ 2.58。理解熵的关键假设是攻击者了解密码的生成模型最小长度、最大长度、可能包含的字符种类等但不知道某个密码是如何随机选择的——例如密码是通过掷骰子从字典中随机选词Diceware 方案生成的。因此一个密码的熵取决于生成它的随机过程而不是它看起来多「复杂」。应该使用多少比特的熵取决于应用的威胁模型大约40 比特的熵足以对抗在线穷举攻击——这类攻击受限于网络速度和应用的认证机制对于离线穷举攻击主要受限于计算速度一般需要更强的密码比如80 比特或更多。实际验证一下假设一个密码由四个小写单词拼接而成每个单词从含 10 万单词的字典中随机选取如correcthorsebatterystaple其熵为4 × log2(100000) ≈ 4 × 16.6 ≈ 66 比特而一个由八个随机大小写字母或数字组成的密码如rg8Ql34g其熵为8 × log2(62) ≈ 8 × 5.95 ≈ 48 比特。前者的熵明显更高这就是 XKCD 漫画结论的数学依据。散列函数不可逆的确定性「指纹」密码散列函数Cryptographic hash function可以将任意大小的数据映射为一个固定大小的输出其规范大致如下hash(value: arraybyte) - vectorbyte, N (N 对于该函数固定)SHA-1 是 Git 中使用的一种散列函数它可将任意大小的输入映射为一个 160 比特可由 40 位十六进制数表示的输出。下面用sha1sum命令验证 SHA-1 对几个字符串的输出——注意同样是hello输出完全一致而大小写不同的Hello输出则完全不同$ printf hello | sha1sum aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d $ printf hello | sha1sum aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d $ printf Hello | sha1sum f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0抽象地讲散列函数可以被认为是一个不可逆、看上去随机但具确定性的函数即散列函数的理想模型 Random Oracle。一个密码散列函数拥有以下特性确定性对于不变的输入永远有相同的输出不可逆性对于hash(m) h难以通过已知的输出h计算出原始输入m目标碰撞抵抗性弱无碰撞对于一个给定输入m_1难以找到m_2 ! m_1且hash(m_1) hash(m_2)碰撞抵抗性强无碰撞难以找到任意一组满足hash(m_1) hash(m_2)的输入m_1, m_2该性质严格强于目标碰撞抵抗性。需要注意的是虽然 SHA-1 仍可用于特定用途但它已经不再被认为是一个强密码散列函数2017 年已被实际碰撞攻击打破。针对具体应用推荐何种散列函数超出了本课程范畴如果散列函数的选择对你的工作至关重要请先系统学习信息安全及密码学。密码散列函数的三个典型应用Git 中的内容寻址存储Content-addressed storage。散列函数是一个宽泛的概念还存在非密码学的散列函数那为什么 Git 特意使用密码学散列函数答案就在仓库的 版本控制Git讲义 中Git 存储数据时所有对象blob、树、提交都基于它们的 SHA-1 哈希进行寻址store时计算id sha1(object)并把对象存进objects[id]。密码散列函数的不可逆性与碰撞抵抗性保证了一旦对象被写入其内容就无法被悄悄篡改篡改会改变哈希、破坏寻址同时攻击者也难以从哈希反推出对象内容。这正是「内容寻址 完整性」合二为一的设计。文件的信息摘要Message digest。像 Linux ISO 这样的软件常从非官方的有时不太可信的镜像站下载因此需要确认下载的软件与官方一致。官方站点一般会在下载链接旁公布安装文件的哈希值用户从镜像站下载后把本地计算出的哈希值与官方公布的哈希值对照即可确定安装文件没有被篡改。这也是后面课后练习中「下载 Debian 镜像并用sha256sum对比」的实际意义。承诺机制Commitment scheme。假设你想承诺一个值、但之后再透露它——比如在没有一个可信的、双方可见的硬币的情况下在脑海中公平地「扔一次硬币」。你可以选择一个值r random()并先分享它的哈希值h sha256(r)这时对方可以开始猜硬币正反约定偶数r代表正面、奇数r代表反面。猜完以后你透露r并判定胜负而对方可以用sha256(r)重新计算并对比你事先公布的h从而确认你没有在事后作弊。密钥生成函数把密码变成密钥密钥生成函数Key Derivation Function, KDF是密码散列函数的相关概念被应用于生成固定长度、可被其他密码算法使用的密钥。与普通散列不同为了对抗穷举法攻击密钥生成函数通常较慢刻意引入大量迭代计算提高暴力破解的成本。密钥生成函数的两个典型应用从密码生成密钥把用户口令转换为可以在其他加密算法如对称加密见下文中直接使用的密钥。安全存储登录凭证绝不可直接存储明文密码。正确做法是针对每个用户随机生成一个盐saltsalt random()然后存储「盐」以及「密钥生成函数对连接了盐的明文密码生成的哈希值」KDF(password salt)。验证登录时用输入的密码连接存储的盐重新计算KDF(input salt)并与存储的哈希值对比。加盐的意义在于即使两个用户使用相同密码由于盐不同存储的哈希也不同从而抵御彩虹表等预计算攻击。对称加密用同一个密钥加密与解密说到加密你可能首先想到隐藏明文信息。对称加密通过以下三个方法实现这一功能keygen() - key (这是一个随机方法) encrypt(plaintext: arraybyte, key) - arraybyte (输出密文) decrypt(ciphertext: arraybyte, key) - arraybyte (输出明文)加密方法encrypt()输出的密文ciphertext很难在不知道key的情况下得出明文plaintext解密方法decrypt()则具有明显的正确性因为功能要求它必须满足decrypt(encrypt(m, k), k) m。AESAdvanced Encryption Standard是现在常用的对称加密系统。对称加密的典型应用加密不信任的云服务上存储的文件。对称加密和密钥生成函数配合就可以用密码加密文件将密码输入密钥生成函数得到密钥key KDF(passphrase)然后存储encrypt(file, key)。这样即使云服务商或数据泄露者拿到文件也无法在没有口令的情况下还原明文。非对称加密公钥与私钥的分工非对称加密的「非对称」在于其环境中使用两个功能不同的密钥一个是不向外公布的私钥private key另一个是可以公开的公钥public key——公布公钥不像公布对称加密的共享密钥那样会危及加密体系的安全性。非对称加密通过以下方法实现加密/解密encrypt/decrypt与签名/验证sign/verifykeygen() - (public key, private key) (这是一个随机方法) encrypt(plaintext: arraybyte, public key) - arraybyte (输出密文) decrypt(ciphertext: arraybyte, private key) - arraybyte (输出明文) sign(message: arraybyte, private key) - arraybyte (生成签名) verify(message: arraybyte, signature: arraybyte, public key) - bool (验证签名是否由与该公钥对应的私钥生成)非对称的加密/解密方法与对称加密有类似的特征信息用公钥加密输出的密文很难在不知道私钥的情况下得出明文解密方法同样满足正确性decrypt(encrypt(m, public key), private key) m。机械锁类比可以帮助记忆二者的区别对称加密好比一个防盗门——只要是有钥匙的人都可以开门或锁门非对称加密好比一个可以拿下来的挂锁——你可以把打开状态的挂锁公钥交给任何人并保留唯一的钥匙私钥。这样别人把信息装进盒子、用挂锁锁上之后只有你能用保留的钥匙开锁。签名/验证方法具有与书面签名类似的特征在不知道私钥的情况下无论要签名的信息是什么都很难计算出一个能使verify(message, signature, public key)返回真的签名而用私钥签名的信息用对应公钥验证时必然返回真verify(message, sign(message, private key), public key) true。非对称加密的典型应用PGP 电子邮件加密用户可以将自己的公钥在线发布如 PGP 密钥服务器或 Keybase任何人都可以向他们发送加密的电子邮件聊天加密Signal 和 Keybase 使用非对称密钥建立私密聊天软件签名Git 支持用户对提交commit和标签tag进行 GPG 签名任何人都可以用软件开发者公布的签名公钥验证下载到的已签名软件确实出自该开发者之手。密钥分发非对称加密的核心难题非对称加密面临的主要挑战是如何分发公钥并把它与现实世界中的人或组织对应起来。目前主要有三种信任模型Signal 的信任模型信任用户第一次使用时的身份trust on first use, TOFU同时支持用户线下out-of-band、面对面交换公钥即 Signal 里的 safety numberPGP 的信任网络Web of Trust想加入一个信任网络就必须让已在网络中的成员对你进行线下验证比如对比证件。验证无误后成员用私钥对你的公钥签名你就成为信任网络的一部分——只要使用被签名公钥对应的私钥就可以证明「我是我」Keybase 的社交网络证明social proof主要利用社交网络账号与公钥的绑定关系配合一些精巧设计完成身份证明。每种信任模型各有优点本课程讲师个人更倾向于 Keybase 使用的模型。案例分析把密码学概念用到真实工具中密码管理器每个人都应该尝试使用密码管理器如 KeePassXC、pass、1Password。密码管理器会为每个网站生成随机且复杂表现为高熵的密码并用你指定的主密码配合密钥生成函数来对称加密它们。你只需记住一个复杂的主密码就能管理大量复杂度高且不重复使用的密码——这样既降低了密码被猜出的概率也减少了一个网站信息泄露后对其他网站密码的连带威胁。两步验证双因子验证2FA两步验证要求用户同时使用密码「你知道的信息」和一个身份验证器「你拥有的物品」比如 YubiKey 这样的硬件令牌以消除密码泄露或钓鱼攻击的威胁。它的价值在于即使密码被窃取攻击者因缺少第二因素仍然无法登录。全盘加密对笔记本电脑硬盘进行全盘加密是防止设备丢失导致信息泄露的简单且有效的方法。Linux 的 cryptsetup LUKS、Windows 的 BitLocker、macOS 的 FileVault都使用一个由密码保护的对称密钥来加密盘上的所有信息。注意它的适用前提全盘加密主要防护「设备离线状态下被物理窃取」的威胁。聊天加密Signal 和 Keybase 使用非对称加密为用户提供端到端End-to-End安全性。获取联系人的公钥非常关键——为了保证安全性应使用线下方式验证 Signal 或 Keybase 的用户公钥或者信任 Keybase 用户提供的社交网络证明。SSH从密码学角度看密钥认证我们在 命令行环境一课 中讨论了 SSH 及 SSH 密钥的使用这里从密码学角度重新审视它们。当你运行ssh-keygen命令时它会生成一个非对称密钥对(public_key, private_key)。生成过程中使用的随机数由系统提供的熵决定——这些熵可以来源于硬件事件hardware events等。公钥最终会被分发可以直接明文存储但私钥必须加密存储以防泄露ssh-keygen会提示用户输入一个密码passphrase将它输入密钥生成函数产生一个密钥最终用对称加密算法和这个密钥加密私钥。仓库的 命令行环境讲义 给出了现代推荐用法即生成 ED25519 密钥对并为私钥设置密码ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519在实际认证中当服务器已知用户的公钥存储在~/.ssh/authorized_keys文件中一般在用户 HOME 目录下时尝试连接的客户端可以用非对称签名证明用户身份——这正是挑战应答方式Challenge–Response Authentication服务器选择一个随机数字发送给客户端客户端用用户私钥对这个数字签名后返回服务器服务器随后用authorized_keys中存储的用户公钥验证返回信息是否由对应私钥所签名。这种验证方式可以有效证明「试图登录的用户确实持有所需的私钥」同时私钥本身从未在网络中传输。把公钥安装到服务器的标准做法同样见 命令行环境讲义cat ~/.ssh/id_ed25519.pub | ssh foobarremote cat ~/.ssh/authorized_keys # 或者更简单 ssh-copy-id -i ~/.ssh/id_ed25519.pub foobarremote资源与延伸阅读去年的讲稿更注重于计算机用户如何增强隐私保护和安全「Cryptographic Right Answers」解答了在一些应用环境下「应该使用什么加密」的问题是挑选密码原语时的实用参考。课后练习熵假设一个密码由四个小写的单词拼接组成每个单词从含 10 万单词的字典中随机选择、概率相同例如correcthorsebatterystaple。这个密码有多少比特的熵假设另一个密码由八个随机的大小写字母或数字组成例如rg8Ql34g。它又有多少比特的熵哪一个密码更强假设攻击者每秒可尝试 1 万个密码分别破解上述两个密码需要多久密码散列函数从 Debian 镜像站下载一个光盘映像ISO使用sha256sum命令对比下载映像的哈希值与官方 Debian 站公布的哈希值官方会在 SHA256SUMS 文件中公布所有镜像的校验值。对称加密使用 OpenSSL 的 AES 模式加密一个文件openssl aes-256-cbc -salt -in {源文件名} -out {加密文件名}用cat或hexdump对比源文件和加密后的文件再用openssl aes-256-cbc -d -in {加密文件名} -out {解密文件名}解密刚才加密的文件最后用cmp命令确认源文件与解密后的文件内容完全相同。非对称加密在自己电脑上使用更安全的 ED25519 算法生成一组 SSH 密钥对参考上文ssh-keygen -o -a 100 -t ed25519命令为确保私钥不被盗用务必用密码加密你的私钥配置 GPG用于消息的加密与签名使用 GPG 加密一封电子邮件发送出去使用git commit -S命令签名一个 Git 提交并用git show --show-signature验证该提交的签名或者使用git tag -s签名一个 Git 标签并用git tag -v验证标签的签名。这也是「软件签名」应用在 Git 工作流中的直接实践——签名后的提交/标签其他人可以用你公布的公钥验证其确实出自你手。赞分享文档教程教育【免费下载链接】missing-semester-cn.github.iothe CS missing semester Chinese version项目地址https://gitcode.com/gh_mirrors/mi/missing-semester-cn.github.io点击查看免费下载相关推荐计算机教育中缺失的一课用 cron 与 anacron 打造 Linux 定时任务自动化计算机教育中缺失的一课用 cron 与 anacron 打造 Linux 定时任务自动化 本篇技术指南源自本仓库 2019 年课程讲义 _2019/autom文档教程教育mempool.space 闪电网络支持完整的节点监控与通道管理mempool.space 闪电网络支持完整的节点监控与通道管理 mempool.space 是一款功能强大的比特币生态系统浏览器不仅提供区块链数据查询还后端前端Paramiko Packetizer底层原理SSH加密、MAC校验与动态Rekey机制全解析Paramiko Packetizer底层原理SSH加密、MAC校验与动态Rekey机制全解析 Paramiko 是 Python 生态中最领先的 SSHv2后端网络通信密码学上一篇Gemma4-26B-A4B-QAT-Uncensored-HauhauCS-Balanced-MTP高级配置指南优化采样参数与GPU设置下一篇JSVerbalExpressions在移动开发中的应用React Native实例创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考