【问题标题】:Decrypting "SunJCE" AES encrypted data on Android在 Android 上解密“SunJCE”AES 加密数据
【发布时间】:2015-12-21 19:26:38
【问题描述】:

我们需要编写一些 Android 代码来解密从我们的服务器发送的一些数据。我们的服务器团队为我们提供了一些使用“SunJCE”提供程序的示例解密代码,不幸的是,Android 上不存在该提供程序。

Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");

有人知道在 Android 上实现此功能的最简洁方法吗?如果我们在 Android 上试试这个

Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");

然后看起来在解密文本的末尾出现了一些不需要的垃圾,例如:

ComparisonFailure: expected:<...lAAAAABJRU5ErkJggg==[]> but was:<...lAAAAABJRU5ErkJggg==[��������]>    

我们在 Android Cipher 类中尝试了多种不同转换的组合(例如“AES/CBC/PKCS5Padding”),但不断遇到诸如 BadPaddingExceptions 之类的问题。

我们还能够使用纯 Java 模块解密这些数据,该模块似乎没有显示相同的垃圾字符。有没有办法只使用 Android 类来做到这一点?

【问题讨论】:

  • 你能打印出十六进制的明文值(使用 NoPadding)吗? Bouncy Castle 和 Apache Commons Codec(以及 Guava 等)具有十六进制编码器。
  • 你试过simple-java-aes-encrypt-decrypt-example的例子吗?
  • 我的情况与“简单的 java AES 加密/解密示例”问题有点不同,因为我们的 Android 客户端只是解密,而不是加密。我们可以在 Android 上很好地加密和解密(以及在测试做同样事情的纯 Java 独立项目时),但是在解密在 Java JVM 上加密的数据时,我们最终会在 Android 上得到垃圾。我相信最后的垃圾字节都是零。我的加密数据在 Java 字符串中定义,解密后的预期明文也是如此(取自示例 Java-only 项目)。

标签: java android encryption cryptography aes


【解决方案1】:

Java 代码中也存在相同的垃圾。只是您可能在使用默认拉丁 (ISO_8859_1) 字符集的 Windows 上运行它,而 Android 默认使用 UTF-8。它还取决于用于打印字符的控制台和字体。在这种情况下,所使用的填充可能不会在 Windows 控制台上打印,而是在 Android 代码上打印。

您需要查看字节数组(例如十六进制)以找出使用了哪个填充,然后在将明文转换为字符串之前将其剥离。

【讨论】:

    【解决方案2】:

    @Maarten Bodewes 是正确的:垃圾字符也出现在我们的纯 Java 模块中。

    问题是我们的服务器端代码在加密之前用零填充输入数据。这样输入与 AES 所需的块大小相匹配。

    这里有一个很好的讨论:Android - Removing padded bits in decryption

    尽管我对使用这个“零填充”有点不安,但我写了一个实用程序来删除它:

    public static String getStringAfterRemovingZeroPadding(byte[] input) {
        if (input == null) {
            return null;
        }
    
        int index = input.length - 1;
    
        while (index >= 0) {
            if (input[index] == 0) {
                // We found some zero padding, look at the next character and see if it's also zero
                // padding
                --index;
            } else {
                // This character is not a zero padding, so go back to the zero padding that we
                // just inspected, or go to the end of the string
                ++index;
                break;
            }
        }
    
        if (index < 0) {
            return "";
        }
    
        return new String(input, 0, index);
    }
    

    ...这是我的单元测试:

    @Test
    public void testRemoveZeroPaddingNull() throws Exception {
        String result = StringUtils.getStringAfterRemovingZeroPadding(null);
        assertThat(result).isNull();
    }
    
    @Test
    public void testRemoveZeroPaddingAllZeros() throws Exception {
        byte[] input = {0, 0};
        String result = StringUtils.getStringAfterRemovingZeroPadding(input);
        assertThat(result).isEqualTo("");
    }
    
    @Test
    public void testRemoveZeroPaddingNoZeros() throws Exception {
        byte[] input = {80, 80, 80};
        String result = StringUtils.getStringAfterRemovingZeroPadding(input);
        assertThat(result).isEqualTo("PPP");
    }
    
    @Test
    public void testRemoveZeroPaddingOneZero() throws Exception {
        byte[] input = {80, 80, 80, 0};
        String result = StringUtils.getStringAfterRemovingZeroPadding(input);
        assertThat(result).isEqualTo("PPP");
    }
    
    @Test
    public void testRemoveZeroPaddingSomeZeros() throws Exception {
        byte[] input = {80, 80, 80, 0, 0, 0, 0, 0};
        String result = StringUtils.getStringAfterRemovingZeroPadding(input);
        assertThat(result).isEqualTo("PPP");
    }
    

    【讨论】:

      【解决方案3】:

      如果您在 Android 上运行此代码,您会看到支持哪些密码:

      TreeSet<String> ciphers = new TreeSet<>();
      for (Provider provider : Security.getProviders())
          for (Service service : provider.getServices())
              if (service.getType().equals("Cipher"))
                  ciphers.add(service.getAlgorithm());
      for (String cipher : ciphers)
          System.out.println(cipher);
      

      在带有 JDK 1.8.0_51 的 Windows 7 上,我得到:

      AES
      AESWrap
      AESWrap_128
      AESWrap_192
      AESWrap_256
      AES_128/CBC/NoPadding
      AES_128/CFB/NoPadding
      AES_128/ECB/NoPadding
      AES_128/GCM/NoPadding
      AES_128/OFB/NoPadding
      AES_192/CBC/NoPadding
      AES_192/CFB/NoPadding
      AES_192/ECB/NoPadding
      AES_192/GCM/NoPadding
      AES_192/OFB/NoPadding
      AES_256/CBC/NoPadding
      AES_256/CFB/NoPadding
      AES_256/ECB/NoPadding
      AES_256/GCM/NoPadding
      AES_256/OFB/NoPadding
      ARCFOUR
      Blowfish
      DES
      DESede
      DESedeWrap
      PBEWithHmacSHA1AndAES_128
      PBEWithHmacSHA1AndAES_256
      PBEWithHmacSHA224AndAES_128
      PBEWithHmacSHA224AndAES_256
      PBEWithHmacSHA256AndAES_128
      PBEWithHmacSHA256AndAES_256
      PBEWithHmacSHA384AndAES_128
      PBEWithHmacSHA384AndAES_256
      PBEWithHmacSHA512AndAES_128
      PBEWithHmacSHA512AndAES_256
      PBEWithMD5AndDES
      PBEWithMD5AndTripleDES
      PBEWithSHA1AndDESede
      PBEWithSHA1AndRC2_128
      PBEWithSHA1AndRC2_40
      PBEWithSHA1AndRC4_128
      PBEWithSHA1AndRC4_40
      RC2
      RSA
      RSA/ECB/PKCS1Padding
      

      【讨论】:

        猜你喜欢
        • 2016-09-22
        • 2013-12-07
        • 2011-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-25
        • 2012-04-08
        • 2012-02-24
        相关资源
        最近更新 更多