在Android上解密“SunJCE”AES加密数据

时间:2015-12-21 19:26:38

标签: java android encryption cryptography aes

我们需要编写一些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类做到这一点?

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:0)

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

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

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

即使我使用这个&#34;零填充&#34;有点不安,这是我写的一个实用程序来删除它:

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");
}

答案 2 :(得分:-1)

如果您在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