我正在编写一个应用程序服务器,我决定使用AES128 / CTR / NoPadding来保护连接,因为它被认为是足够安全的,而不必将字节扩展到块边界,我认为它非常适合到TCP,这在逻辑上是一个无缝流。
问题是Cipher.update()不会返回加密块,直到它有一个完整的16字节块,因为CTR基本上是基于块密码,虽然模拟流密码。我应该从tcp套接字读取数据并在消息到达时立即处理消息,但是我无法检索最新的块,因为它仍在构建并且其大小小于16个字节。我不能等待,因为我们不知道下一条消息何时发送。当然我可以调用Cipher.doFinal()来获取剩余的但这意味着流的结束(连接)和Cipher对象将被重新初始化。
如果有办法偷看结转,我觉得这很好。 CTR只是简单地使用密钥流对纯文本进行异或,因此无论块中的其余字节如何,我都应该能够获得加密数据。这个问题会有一个很好的解决方法吗?我正在考虑编写一个包装器,用零加密假纯文本以提前获取密钥流并手动进行异或,但我不知道其他人是如何解决这个问题的。
更新
我正在开发一个Android应用程序,结果证明这是Dalvik VM的问题。正如Robert和monnand在下面指出的那样,Java SE至少在默认提供程序中没有这个问题。我想我必须编写一个包装类或将模式更改为CFB8来解决这个问题。 (CTR8无效)感谢所有回复!
答案 0 :(得分:5)
我刚刚使用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”不是我系统上的已知密码。
答案 1 :(得分:4)
我今天遇到了完全相同的问题,并且刚刚解决了这个问题。
问题是您的提供商,可能是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,或者是一种(奇怪但合理的)功能。
答案 2 :(得分:0)
我自己没有必要解决这个问题,但一种解决方法是手动生成密钥流并自己处理XOR。
也就是说,只要您需要使用密文进行XOR的新数据,就可以从AES/CTR/NoPadding
切换到AES/ECB/NoPadding
并重复加密incrementing counter value。
远非理想,但我认为它会起作用。