首页 > 解决方案 > 如何使用 javacardx.security.derivation 定义的 KDF (X9.63) 提取加密和 MAC 密钥

问题描述

根据 Java Card v3.1 定义了新包 javacardx.security.derivation

https://docs.oracle.com/en/java/javacard/3.1/jc_api_srvc/api_classic/javacardx/security/derivation/package-summary.html

KDF X9.63 适用于三个输入:输入密码、计数器和共享信息。根据生成的密钥材料的长度,对哈希进行多轮以生成最终输出。

我正在通过 JC API 使用此 KDF 为 16 字节加密密钥、16 字节 IV 和 32 字节 MAC 密钥生成 64 字节输出(由 2 轮 SHA-256 执行)。

注意:这只是用必要的细节提出我的问题的伪代码。

DerivationFunction df = DerivationFunction.getInstance(DerivationFunction.ALG_KDF_ANSI_X9_63, false);
df.init(KDFAnsiX963Spec(MessageDigest.ALG_SHA_256, input, sharedInfo, (short) 64);

SecretKey encKey = KeyBuilder.buildKey(KeyBuilder.TYPE_AES, (short)16, false);
SecretKey macKey = KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short)32, false);

df.nextBytes(encKey); 
df.nextBytes(IVBuffer, (short)0, (short)16);
df.lastBytes(macKey);

我有以下问题:

  1. 何时执行几轮 KDF?这些是在 df.init() 期间还是在df.nextBytes()&期间执行的df.lastBytes()

  2. 一轮 KDF 将生成 32 字节输出(考虑 SHA-256 算法),那么 API 的df.nextBytes()&df.lastBytes()将如何与任何输出预期长度 < 32 字节一起工作?

  3. 在这个 KDF 计数器中,每下一轮都会递增,那么如何在df.nextBytes()& df.lastBytes()API 之间管理计数器?

标签: cryptographyjavacard

解决方案


  1. 何时执行几轮 KDF?这些是在 df.init() 期间还是在df.nextBytes()&期间执行的df.lastBytes()

这似乎是特定于我的实现。一次执行所有计算可能会更快,但在这种情况下,等待字节的第一个请求仍然有意义。另一方面,RAM 也经常是一个问题,因此按需生成也有一定意义。不过,这需要一些更棘手的实现。

预先指定输出大小的事实可能表明 API 设计人员至少预见到了一次生成所有关键材料的更简单方法(他们可能在 JCF 中对其进行同行评审之前创建了一个实现)。

  1. 一轮 KDF 将生成 32 字节的输出(考虑 SHA-256 算法),那么 API 的df.nextBytes()&df.lastBytes()将如何与任何输出预期长度 < 32 字节一起工作?

它通常会返回(散列输出的)最左边的字节,并可能将其余字节留在缓冲区中。当被调用时,这个缓冲区可能会与状态的其余部分一起被销毁lastBytes(所以不要忘记这样做)。

DerivationFunction请注意,API 明确指出,如果您想再次使用该实例,则必须重新初始化它。所以这是一个非常强烈的迹象,表明他们考虑破坏关键材料(FIPS 和通用标准认证所要求的东西,而不仅仅是常识)。

其他 KDF可能有不同的返回字节的方式,但是使用最左边的字节然后在右边添加轮次是很常见的,你可以称之为通用。对于 ANSI X9.63 KDF,情况确实如此,并且在标准中明确规定了这种方式。

  1. 在这个 KDF 计数器中,每下一轮都会递增,那么如何在df.nextBytes()& df.lastBytes()API 之间管理计数器?

这些是同一类的方法,不能单独查看,因此它们不是单独的 API。类实例可以以任何他们想要的方式保持状态。它可能只是将计数器作为类变量保存,但如果它决定在init或第一次nextBytes/lastBytes调用期间生成字节,则甚至不再需要计数器。


推荐阅读