【问题标题】:Destroying SecretKey throws DestroyFailedException?销毁 SecretKey 抛出 DestroyFailedException?
【发布时间】:2021-02-20 11:09:54
【问题描述】:

我正在编写一个函数来使用 SecretKeyFactory 基于密码生成密钥(字节)。我想在不再需要时销毁 SecretKey 实例,但它会引发异常。

try {
    byte[] salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

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

    PBEKeySpec keySpec = new PBEKeySpec("password".toCharArray(), salt, 1000, 256);

    SecretKey secretkey = factory.generateSecret(keySpec);
    byte[] key = secretkey.getEncoded();

    // Using key

    // Destroy key
    Arrays.fill(key, (byte)0);

    // Destroy secretKey
    secretkey.destroy();  // --> Throw DestroyFailedException

} catch (Exception e) {
    e.printStackTrace();
}

我在 Mac 上使用 Oracle JDK1.8.0_66。

我看了一下SecretKey源代码,发现了这个默认实现(SecretKey实现了Destroyable接口)

public default void destroy() throws DestroyFailedException {
    throw new DestroyFailedException();
}

意思是:SecretKey的实现没有重写destroy方法来销毁内部密码字符和内部密钥字节。

这是 JDK 8 中的错误吗?

【问题讨论】:

  • 根据 Java 8 API 的 SecretKey its only two known implementing classes are KerberosKey and SecretKeySpec。在这两个中,只有 KerberosKey 覆盖了 destroy()。密钥的文档还说,任何扩展类都应该覆盖 Destroyable 提供的 destroy() 方法,所以这看起来不像是错误,而是预期的行为。您知道您的 generateSecret 调用返回的实际类吗?
  • @BrandonDockery:实现是 com.sun.crypto.provider.PBKDF2KeyImpl。该类实现了继承自 SercretKey 接口的 PBEKey 接口。
  • 请记住,您还应该调用 keySpec.clearPassword()(尽管这并不能解决缺少清除 SecretKey 的问题)
  • @BrandonDockery 后期评论:我认为这是一个 API 错误,因为您目前无法编写代码来调用 destroy,而且似乎没有任何方法可以测试对象是否可销毁。也许他们应该创建一个方法tryDestroy(): DestructionResult

标签: java java-8


【解决方案1】:

你是对的。 PBKDF2KeyImpl 类没有实现从 Destroyable 继承的 destroy 方法。 It also looks like you are not the first person to be concerned by this

这不一定是 JDK 中的错误,因为SecretKey 的 API 明确地将其留给实现类来定义此行为,尽管此行为未被覆盖似乎有点奇怪。

【讨论】:

  • 没问题,但是如果这回答了您的问题,您可以将其标记为答案吗?谢谢!
【解决方案2】:

这对我来说看起来很糟糕。我也在寻找销毁 SecretKey 的方法。您可以使用反射。我知道这很 hacky,但它允许我们获取底层的私钥数组:

public class DestroyableSecretKeySpec extends SecretKeySpec {
    public DestroyableSecretKeySpec( byte[] key, int offset, int len, String algorithm ) {
        super( key, offset, len, algorithm );
    }

    @Override
    public void destroy() {
        try {
            // Use hacky reflection to clear the underlying private key from memory.
            // This is so ugly :/
            Field f = SecretKeySpec.class.getDeclaredField( "key" );
            f.setAccessible(true);
            byte[] key = (byte[]) f.get( this );
            Arrays.fill( key, (byte)0xCC );
        } catch( NoSuchFieldException | IllegalAccessException e ) {
            throw new EncryptionException( "Can't use reflection to clear a secret key.", e );
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-06
    • 2021-04-18
    • 2011-01-25
    • 2018-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多