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