【问题标题】:Simulating a stream cipher with AES/CTR使用 AES/CTR 模拟流密码
【发布时间】:2013-04-23 22:45:47
【问题描述】:

我正在编写一个应用程序服务器,我决定使用 AES128/CTR/NoPadding 来保护连接,因为它被认为足够安全,无需将字节扩展到块边界,而且我认为它非常适合到 TCP,它在逻辑上是一个无缝流。

问题是 Cipher.update() 直到它有一个完整的 16 字节块才返回加密块,因为 CTR 基本上基于块密码,虽然模拟了流密码。我应该从 tcp 套接字读取数据并在消息到达后立即处理它们,但我无法检索最新的块,因为它仍在构建中并且它的大小小于 16 字节。我不能等待,因为我们不知道下一条消息何时发送。当然,我可以调用 Cipher.doFinal() 来获取剩余部分,但这意味着流(连接)的结束,并且 Cipher 对象将被重新初始化。

我认为如果有办法查看结转情况会很好。 CTR 只是将纯文本与密钥流进行异或运算,因此无论块中的其余字节如何,我都应该能够获得加密数据。这个问题会有一个很好的解决方法吗?我正在考虑编写一个包装器,用零加密假纯文本以提前获取密钥流并手动进行异或,但我想知道其他人是如何解决这个问题的。

更新

我正在开发一个 Android 应用程序,结果发现这是 Dalvik VM 的问题。正如 Robert 和 monnand 在下面指出的,Java SE 至少在默认提供程序中没有这个问题。我想我必须编写一个包装类或将模式更改为 CFB8 来解决这个问题。 (CTR8 无效)感谢所有回复!

【问题讨论】:

  • 你可以填充消息,使其长度为 16 的倍数吗?这将确保您始终得到一个可解密的块。
  • @Chris:感谢您的建议,这可能是一种可能的解决方法。但如果可能的话,我想避免这种情况,因为它在网络效率方面并不可取,尽管它可能可以忽略不计。
  • 您可以简单地在一个由零组成的数组上调用它来获取密钥流。然后手动将其异或到您的消息中。
  • Java SE 没有这个问题,因为它根本不支持AES128/CTR/NoPadding,至少对于默认提供程序。我刚刚尝试过,Java 7 Oracle(但也许我的安装搞混了,我使用的是 OpenJDK,一切皆有可能)。

标签: java encryption cryptography aes bouncycastle


【解决方案1】:

我刚刚使用 Oracle Java 1.7 在 CTR 模式下测试了 AES,我无法验证您的观察结果:

Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[1]).length);  // output: 1
System.out.println(c.update(new byte[20]).length); // output: 20

您可能使用了有缺陷的第三方实现,因为“AES128/CTR/NoPadding”在我的系统上不是已知密码。

【讨论】:

  • 你是对的,这是 Android Dalvik VM 的问题,而不是 Java SE。
【解决方案2】:

我今天也遇到了同样的问题,刚刚解决了。

问题出在您的提供者上,可能是Bouncy Castle。当您致电getInstance() 时,只需提供您的算法的名称(在我的例子中是“AES/CTR/NoPadding”)。 请勿指定提供者。

让代码自己解释一下:

正如@Robert 所说,以下代码可以正常工作:

Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[1]).length);  // output: 1
System.out.println(c.update(new byte[20]).length); // output: 20

但是,如果您将提供者指定为“BC”,那就错了:

Cipher c = Cipher.getInstance("AES/CTR/NoPadding", "BC");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[20]).length); // output: 16
System.out.println(c.update(new byte[1]).length);  // null pointer exception

它可以被认为是Bouncy Castle的一个bug,或者是一种(奇怪但合理的)功能。

【讨论】:

  • 在我的情况下,Android Dalvik 是问题,但感谢您指出自定义提供程序也有同样的问题。
  • 感谢您让我知道 Android 也有同样的问题(功能?)。这很烦人,因为计数器模式的一个好处是它可以在没有填充的情况下使用。
  • 我知道这个问题已经存在了一段时间......这个问题仍然存在于现代 BC 资源中吗?
【解决方案3】:

我不必自己解决这个问题,但一种解决方法是手动生成密钥流并自己处理 XORing。

也就是说,您可以从AES/CTR/NoPadding 切换到AES/ECB/NoPadding 并重复加密您的incrementing counter value,只要您需要新数据与密文进行异或运算。

远非理想,但我想它会起作用。

【讨论】:

  • 感谢您的建议。我想我可以使用带有零数组的 CTR 来获取密钥流。
猜你喜欢
  • 1970-01-01
  • 2015-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-05
  • 1970-01-01
  • 2018-04-15
相关资源
最近更新 更多