介绍如何使用Noise保护通信协议

2021-01-23 02:16:30

在后端服务的世界中,提供安全的通信渠道至关重要。传统上,在由Amazon,Microsoft或Google提供的服务中,外部可访问接口是通过HTTPS端点提供的,希望将TLS连接配置为仅允许安全密码套件并提供适当的证书。

在与AWSservices的不同部分的团队进行安全性审查时,我发现团队很容易遵循有关如何保护客户面的标准准则,但是在松散耦合的内部服务中拥有正确的安全性和机密性变得越来越困难。当然,指南也存在,团队也很好地遵循它们。但是在API后面是实现业务逻辑和扩展服务的地方,这是工程师的才智来提出可伸缩体系结构和有效解决方案的地方。在许多情况下,这将需要服务组件之间进行某种类型的通信。并非在所有情况下TLS都是一切的解决方案。

我上一次处理这种情况时,我们进行了以下设置。多个参与者正在通过路由代理进行通信。代理提供基本的基础结构路由功能和非常有限的协议检查。端点之间是松散耦合的,需要端到端的安全性和完整性。

端到端加密有多种选择,例如通过代理嵌套TLS连接,例如使用对称或非对称密钥来保护有效载荷。这些方法都没有优雅和可扩展的感觉。

在这种情况下,我开始研究TLS的工作原理以及它如何提供必要的安全性。在这里,我了解了Diffie-Hellmankey交换以及如何将其嵌入TLS。使用低级的OppenSSL函数,我能够使用临时和静态公钥快速制定基于DH密钥交换的协议,对此我感到很高兴。

但是,这种方法的缺点是,使用OpenSSL的代码相当丑陋,无法保证它在跨编程语言中的工作方式完全一样,并且有人希望不必“滚动自己的密码”。在这一点上,这个想法被放弃了,取而代之的是已经可以使用的东西,但是解决这个问题的方法不太合适。

在接下来的几个月中,我遇到了Noise,这是一个用于基于DH密钥交换构建安全协议的协议框架,该框架旨在使其很难应对通信挑战。

最初,我认为,这可能很有趣,但看起来并不太多。但是在浏览规范时,我喜欢这种方法的简单性并继续阅读。从本质上讲,Noise是建立在握手模式上的,用于建立安全的通信挑战,可以根据情况将这些模式的不同元素组合在一起,然后在查看以下握手模式时会给我留下深刻的印象:

在Noise-speak中,这意味着两个参与方(Alice与Bob)通过以下已达成共识的握手来通信和实现协议。 KK模式代表了双方先前交换过其公钥的场景。为了启动通信,Alice执行了一系列步骤,基本上是一系列的DH和密钥派生步骤,这些步骤由Bob镜像。然后,Bob返回一条消息,其中包含类似的派生密钥。

很棒的事情是,这种特定的模式与我在项目中提出的协议非常相似,但是由于对经过身份验证的数据进行了额外的加密,因此更加透彻且安全。

我什至发现了一个证实,为什么这种方法对我们的案例会非常有益:

0-RTT加密(0往返加密)-通信可以直接从第一个消息开始,因为所有组件都提前知道。在这种情况下,公共密钥。

但这并不能解释其工作方式和原因。在下一节中,我将尝试为上述情况说明密钥是如何生成的,并尝试阐明消息协议如何最终起作用。

噪声应用了一组非常著名且经过研究的原理,使噪声以一种非常优雅的方式工作,从而避免了混乱和错误。需要理解的最重要的部分是:

Diffie-Hellman KeyExchange,特别是椭圆曲线Diffie-Hellman。非常简短的摘要是ECDH允许基于两个非对称密钥对派生共享对称密钥。

使用基于散列的函数的密钥派生,该散列函数使用基于HMAC的散列步骤中使用的相同散列函数。

基本了解使用上述构建块会发生什么之后,我们现在可以更深入地研究消息握手协议。如前所述,握手协议是如何建立内部状态的精确规范,以便双方最终使用相同的对称加密密钥。

但是,Noiseallow不仅可以对寿命长的密钥对使用Diffie-Hellman,还可以使用静态密钥和临时密钥对的几种组合来实现所需的安全性。

在下图中,我将引导您完成“ KK”握手模式的第一个重要序列。快速回顾一下,“ KK”消息模式依赖于以下事实:双方已经交换了其公钥,但希望建立具有前向保密性的安全通信通道。

KK模式的关键要素是双方都知道彼此的公钥。必需的公用密钥的交换以前是在受控环境中进行的。例如,假设有一组后端主机,并且与每个主机一起生成特定的密钥对。在云环境中,您可以使用密钥管理服务来提供这些密钥,并确保它们仅正确绑定到授权主机。

