标签 私钥 下的文章

学习密码学背后的基本概念,主要是保密性、完整性和身份认证。

安全通信正快速成为当今互联网的规范。从 2018 年 7 月起,Google Chrome 将对全部使用 HTTP 传输(而不是 HTTPS 传输)的站点开始显示“不安全”警告。虽然密码学已经逐渐变广为人知,但其本身并没有变得更容易理解。Let's Encrypt 设计并实现了一套令人惊叹的解决方案,可以提供免费安全证书和周期性续签;但如果不了解底层概念和缺陷,你也不过是加入了类似“ 货物崇拜 cargo cult ”的技术崇拜的程序员大军。

安全通信的特性

密码学最直观明显的目标是 保密性 confidentiality 消息 message 传输过程中不会被窥探内容。为了保密性,我们对消息进行加密:对于给定消息,我们结合一个 密钥 key 生成一个无意义的乱码,只有通过相同的密钥逆转加密过程(即解密过程)才能将其转换为可读的消息。假设我们有两个朋友 Alice 和 Bob,以及他们的 八卦 nosy 邻居 Eve。Alice 加密类似 "Eve 很讨厌" 的消息,将其发送给 Bob,期间不用担心 Eve 会窥探到这条消息的内容。

对于真正的安全通信,保密性是不够的。假如 Eve 收集了足够多 Alice 和 Bob 之间的消息,发现单词 “Eve” 被加密为 "Xyzzy"。除此之外,Eve 还知道 Alice 和 Bob 正在准备一个派对,Alice 会将访客名单发送给 Bob。如果 Eve 拦截了消息并将 “Xyzzy” 加到访客列表的末尾,那么她已经成功的破坏了这个派对。因此,Alice 和 Bob 需要他们之间的通信可以提供 完整性 integrity :消息应该不会被篡改。

而且我们还有一个问题有待解决。假如 Eve 观察到 Bob 打开了标记为“来自 Alice”的信封,信封中包含一条来自 Alice 的消息“再买一加仑冰淇淋”。Eve 看到 Bob 外出,回家时带着冰淇淋,这样虽然 Eve 并不知道消息的完整内容,但她对消息有了大致的了解。Bob 将上述消息丢弃,但 Eve 找出了它并在下一周中的每一天都向 Bob 的邮箱中投递一封标记为“来自 Alice”的信封,内容拷贝自之前 Bob 丢弃的那封信。这样到了派对的时候,冰淇淋严重超量;派对当晚结束后,Bob 分发剩余的冰淇淋,Eve 带着免费的冰淇淋回到家。消息是加密的,完整性也没问题,但 Bob 被误导了,没有认出发信人的真实身份。 身份认证 Authentication 这个特性用于保证你正在通信的人的确是其声称的那样。

信息安全还有其它特性,但保密性、完整性和身份验证是你必须了解的三大特性。

加密和加密算法

加密都包含哪些部分呢?首先,需要一条消息,我们称之为 明文 plaintext 。接着,需要对明文做一些格式上的初始化,以便用于后续的加密过程(例如,假如我们使用 分组加密算法 block cipher ,需要在明文尾部填充使其达到特定长度)。下一步,需要一个保密的比特序列,我们称之为 密钥 key 。然后,基于私钥,使用一种加密算法将明文转换为 密文 ciphertext 。密文看上去像是随机噪声,只有通过相同的加密算法和相同的密钥(在后面提到的非对称加密算法情况下,是另一个数学上相关的密钥)才能恢复为明文。

(LCTT 译注:cipher 一般被翻译为密码,但其具体表达的意思是加密算法,这里采用加密算法的翻译)

加密算法使用密钥加密明文。考虑到希望能够解密密文,我们用到的加密算法也必须是 可逆的 reversible 。作为简单示例,我们可以使用 XOR。该算子可逆,而且逆算子就是本身(P ^ K = C; C ^ K = P),故可同时用于加密和解密。该算子的平凡应用可以是 一次性密码本 one-time pad ,不过一般而言并不可行。但可以将 XOR 与一个基于单个密钥生成 任意随机数据流 arbitrary stream of random data 的函数结合起来。现代加密算法 AES 和 Chacha20 就是这么设计的。

