使用 Lets Encrypt 固定 iOS 和 Android 证书

信息安全 加密 证书 安卓 IOS 证书固定
2021-08-17 14:28:49

我们在为 iOS 和 Android 开发时使用证书固定。我们使用付费的常规 12 个月证书。

我们最近为我们的一般网站证书切换到 Lets Encrypt (LE),并希望在我们的移动应用程序中使用 LE 证书。

问题是我们当前的证书固定框架要求应用程序与每个新证书一起重新提交,因此如果使用 LE,这将是每 3 个月(LE 证书的最大长度)。这是不切实际的。

我研究了替代方案,最可行的似乎是公钥散列。因此查询服务器获取证书,提取公钥并与我们存储在应用程序二进制文件中的哈希值进行比较。

这种方法意味着我们将手动创建用于 LE 的证书签名请求 (CSR),因为目前它只是为每个证书请求创建一个新的,以保留原始密钥对。

我的问题:这个公钥散列是移动应用程序中长期证书固定的最佳解决方案吗?

1个回答

TLDR;在颁发证书的生命周期内执行证书创建和应用程序提交,或者;不要使用 Let's Encrypt,或者;不要在应用程序中使用固定,而是应用 DANE/DNSSEC 和 CAA。

解释:

使用证书固定的嵌入式应用程序(如 iOS 和 Android)的常规方法是拥有多个固定证书,且不存在(或很少)重叠日期,因此notBefore下一个有效固定证书的日期与上一个有效固定证书notAfter日期相同。

默认情况下,Let's Encrypt 证书notBefore从请求开始故意回溯 1 小时。由 CA 决定日期notBeforenotAfter日期,并且不支持用于嵌入式应用程序的方法。

Let's encrypt 允许您生成 CSR,这是自定义他们颁发的证书的最佳方式,这是我自己的生产应用程序中的示例:

申请证书 1:

certbot certonly \
  --dry-run \
  -n \
  --dns-route53 \
  --agree-tos \
  --csr "<path to my CSR>" \
  --register-unsafely-without-email \
  --no-eff-email \
  --dns-route53-propagation-seconds 300 \
  --cert-path "<path to store the issued X.509 Leaf Certificate>" \
  --key-path "<path to store the private key>" \
  --fullchain-path "<path to store the issued X.509 Leaf Certificate and the chain of intermediate and CA Certificates as a bundle>" \
  --chain-path "<path to store the chain of intermediate and CA Certificates as a bundle minus the actual issued X.509 Leaf Certificate>" \
  -d sub.domain.tld

为同一域请求另一个证书,不删除原始证书,并在第一个证书后 2 个月运行:

certbot certonly \
  --- same as above ---
  --duplicate \
  --force-renewal \
  -d sub.domain.tld

第二个证书是notBefore在第一个证书后 2 个月颁发的。

需要注意的是,CSR 格式 (PKCS#10) 没有任何字段来放置这些日期,因此它仍然取决于您无法控制的 Let's Encrypt 隐藏决策来获得此结果。

回到威胁模型并更好地理解为什么实施证书固定对您来说会更有益。大多数情况下,为了更合适的选项,pin 被删除了,因为 pin 曾经是许多不适合的事情的解决方案,今天我们有 TLSA/DANE、DNSSEC 等选项,并使用 CAA 记录来完全验证预期的信任链信任锚。

如果您发现您的威胁模型表明固定是您识别出的任何风险的最佳缓解措施,那么您将需要找到一个 CA,该 CA 可以颁发未来日期 ( notBefore) 的叶证书并在您的应用程序中部署多个固定证书。