如何确保Java对象(包含加密材料)被清零?

5

我担心由垃圾回收器管理的加密密钥和机密信息可能会在内存中被复制和移动而没有经过清零操作。

作为可能的解决方案,是否仅需要:

public class Key {
  private char[] key;
  // ...
  protected void finalize() throws Throwable { 
    try {
      for(int k = 0; k < key.length; k++) {
        key[k] = '\0';
      }
    } catch (Exception e) {
      //...
    } finally {
      super.finalize();
    }
  }
  // ...
}

编辑:请注意,我的问题不仅涉及对象的官方(参考)副本的清零,还包括垃圾回收器在为了空间和速度效率而移动内存时可能生成的任何旧副本。

最简单的例子是标记-清除垃圾收集器,其中对象被标记为“引用”,然后所有这些对象都被复制到另一个区域。其余的对象则是垃圾,因此会被收集。当复制发生时,可能会留下残留的关键数据,这些数据不再由垃圾回收器管理(因为“正式”数据位于新区域中)。

此问题的试金石是,如果您在加密模块中使用密钥、将密钥清零,然后检查整个JVM进程空间,您不应该找到该密钥。


我四处查找,但没有找到访问GC数据的方法。如果删除属性和其值之间的引用,就无法再次访问它,这是我的答案,但我不确定,因为你有这个疑问。 - marionmaiden
不太确定重点在哪里。信息肯定是存储在内存中的。如果攻击者可以访问本地内存,那么你就基本上束手无策了。 - james
@詹姆斯,这个想法是为了风险缓解。当你使用它时,它确实存在。这个想法是当你不再需要它时摆脱它,以减少你必须承担的风险量。 - Jeremy Powell
如果你想销售具有加密功能的产品,你必须在FIPS 140-2下对其进行认证,这要求立即进行密钥清零。 - Jeremy Powell
1
除非你在本地代码中进行加密,否则你基本上会陷入困境。即使下面的allocateDirect解决方案也不会真正有所帮助,因为你需要从数组中读取密钥才能实际使用它... - james
显示剩余2条评论
4个回答

4

这是一个非常好的观点,但我不确定它是否足够。请查看我最近对问题的更新。 - Jeremy Powell
使用较新的Java虚拟机,仅覆盖数组内容是不够的,因为该数组可能会在进程内存空间中移动,例如如果堆进行了碎片整理。 - jarnbjo

1
你最好使用NIO中的allocateDirect。在一个合理的实现中,它不应该被移动。但它可能会有资格被分页到磁盘上(我猜你可以轮询它)并休眠。

1

如果内存中的数据没有在特定时间被覆盖,那又怎样呢?如果你担心攻击者读取你机器的内存,那才是问题所在,而不是他在什么时间能在那里找到什么。

你担心的是什么实际攻击场景,使得留在JVM内存中的密钥成为一个严重的问题?如果攻击者可以查看JVM的内存,那么他也可以在你使用密钥时捕获它们。

如果你担心攻击者以普通用户身份运行并获取JVM丢弃的内存:据我所知,所有现代操作系统都会用零覆盖新分配的内存。


首先,这个想法只是为了风险缓解。你在内存中保存密钥的时间越短,攻击者看到它的机会就越小。然而,你是完全正确的。我面临的问题相当牵强。FIPS 140-2要求源自硬件要求,假定每个人都可以访问硬件,因此你需要缩小攻击窗口。对于软件模块来说,这没有太多意义。 - Jeremy Powell

0

所以我从@jambjo和@james得出结论,实际上没有什么办法可以防止密钥被复制并变得无法跟踪。

作为开发人员,最好的策略是将加密库下降到C(或任何其他非托管语言)中,并在那里实现您的内容。

如果有人能提出更好的解决方案,我将非常乐意接受他们的答案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接