我们把加密和解密使用同一个密钥的加密算法称为 对称加密算法 symmetric cipher 。对称加密算法分为 流加密算法 stream ciphers 和分组加密算法两类。流加密算法依次对明文中的每个比特或字节进行加密。例如,我们上面提到的 XOR 加密算法就是一个流加密算法。流加密算法适用于明文长度未知的情形,例如数据从管道或 socket 传入。RC4 是最为人知的流加密算法,但在多种不同的攻击面前比较脆弱,以至于最新版本 (1.3)的 TLS (“HTTPS” 中的 “S”)已经不再支持该加密算法。Efforts 正着手创建新的加密算法,候选算法 ChaCha20 已经被 TLS 支持。

分组加密算法对固定长度的分组,使用固定长度的密钥加密。在分组加密算法领域,排行第一的是 先进加密标准 Advanced Encryption Standard (AES),使用的分组长度为 128 比特。分组包含的数据并不多,因而分组加密算法包含一个工作模式,用于描述如何对任意长度的明文执行分组加密。最简单的工作模式是 电子密码本 Electronic Code Book (ECB),将明文按分组大小划分成多个分组(在必要情况下,填充最后一个分组),使用密钥独立的加密各个分组。

这里我们留意到一个问题:如果相同的分组在明文中出现多次(例如互联网流量中的 GET / HTTP/1.1 词组),由于我们使用相同的密钥加密分组,我们会得到相同的加密结果。我们的安全通信中会出现一种 模式规律 pattern ,容易受到攻击。

因此还有很多高级的工作模式,例如 密码分组链接 Cipher Block Chaining (CBC),其中每个分组的明文在加密前会与前一个分组的密文进行 XOR 操作,而第一个分组的明文与一个随机数构成的初始化向量进行 XOR 操作。还有其它一些工作模式,在安全性和执行速度方面各有优缺点。甚至还有 Counter (CTR) 这种工作模式,可以将分组加密算法转换为流加密算法。

除了对称加密算法,还有 非对称加密算法 asymmetric ciphers ,也被称为 公钥密码学 public-key cryptography 。这类加密算法使用两个密钥:一个 公钥 public key ,一个 私钥 private key 。公钥和私钥在数学上有一定关联,但可以区分二者。经过公钥加密的密文只能通过私钥解密,经过私钥加密的密文可以通过公钥解密。公钥可以大范围分发出去,但私钥必须对外不可见。如果你希望和一个给定的人通信,你可以使用对方的公钥加密消息,这样只有他们的私钥可以解密出消息。在非对称加密算法领域,目前 RSA) 最具有影响力。

非对称加密算法最主要的缺陷是,它们是 计算密集型 computationally expensive 的。那么使用对称加密算法可以让身份验证更快吗?如果你只与一个人共享密钥,答案是肯定的。但这种方式很快就会失效。假如一群人希望使用对称加密算法进行两两通信,如果对每对成员通信都采用单独的密钥,一个 20 人的群体将有 190 对成员通信,即每个成员要维护 19 个密钥并确认其安全性。如果使用非对称加密算法,每个成员仅需确保自己的私钥安全并维护一个公钥列表即可。

非对称加密算法也有加密数据长度限制。类似于分组加密算法,你需要将长消息进行划分。但实际应用中,非对称加密算法通常用于建立 机密 confidential 已认证 authenticated 通道 channel ,利用该通道交换对称加密算法的共享密钥。考虑到速度优势,对称加密算法用于后续的通信。TLS 就是严格按照这种方式运行的。

基础

