微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 Java 14 中销毁 SecretKey?

如何解决如何在 Java 14 中销毁 SecretKey?

我试图在解密后清除我的 Secretkey。 从我读过的内容来看,从 Java 8 开始,SecretKeys 可以通过 destroy 方法销毁。 我使用的是 Java 14,所以应该可以。

但是,每当我在键上使用 destroy 方法时,都会抛出 DestroyFailedException。 我还看到人们在他们的代码中忽略了该异常,但是,如果我这样做,我可以在调用 destroy 方法后打印密钥。

这里是我的解密方法

private byte[] decrypt(byte[] encryptedText,char[] password) throws InvalidKeyException,InvalidAlgorithmParameterException,NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeySpecException,IllegalBlockSizeException,BadPaddingException,DestroyFailedException {

    ByteBuffer bb = ByteBuffer.wrap(encryptedText);

    byte[] iv = new byte[ivLengthByte];
    bb.get(iv);

    byte[] salt = new byte[saltLengthByte];
    bb.get(salt);

    byte[] cipherText = new byte[bb.remaining()];
    bb.get(cipherText);

    SecretKey key;
    key = crypto.getAESKeyFromPassword(password,salt);

    Cipher cipher;
    cipher = Cipher.getInstance(algorithm);

    cipher.init(Cipher.DECRYPT_MODE,key,new GCMParameterSpec(tagLengthBit,iv));

    byte[] plainText = cipher.doFinal(cipherText);

    Main.cleararray(password,null);
    Main.cleararray(null,iv);
    Main.cleararray(null,salt);
    Main.cleararray(null,cipherText);

    key.destroy();

    cipher = null;

    return plainText;

}

调用 destroy 方法后,我可以通过 String encodingKey = Base64.getEncoder().encodetoString(key.getEncoded());

编辑: 在数组上使用我的 Clear 方法后,我仍然可以打印它:

byte[] temp = key.getEncoded();
        Main.cleararray(null,temp);

清除数组:

protected static void cleararray(char[] chars,byte[] bytes) {
    if (chars != null) {
        for (int i = 0; i < chars.length; i++) {
            chars[i] = '\0';
        }

    }
    if (bytes != null) {
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = 0;
        }

    }

}

getAESKey:

protected SecretKey getAESKeyFromPassword(char[] password,byte[] salt)
        throws NoSuchAlgorithmException,InvalidKeySpecException {

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");

    KeySpec spec = new PBEKeySpec(password,salt,65536,256);
    SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(),"AES");

    return secret;

}

最终编辑:

最好的解决方案是将 frim PBKDF2 切换为 argon2。 https://github.com/kosprov/jargon2-api Argon2 允许使用原始哈希,然后您可以将该字节数组存储在上述 SecureKeySpec 中,因为它允许销毁 Spec,并清除原始哈希数组。

解决方法

实际上,对此没有简单的解决方案。问题在于 destroy 方法是一种“可选”方法。并非 SecretKey 的所有实现都实现了它。如果您使用的是未实现该方法的 SecretKey 类型,则会出现此异常并且没有简单的解决方案。

不幸的是,您不能自己实现该方法,因为(通常)它所属的类是由 Java SE 库提供的。

即使你搞清楚了如何销毁密钥,也存在包含密码1String的问题。 (这个问题更多是一个安全风险,因为搜索包含密码的 String 可能比搜索未知字节序列更容易。)

选项:

  1. 忘记这个问题。不要破坏他们在内存中的密钥/密码。 (有关为什么这并不像听起来那么糟糕的解释,请参见下文。)

  2. 寻找替代 JSSE 加密库,其中 AES 密钥的 SecretKey 实现确实实现了 destroy。我猜测充气城堡图书馆可能会。 (如果他们不这样做,您始终可以选择下载源代码并修补它们。)

  3. 讨厌的反射。您可以确定哪个实际类实现了密钥,并查看其代码以了解它如何在内部表示密钥。然后,您可以使用反射来打破抽象并访问其私有状态,并在密钥上写入零。


为什么不破坏密钥不是灾难?

所以一些安全专家可能不同意这一点,但我仍然认为这是一个有效的观点。

当您将内存中的密钥或密码归零时,您(表面上)是在防止以下类型的攻击:

  • 将 Java 调试器附加到 JVM 进程并使用它来定位和读取密钥。
  • 读取 JVM 进程内存。
  • 读取已写入磁盘的内存页。

这些攻击有多容易?那么前两个要求黑客已经进入主机并升级到(可能)root权限。在第三种情况下,您可以这样做,但黑客也可以窃取写入交换页的硬盘。

在所有情况下,黑客都必须找到密钥。与(比如说)C/C++ 程序不同,密钥不会存储在固定位置。相反,黑客必须通过模式匹配或通过查找引用链来找到它。 (Java 调试器会更容易,前提是键对象仍然可以访问。)另一方面,一旦键被垃圾回收,内存中的副本将消失,而交换中的副本将进入下一个操作系统写出关键对象曾经存在的(现在)脏页的时间。在那之后……出于所有实际目的,它“消失了”。

所以倒退一点。我说为了进行这种攻击,黑客已经需要root权限。 (或硬盘驱动器,这很可能相当于同一件事。)现在,如果他们拥有它,他们就可以通过其他方式窃取密钥。例如:

  • 使用调试器在(比如说)destroy 方法上设置断点,并在它被销毁之前获取密钥。

  • 在创建密钥之前使用调试器捕获密码。

  • 窃取服务器 SSL 证书(或其他)的私钥,以便他们可以从网络流量中获取密码。

  • 安装软件击键记录器。

  • 将您的应用程序代码替换为通过某些侧通道泄露密钥或密码的版本。

当然,他们可以安装后门等。简而言之,如果黑客已经将系统破坏到了对 JVM 进行“内存中窃取”攻击所需的程度,那么这可能是您最不想要的担心。

现在,安全专家可能会说对黑客进行分层防御是“最佳实践”。这是有一定道理的。但是,如果安全性对您来说很重要,您应该进行适当的安全性分析(不仅仅是“勾选框”审计)并找出真正的风险是什么。这将(可能2)告诉您,最好专注于确保系统安全,而不是担心有人(具有 root 权限)是否可以从内存中窃取密钥。


1 - 虽然不是你的情况,因为我看到你正在使用 char[] ... 可以清除。除了这仍然容易受到我谈到的所有其他攻击。
2 - 或者可能不会。但是你需要做分析!

,

您必须自己实现 destroy 方法。文档对此进行了解释。

https://docs.oracle.com/javase/8/docs/api/javax/crypto/SecretKey.html

,

我可能找到了解决方案,我尝试使用:https://github.com/dbsystel/SecureSecretKeySpec

唯一的问题是键必须是一个字节数组,并且这样做:

protected SecureSecretKeySpec getAESKeyFromPassword(char[] password,byte[] salt)
        throws NoSuchAlgorithmException,InvalidKeySpecException {

    SecretKeyFactory factory= SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");

    KeySpec spec = new PBEKeySpec(password,salt,65536,256);

    byte[] temp = factory.generateSecret(spec).getEncoded();

    SecureSecretKeySpec sec= new SecureSecretKeySpec(temp,"AES");

    Main.clearArray(null,temp);

    return sec;

}

可能不太好,因为在 SecretKey 上调用了 getEncoded,所以内存中可能有一个 SecretKey?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。