2015-12-21 25 views
1

我們需要編寫一些Android代碼來解密從我們的服務器發送的一些數據。我們的服務器團隊向我們提供了一些使用「SunJCE」提供程序的解密代碼示例,但不幸的是,這些代碼在Android上不存在。在Android上解密「SunJCE」AES加密數據

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

有沒有人知道在Android上實現這個最簡潔的方法?如果我們嘗試這在Android

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

那麼它看起來像一些不需要的垃圾出現在解密文本的結尾,例如:

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

我們嘗試了很多不同的變換組合,在Android密碼類(例如「AES/CBC/PKCS5Padding」),但不斷地擊中BadPaddingExceptions之類的東西。

我們還能夠使用純Java模塊解密此數據,該模塊似乎沒有顯示相同的垃圾字符。有沒有辦法使用Android類來做到這一點?

+0

你能打印出十六進制的純文本值(使用NoPadding)嗎? Bouncy Castle和Apache Commons Codec(以及Guava等)都有一個十六進制編碼器。 –

+0

[Simple java AES encryption/decrypt example]可能的重複(http://stackoverflow.com/questions/15554296/simple-java-aes-encrypt-decrypt-example) – JFPicard

+0

您是否試過這個例子[simple-java-aes -encrypt - 解密 - 示例](http://stackoverflow.com/questions/15554296/simple-java-aes-encrypt-decrypt-example)? – caot

回答

3

同樣的垃圾也存在於Java代碼中。只是你可能在Windows上運行這個默認的拉丁語(ISO_8859_1)字符集,並且Android默認使用UTF-8。它還取決於用於打印字符的控制檯和字體。在這種情況下,使用的填充可能不會在Windows控制檯上打印,而會在Android代碼上打印。

您需要查看字節數組(例如,以十六進制)來查明使用了哪個填充,然後在將明文轉換爲字符串之前將其剝離。

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

在Windows 7上使用JDK 1.8.0_51,我得到:

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 
0

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