安全通信的核心在于随机数。随机数用于生成密钥并为 确定性过程 deterministic processes 提供不可预测性。如果我们使用的密钥是可预测的,那我们从一开始就可能受到攻击。计算机被设计成按固定规则操作,因此生成随机数是比较困难的。计算机可以收集鼠标移动或 键盘计时 keyboard timings 这类随机数据。但收集随机性(也叫 信息熵 entropy )需要花费不少时间,而且涉及额外处理以确保 均匀分布 uniform distribution 。甚至可以使用专用硬件,例如 熔岩灯 lava lamps 等。一般而言,一旦有了一个真正的随机数值,我们可以将其用作 种子 seed ,使用 密码安全的伪随机数生成器 cryptographically secure pseudorandom number generator 生成随机数。使用相同的种子,同一个随机数生成器生成的随机数序列保持不变,但重要的是随机数序列是无规律的。在 Linux 内核中,/dev/random 和 /dev/urandom 工作方式如下:从多个来源收集信息熵,进行 无偏处理 remove biases ,生成种子,然后生成随机数,该随机数可用于 RSA 密钥生成等。

其它密码学组件

我们已经实现了保密性,但还没有考虑完整性和身份验证。对于后两者,我们需要使用一些额外的技术。

首先是 密码散列函数 crytographic hash function ,该函数接受任意长度的输入并给出固定长度的输出(一般称为 摘要 digest )。如果我们找到两条消息,其摘要相同,我们称之为 碰撞 collision ,对应的散列函数就不适合用于密码学。这里需要强调一下“找到”:考虑到消息的条数是无限的而摘要的长度是固定的,那么总是会存在碰撞;但如果无需海量的计算资源,我们总是能找到发生碰撞的消息对,那就令人比较担心了。更严重的情况是,对于每一个给定的消息,都能找到与之碰撞的另一条消息。

另外,哈希函数必须是 单向的 one-way :给定一个摘要,反向计算对应的消息在计算上不可行。相应的,这类条件被称为 碰撞阻力 collision resistance 第二原象抗性 second preimage resistance 原象抗性 preimage resistance 。如果满足这些条件,摘要可以用作消息的指纹。理论上不存在具有相同指纹的两个人,而且你无法使用指纹反向找到其对应的人。

如果我们同时发送消息及其摘要,接收者可以使用相同的哈希函数独立计算摘要。如果两个摘要相同,可以认为消息没有被篡改。考虑到 SHA-1 已经变得有些过时,目前最流行的密码散列函数是 SHA-256

散列函数看起来不错,但如果有人可以同时篡改消息及其摘要,那么消息发送仍然是不安全的。我们需要将哈希与加密算法结合起来。在对称加密算法领域,我们有 消息认证码 message authentication codes (MAC)技术。MAC 有多种形式,但 哈希消息认证码 hash message authentication codes (HMAC) 这类是基于哈希的。HMAC 使用哈希函数 H 处理密钥 K、消息 M,公式为 H(K + H(K + M)),其中 + 代表 连接 concatenation 。公式的独特之处并不在本文讨论范围内,大致来说与保护 HMAC 自身的完整性有关。发送加密消息的同时也发送 MAC。Eve 可以任意篡改消息,但一旦 Bob 独立计算 MAC 并与接收到的 MAC 做比较,就会发现消息已经被篡改。

在非对称加密算法领域,我们有 数字签名 digital signatures 技术。如果使用 RSA,使用公钥加密的内容只能通过私钥解密,反过来也是如此;这种机制可用于创建一种签名。如果只有我持有私钥并用其加密文档,那么只有我的公钥可以用于解密,那么大家潜在的承认文档是我写的:这是一种身份验证。事实上,我们无需加密整个文档。如果生成文档的摘要,只要对这个指纹加密即可。对摘要签名比对整个文档签名要快得多,而且可以解决非对称加密存在的消息长度限制问题。接收者解密出摘要信息,独立计算消息的摘要并进行比对,可以确保消息的完整性。对于不同的非对称加密算法,数字签名的方法也各不相同;但核心都是使用公钥来检验已有签名。

汇总

现在,我们已经有了全部的主体组件,可以用其实现一个我们期待的、具有全部三个特性的 体系 system 。Alice 选取一个保密的对称加密密钥并使用 Bob 的公钥进行加密。接着,她对得到的密文进行哈希并使用其私钥对摘要进行签名。Bob 接收到密文和签名,一方面独立计算密文的摘要,另一方面使用 Alice 的公钥解密签名中的摘要;如果两个摘要相同,他可以确信对称加密密钥没有被篡改且通过了身份验证。Bob 使用私钥解密密文得到对称加密密钥,接着使用该密钥及 HMAC 与 Alice 进行保密通信,这样每一条消息的完整性都得到保障。但该体系没有办法抵御消息重放攻击(我们在 Eve 造成的冰淇淋灾难中见过这种攻击)。要解决重放攻击,我们需要使用某种类型的“ 握手 handshake ”建立随机、短期的 会话标识符 session identifier

