在Java中加密字符串以便C程序可以解密它,而在解密文本中没有任何填充?

时间:2010-08-23 15:40:00

标签: java c encryption aes

我在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加密字符串一样?或者这不是问题?

3 个答案:

答案 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"),这是一个很好的选择。