我在Java中进行简单的AES加密:
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, getAES128SecretKey());
byte[] encrypted = cipher.doFinal(input);
输出转换为十六进制并存储在数据库中。
后来的进程(用C / C ++编写的进程)出现并读取十六进制,将其转换为字节并对其进行解密。
问题显然是C实现正确地解密了文本,但也在最后保留了不必要的额外字节。
例如(不是真实值):
Java: encrypt("eric") -> 1234567890FFFFFF1234567890FFFFFF (hex)
Java: decrypt("1234567890FFFFFF1234567890FFFFFF") -> "eric"
C: decrypt("1234567890FFFFFF1234567890FFFFFF") -> "eric XXXX XXXX XXXX"
我不拥有C解密算法,并且使用它的一方建议我在加密之前将空终结符字符'\0'
附加到Java字节。我的问题是,这是否有效,我是否应该接受这个想法?
对问题Padding error when using AES encryption in Java and decryption in c读取第一个答案(虽然“不接受”,对我来说听起来是对的),填充大小的显式编码似乎是正确的解决方法。
但是,如果字符串在C中加密并在C中解密怎么办?在C中解密时,C加密字符串是否会出现问题,并且删除填充就好像它是Java加密字符串一样?或者这不是问题?
答案 0 :(得分:1)
由于AES是分组密码,因此应将加密的数据填充为16字节。 由于C代码不处理填充,可能会在加密时报告错误,或者添加“默认”填充。 更好,自己添加填充。
答案 1 :(得分:1)
加密/解密过程在很大程度上是无关紧要的。毕竟,进来的内容应该与出来的内容相同。问题是当写入文件时,表示String对象的字节是什么样的。
快速测试确认当java将字符串写入流时,字符串和流都不会以空值终止。请考虑以下代码:
import java.io.FileWriter;
import java.io.IOException;
public SimpleStringIoTest
{
public static void main (String[] args) throws IOException
{
String message = "Raw Java String";
FileWriter fileWriter = new FileWriter("javaoutput.bin");
fileWriter.write(message);
fileWriter.close();
}
}
执行产生以下内容的文件的hexdump。
00000000 52 61 77 20 41 61 76 61 20 53 74 72 69 6e 67 Raw Java String
因此,当您加密时,最终会得到15个字节的加密字符串和blocksize - 15个字节的加密填充。解密时,不仅会得到字符串,还会得到纯文本填充,除非您知道纯文本的长度或者有嵌入的纯文本终止符,否则无法知道纯文本的结束位置。
使用NUL('\ 0')作为终结符是有意义的,因为这也是C用来标记字符串结尾的终结符。可能的问题是,如果NUL已经以某种方式出现在你的字符串中。鉴于这可能会导致C / C ++程序的其他问题,我有点怀疑,但如果它是一个问题,你总是可以逃避它。
哦,我检查了如下更改messages
:
String message = "Raw Java String\0";
提供
的十六进制转储00000000 52 61 77 20 41 61 76 61 20 53 74 72 69 6e 67 00 Raw Java String
答案 2 :(得分:1)
String问题和NULL填充是一种分心,可能无关紧要。 Java使用标准填充方案(PKCS#5)明确地允许解密器知道需要丢弃最后一个块的字节数。 C方面也使用了一些方案。你需要知道那个方案是什么。在你知道C解密器所期望的填充算法之前,我会避免尝试使用'\ 0'进行填充。
您还应该在Cipher.getInstance()
方法中明确请求填充和模式,而不是依赖于默认值。例如,如果您使用Sun JCE提供程序,那么您的用法相当于Cipher.getInstance("AES/CBC/PKCS5PADDING")
,这是一个很好的选择。