密码学的世界博大精深,我希望这篇文章能让你对密码学的核心目标及其组件有一个大致的了解。这些概念为你打下坚实的基础,让你可以继续深入学习。

感谢 Hubert Kario、Florian Weimer 和 Mike Bursell 在本文写作过程中提供的帮助。


via: https://opensource.com/article/18/5/cryptography-pki

作者:Alex Wood 选题:lujun9972 译者:pinewall 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

存储 SSH 的加密秘钥和记住密码一直是一个让人头疼的问题。但是不幸的是,在当前这个充满了恶意黑客和攻击的世界中,基本的安全预防是必不可少的。对于许多普通用户来说,大多数人只能是记住密码,也可能寻找到一个好程序去存储密码,正如我们提醒这些用户不要在每个网站采用相同的密码。但是对于在各个 IT 领域的人们,我们需要将这个事情提高一个层面。我们需要使用像 SSH 密钥这样的加密秘钥,而不只是密码。

设想一个场景:我有一个运行在云上的服务器,用作我的主 git 库。我有很多台工作电脑,所有这些电脑都需要登录到这个中央服务器去做 push 与 pull 操作。这里我设置 git 使用 SSH。当 git 使用 SSH 时,git 实际上是以 SSH 的方式登录到服务器,就好像你通过 SSH 命令打开一个服务器的命令行一样。为了把这些配置好,我在我的 .ssh 目录下创建一个配置文件,其中包含一个有服务器名字、主机名、登录用户、密钥文件路径等信息的主机项。之后我可以通过输入如下命令来测试这个配置是否正确。

ssh gitserver

很快我就可以访问到服务器的 bash shell。现在我可以配置 git 使用相同配置项以及存储的密钥来登录服务器。这很简单,只是有一个问题:对于每一个我要用它登录服务器的电脑,我都需要有一个密钥文件,那意味着需要密钥文件会放在很多地方。我会在当前这台电脑上存储这些密钥文件,我的其他电脑也都需要存储这些。就像那些有特别多的密码的用户一样,我们这些 IT 人员也被这些特别多的密钥文件淹没。怎么办呢?

清理

在我们开始帮助你管理密钥之前,你需要有一些密钥应该怎么使用的基础知识,以及明白我们下面的提问的意义所在。同时,有个前提,也是最重要的,你应该知道你的公钥和私钥该放在哪里。然后假设你应该知道:

  1. 公钥和私钥之间的差异;
  2. 为什么你不可以从公钥生成私钥,但是反之则可以?
  3. authorized_keys 文件的目的以及里面包含什么内容;
  4. 如何使用私钥去登录一个你的对应公钥存储在其上的 authorized_keys 文件中的服务器。

这里有一个例子。当你在亚马逊的网络服务上创建一个云服务器,你必须提供一个用于连接你的服务器的 SSH 密钥。每个密钥都有一个公开的部分(公钥)和私密的部分(私钥)。你要想让你的服务器安全,乍看之下你可能应该将你的私钥放到服务器上,同时你自己带着公钥。毕竟,你不想你的服务器被公开访问,对吗?但是实际上的做法正好是相反的。

你应该把自己的公钥放到 AWS 服务器,同时你持有用于登录服务器的私钥。你需要保护好私钥,并让它处于你的控制之中,而不是放在一些远程服务器上,正如上图中所示。

原因如下:如果公钥被其他人知道了,它们不能用于登录服务器,因为他们没有私钥。进一步说,如果有人成功攻入你的服务器,他们所能找到的只是公钥,他们不可以从公钥生成私钥。同时,如果你在其他的服务器上使用了相同的公钥,他们不可以使用它去登录别的电脑。

