代码之家  ›  专栏  ›  技术社区  ›  Eric

在Java中加密一个字符串,以便C程序可以在解密文本中不加任何填充地解密它。

  •  3
  • Eric  · 技术社区  · 15 年前

    我在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 回复  |  直到 14 年前
        1
  •  1
  •   torak    15 年前

    加密/解密过程在很大程度上是不相关的。毕竟,进来的应该和出来的一样。问题是,当写入文件时,表示字符串对象的字节是什么样子的。

    快速测试确认当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();
      }
    }
    

    对生成以下内容的文件执行十六进制转储。

    00000000  52 61 77 20 41 61 76 61 20 53 74 72 69 6e 67     Raw Java String
    

    因此,当您加密时,最终得到一个15字节的加密字符串和块大小-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
  •   Nickolay Olshevsky    15 年前

    因为aes是块密码,所以应该将要加密的数据填充到16个字节。 由于C代码不处理填充,它可能会在加密时报告错误,或者添加“默认”填充。 最好自己加衬垫。

        3
  •  1
  •   President James K. Polk    15 年前

    字符串问题和空填充会分散注意力,并且可能不相关。Java使用标准的填充方案(PKCSα5)。 明确地 允许解密程序知道需要丢弃最后一个块的字节数。C端也使用了一些方案。你需要知道那个计划是什么。在您知道C解密程序所期望的填充算法之前,我将避免使用'\0'进行填充之类的操作。

    您还应该在 Cipher.getInstance() 方法而不是依赖默认值。例如,您的用法相当于 Cipher.getInstance("AES/CBC/PKCS5PADDING") 如果您使用的是Sun JCE提供者,这是一个很好的选择。

    推荐文章