蓝色左侧是Alice,此参与者使用一个静态密钥对进行身份验证,并使用一个临时密钥对进行连接建立。应该为每个新会话生成临时密钥对。

绿色右侧是Bob,该演员具有相似的静态密钥对和临时密钥对进行连接。

绿色箭头表示从Alice到Bob的公用密钥(实心arrowarrow)或从Bob到Alice的公共密钥交换(虚线绿色箭头)。

现在,让我们至少在握手的第一部分中逐步完成该过程,对其进行更多的扩展将使该图特别混乱,但是您将了解所发生的链接过程的要旨,这使得它很容易理解。噪声规范提到了三种状态上下文:握手状态,对称状态,密码状态。

握手状态包含并建立处理消息所需的公钥和私钥。

对称状态包含一个哈希值h和一个链接密钥ck,它们会不断更新以构建内部状态。

密码状态包含对称加密密钥k和随机数n,每次使用加密密钥k时,该随机数n都会增加。 k可用于加密握手消息中的某些有效负载,并且特别适用于零往返加密。

握手的第一部分是处理预消息。某些握手模式没有预消息,其他的则没有。在“ KK”模式的情况下,预消息包含了双方先前交换的公共密钥。

要初始化状态,首先要对协议名称(完整的协议名称包含模式,哈希函数和加密方法)进行哈希处理,然后初始化h和ck变量,这些变量对于跟踪加密状态最重要。在该初始步骤(1)中,h和ck具有相同的值。

现在,对Bob的静态公钥进行哈希处理并更新h。这样就完成了消息前处理。

在处理了预消息之后,现在该处理握手第一部分的所有握手符号了。即,e,es,ss:

处理e意味着哈希Alice的公共临时密钥并更新h。同样,它将把Alice的临时公共密钥附加到发送给Bob的消息缓冲区中。这标志着公共密钥的交换。

处理es:这是我们第一次执行Diffie-Hellman操作以导出密钥。我们使用Alice的临时私钥和Bob的静态公钥来派生新的临时密钥。

使用链接密钥ck和基于HMAC的密钥派生功能可以推导新的链接密钥和加密密钥。基于selectedhash函数的HMAC被多次应用以创建这两个新的secretvalues。在此步骤结束时,用新值更新ck,并将秘密加密密钥k设置为第二个临时密钥,并且将现时值n重置为0。

最后一步是处理ss。这将执行与上一步类似的操作,但使用一组不同的键。它将使用Alice的静态私钥和Bob的静态公钥。首先,它使用Diffie-Hellman生成临时对称密钥。

这是处理ss模式的最后一步。它将上一步的临时输出值用作密钥推导函数的输入,同样会更新密码状态的ck和k。

鲍勃的一面正在执行或多或少的相同操作,但与相反的私钥略有相似。由于Diffie-Hellman的工作方式,Bob的侧面建立了相同的加密面。

上面的步骤仅处理握手的第一模式,第二步骤是处理模式e,ee,se的第二部分。该处理遵循与先前相同的规则,并继续继续修改内部状态,尤其是h和ck。一旦处理完所有模式,双方将调用Split(),后者再次调用密钥派生函数以生成两个密钥,一个用于发送消息,另一个用于接收消息。

一旦拥有了这两个密钥,我们就可以在Alice和Bob之间安全地进行通信。由于将临时密钥与静态密钥结合使用,我们可以进行会话安全通信。

当然,原则上Noise和TLS相对类似,并且交换模式的过程与TLS的过程非常相似。证书包含公共密钥,并且在每个会话中类似地使用TLS临时密钥。

但是,好处是配置空间显着减小,导致更少的表面空间弄乱了某些东西。此外,在传统TLS难以实现的地方使用Noise变得更加简单。 Noise的重点不一定是一个协议,而是要提供一个框架来构建从头开始是安全的协议,但具有适应特定用例的必要灵活性。

在介绍性示例中,我提到了以下情况:在多个后端实例之间嵌套了一个路由器层,该路由器层需要从流量中检查某些属性,但必须不能读取端点之间的流量。与依靠标准TLS相比,这将是利用Noise以更灵活,针对特定问题的方式保护流量的绝佳机会。

但是,当然,您的里程可能会有所不同,因此始终有充分的理由证明使用Noise(而非TLS)是合理的。 总而言之,我可能会错过了某些需要额外突出显示的“噪声”功能,但我将其留给您直接从规格中查找。 介绍性视频,说明如何处理握手。 该视频已成为此详细博客文章的灵感的一部分:https://cryptoservices.github.io/cryptography/protocols/2016/04/27/noise-protocol.html