这就是为什么你要把你自己的公钥放到你的服务器上以便通过 SSH 登录这些服务器。你持有这些私钥,不要让这些私钥脱离你的控制。

但是还有一点麻烦。试想一下我 git 服务器的例子。我需要做一些抉择。有时我登录架设在别的地方的开发服务器,而在开发服务器上,我需要连接我的 git 服务器。如何使我的开发服务器连接 git 服务器?显然是通过使用私钥,但这样就会有问题。在该场景中,需要我把私钥放置到一个架设在别的地方的服务器上,这相当危险。

一个进一步的场景:如果我要使用一个密钥去登录许多的服务器,怎么办?如果一个入侵者得到这个私钥,这个人就能用这个私钥得到整个服务器网络的权限,这可能带来一些严重的破坏,这非常糟糕。

同时,这也带来了另外一个问题,我真的应该在这些其他服务器上使用相同的密钥吗?因为我刚才描述的,那会非常危险的。

最后,这听起来有些混乱,但是确实有一些简单的解决方案。让我们有条理地组织一下。

(注意,除了登录服务器,还有很多地方需要私钥密钥,但是我提出的这个场景可以向你展示当你使用密钥时你所面对的问题。)

常规口令

当你创建你的密钥时,你可以选择是否包含一个密钥使用时的口令。有了这个口令,私钥文件本身就会被口令所加密。例如,如果你有一个公钥存储在服务器上,同时你使用私钥去登录服务器的时候,你会被提示输入该口令。没有口令,这个密钥是无法使用的。或者你也可以配置你的密钥不需要口令,然后只需要密钥文件就可以登录服务器了。

一般来说,不使用口令对于用户来说是更方便的,但是在很多情况下我强烈建议使用口令,原因是,如果私钥文件被偷了,偷密钥的人仍然不可以使用它,除非他或者她可以找到口令。在理论上,这个将节省你很多时间,因为你可以在攻击者发现口令之前,从服务器上删除公钥文件,从而保护你的系统。当然还有一些使用口令的其它原因,但是在很多场合这个原因对我来说更有价值。(举一个例子,我的 Android 平板上有 VNC 软件。平板上有我的密钥。如果我的平板被偷了之后,我会马上从服务器上删除公钥,使得它的私钥没有作用,无论有没有口令。)但是在一些情况下我不使用口令,是因为我正在登录的服务器上没有什么有价值的数据,这取决于情境。

服务器基础设施

你如何设计自己服务器的基础设施将会影响到你如何管理你的密钥。例如,如果你有很多用户登录,你将需要决定每个用户是否需要一个单独的密钥。(一般来说,应该如此;你不会想在用户之间共享私钥。那样当一个用户离开组织或者失去信任时,你可以删除那个用户的公钥,而不需要必须给其他人生成新的密钥。相似地,通过共享密钥,他们能以其他人的身份登录,这就更糟糕了。)但是另外一个问题是你如何配置你的服务器。举例来说,你是否使用像 Puppet 这样工具配置大量的服务器?你是否基于你自己的镜像创建大量的服务器?当你复制你的服务器,是否每一个的密钥都一样?不同的云服务器软件允许你配置如何选择;你可以让这些服务器使用相同的密钥,也可以给每一个服务器生成一个新的密钥。

如果你在操作这些复制的服务器,如果用户需要使用不同的密钥登录两个不同但是大部分都一样的系统,它可能导致混淆。但是另一方面,服务器共享相同的密钥会有安全风险。或者,第三,如果你的密钥有除了登录之外的需要(比如挂载加密的驱动),那么你会在很多地方需要相同的密钥。正如你所看到的,你是否需要在不同的服务器上使用相同的密钥不是我能为你做的决定;这其中有权衡,你需要自己去决定什么是最好的。

最终,你可能会有:

  • 需要登录的多个服务器
  • 多个用户登录到不同的服务器,每个都有自己的密钥
  • 每个用户使用多个密钥登录到不同的服务器

(如果你正在别的情况下使用密钥,这个同样的普适理论也能应用于如何使用密钥,需要多少密钥,它们是否共享,你如何处理公私钥等方面。)

