Encryption 如何实现零停机键旋转

Encryption 如何实现零停机键旋转,encryption,secret-key,aws-secrets-manager,shared-secret,Encryption,Secret Key,Aws Secrets Manager,Shared Secret,我有几个微服务在AWS中运行,其中一些相互通信,一些具有外部客户机或是外部服务的客户机 为了实现我的服务,我需要一些秘密(RSA密钥对来签名/验证令牌、对称密钥、API密钥等)。我正在为此使用AWS SecretsManager,它运行良好,但我现在正在实现对键旋转的适当支持,我有一些想法 我正在使用AWS SecretsManager,定期(约5分钟)获取机密并在本地缓存它们 我正在使用AWS SecretsManager的版本阶段功能,根据需要引用AWS当前版本和AWS以前的版本 假设服

我有几个微服务在AWS中运行,其中一些相互通信,一些具有外部客户机或是外部服务的客户机

为了实现我的服务,我需要一些秘密(RSA密钥对来签名/验证令牌、对称密钥、API密钥等)。我正在为此使用AWS SecretsManager,它运行良好,但我现在正在实现对键旋转的适当支持,我有一些想法

  • 我正在使用AWS SecretsManager,定期(约5分钟)获取机密并在本地缓存它们
  • 我正在使用AWS SecretsManager的版本阶段功能,根据需要引用AWS当前版本和AWS以前的版本
假设服务A需要服务B的密钥K:

  • 假设在开始时,K具有当前值K1和之前的值K0
  • 在与B的通信中,服务A将始终使用(并在本地缓存)K的AWSCURRENT版本,因此在本例中为K1
  • 服务B将在其本地缓存中保留版本AWSCURRENT和AWSPREVIOUS,并同时接受[K1,K0]
  • 在旋转K时,我首先确保服务B使用的秘密被旋转,以便在刷新间隔结束后,服务B的所有实例都接受[K2,K1],而不是[K1,K0]。在刷新间隔结束之前,的所有实例仍使用K1
  • 当刷新间隔结束时,意味着B的所有实例都必须获取K2,我旋转键进行服务,以便A将使用K1或K2,直到刷新间隔结束,然后仅使用K2
  • 这就完成了密钥轮换(但是如果K1被认为被泄露了,我们可以再次轮换B的秘密来推出K1并得到[K3,K2])

这是最好的方法还是其他人要考虑?

然后,在某些情况下,我有一个在同一服务中使用的对称密钥J,例如用于加密某个会话的密钥。因此,在对服务C的一个请求中,会话使用密钥J1加密,然后需要在稍后阶段使用J1解密。我有多个C服务实例

这里的问题是,如果加密和解密都使用相同的密钥,那么旋转它会变得更加混乱-如果密钥被旋转为具有值J2,并且一个实例已刷新,以便使用J2进行加密,而另一个实例仍然看不到J2,则解密将失败

我可以在这里看到一些方法:

  • 使用单独的旋转方案拆分为两个秘密,并一次旋转一个,类似于上面所述。这增加了额外机密处理的开销,这些机密具有相同的值(除了它们之间会有一段时间的轮换)

  • 让解密在失败时强制刷新机密:

    • 加密始终使用AWSCURRENT(J1或J2取决于是否刷新)
    • 解密将先尝试AWSCURRENT,然后尝试AWSPREVIOUS,如果两者都失败(因为存储了J2和[J1,J0]所使用的另一个实例的加密),则将请求手动刷新机密([J2,J1]现在已存储),然后再次尝试AWSCURRENT和AWSPREVIOUS
  • 在密钥窗口中使用三个密钥,并始终使用中间的密钥加密,因为它应该始终位于所有其他实例的窗口中(除非它旋转了几次,速度比刷新间隔快)。这增加了复杂性

  • 还有什么其他选择?这似乎是一个标准用例,但我仍在努力寻找最佳方法

    编辑------------------

    根据JoeB的回答,到目前为止我提出的算法是: 假设最初机密的当前值为K1,挂起值为null

    正常操作

    • 所有服务定期(每T秒)向SecretsManager查询
      AWSCURRENT
      AWSCURRENT
      和自定义标签
      ROTATING
      ,并全部接受(如果存在)->所有服务接受[
      AWSCURRENT
      =K1]
    • 所有客户端都使用
      AWSCURRENT
      =K1
    关键点旋转

  • 为挂起阶段输入新值K2
  • 等待T秒->所有服务现在接受[
    AWSCURRENT
    =K1,
    awexpensing
    =K2]
  • ROTATING
    添加到K1版本+将
    AWSCURRENT
    移动到K2版本+从K2中移除
    AWSCURRENT
    标签(似乎没有标签的原子交换)。直到T秒过去,一些客户端将使用K2和一些K1,但所有服务都接受这两种
  • 等待T秒->所有服务仍然接受[
    AWSCURRENT
    =K2,
    awexpensing
    =K1],并且所有客户端都使用
    AWSCURRENT
    =K2
  • 从K1上卸下旋转的
    级。请注意,K1仍将具有先前的
    AWSPREVIOUS
    阶段
  • T秒后,所有服务将只接受[
    AWSCURRENT
    =K2],K1实际上已失效
  • 这应该适用于单独的秘密以及用于加密和解密的对称秘密


    不幸的是,我不知道如何使用内置的旋转机制,因为它需要几个步骤,中间有延迟。一个想法是发明一些自定义步骤,让
    setSecret
    步骤创建一个CloudWatch cron事件,该事件将在T秒后再次调用该函数,使用步骤
    swapPending
    removePending
    调用它。如果SecretsManager能够自动支持这一点,那就太棒了,例如支持函数返回一个值,指示下一步应该在T秒后调用。

    对于您的凭证问题,只要服务B支持两个活动凭据,就不必在应用程序中同时保留当前凭据和以前的凭据。为此,您必须确保凭证在准备就绪之前未标记为AWSCURRENT。然后应用程序总是获取并使用