安全方法

了解你的基础设施和特有的情况,你需要组合一个密钥管理方案,它会指导你如何去分发和存储你的密钥。比如,正如我之前提到的,如果我的平板被偷了,我会从我服务器上删除公钥,我希望这在平板在用于访问服务器之前完成。同样的,我会在我的整体计划中考虑以下内容:

  1. 私钥可以放在移动设备上,但是必须包含口令;
  2. 必须有一个可以快速地从服务器上删除公钥的方法。

在你的情况中,你可能决定你不想在自己经常登录的系统上使用口令;比如,这个系统可能是一个开发者一天登录多次的测试机器。这没有问题,但是你需要调整一点你的规则。你可以添加一条规则:不可以通过移动设备登录该机器。换句话说,你需要根据自己的状况构建你的准则,不要假设某个方案放之四海而皆准。

软件

至于软件,令人吃惊的是,现实世界中并没有很多好的、可靠的存储和管理私钥的软件解决方案。但是应该有吗?考虑下这个,如果你有一个程序存储你所有服务器的全部密钥,并且这个程序被一个快捷的密钥锁住,那么你的密钥就真的安全了吗?或者类似的,如果你的密钥被放置在你的硬盘上,用于 SSH 程序快速访问,密钥管理软件是否真正提供了任何保护吗?

但是对于整体基础设施和创建/管理公钥来说,有许多的解决方案。我已经提到了 Puppet,在 Puppet 的世界中,你可以创建模块以不同的方式管理你的服务器。这个想法是服务器是动态的,而且不需要精确地复制彼此。这里有一个聪明的方法,在不同的服务器上使用相同的密钥,但是对于每一个用户使用不同的 Puppet 模块。这个方案可能适合你,也可能不适合你。

或者,另一个选择就是完全换个不同的档位。在 Docker 的世界中,你可以采取一个不同的方式,正如关于 SSH 和 Docker 博客所描述的那样。

但是怎么样管理私钥?如果你搜索过的话,你无法找到很多可以选择的软件,原因我之前提到过;私钥存放在你的硬盘上,一个管理软件可能无法提到更多额外的安全。但是我使用这种方法来管理我的密钥:

首先,我的 .ssh/config 文件中有很多的主机项。我要登录的都有一个主机项,但是有时我对于一个单独的主机有不止一项。如果我有很多登录方式,就会出现这种情况。对于放置我的 git 库的服务器来说,我有两个不同的登录项;一个限制于 git,另一个用于一般用途的 bash 访问。这个为 git 设置的登录选项在机器上有极大的限制。还记得我之前说的我存储在远程开发机器上的 git 密钥吗?好了。虽然这些密钥可以登录到我其中一个服务器,但是使用的账号是被严格限制的。

其次,大部分的私钥都包含口令。(对于需要多次输入口令的情况,考虑使用 ssh-agent。)

再次,我有一些我想要更加小心地保护的服务器,我不会把这些主机项放在我的 host 文件中。这更加接近于社会工程方面,密钥文件还在,但是可能需要攻击者花费更长的时间去找到这个密钥文件,分析出来它们对应的机器。在这种情况下,我就需要手动打出来一条长长的 SSH 命令。(没那么可怕。)

同时你可以看出来我没有使用任何特别的软件去管理这些私钥。

无放之四海而皆准的方案

我们偶尔会在 linux.com 收到一些问题,询问管理密钥的好软件的建议。但是退一步看,这个问题事实上需要重新思考,因为没有一个普适的解决方案。你问的问题应该基于你自己的情景。你是否简单地尝试找到一个位置去存储你的密钥文件?你是否寻找一个方法去管理多用户问题,其中每个人都需要将他们自己的公钥插入到 authorized_keys 文件中?

通过这篇文章,我已经囊括了这方面的基础知识,希望到此你明白如何管理你的密钥,并且,只有当你问出了正确的问题,无论你寻找任何软件(甚至你需要另外的软件),它都会出现。


via: http://www.linux.com/learn/tutorials/838235-how-to-best-manage-encryption-keys-on-linux

作者:Jeff Cogswell 译者:mudongliang 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出