我想用Java解密AES加密消息。我一直在尝试standard library和BouncyCastle中的各种算法/模式/填充选项。没有运气: - (
加密实体是用Python编写的,现在已经投入生产。加密的消息已经消失,所以我不能轻易改变那部分。 Python代码如下所示:
from Crypto.Cipher import AES
import base64
import os
import sys
BLOCK_SIZE = 16
PADDING = '\f'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
secret = 'XXXXXXXXXXXXXXXX'
cipher = AES.new(secret)
clear='test'
encoded = EncodeAES(cipher, clear)
print 'Encrypted string:>>{}<<'.format(encoded)
decoded = DecodeAES(cipher, encoded)
print 'Decrypted string:>>{}<<'.format(decoded)
显然使用了AES,我发现我必须使用ECB模式。但我还没有找到适用于Java端的填充模式。如果输入符合块大小并且没有填充,我可以用Java解密消息。如果需要填充消息,则解密失败。
要解密的Java代码如下所示:
public class AESPaddingTest {
private enum Mode {
CBC, ECB, CFB, OFB, PCBC
};
private enum Padding {
NoPadding, PKCS5Padding, PKCS7Padding, ISO10126d2Padding, X932Padding, ISO7816d4Padding, ZeroBytePadding
}
private static final String ALGORITHM = "AES";
private static final byte[] keyValue = new byte[] { 'X', 'X', 'X', 'X',
'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X' };
@BeforeClass
public static void configBouncy() {
Security.addProvider(new BouncyCastleProvider());
}
@Test
public void testECBPKCS5Padding() throws Exception {
decrypt("bEpi03epVkSBTFaXlNiHhw==", Mode.ECB,
Padding.PKCS5Padding);
}
private String decrypt(String valueToDec, Mode modeOption,
Padding paddingOption) throws GeneralSecurityException {
Key key = new SecretKeySpec(keyValue, ALGORITHM);
Cipher c = Cipher.getInstance(ALGORITHM + "/" + modeOption.name() + "/" + paddingOption.name());
c.init(Cipher.DECRYPT_MODE, key);
byte[] decValue = c.doFinal(valueToDec.getBytes());
String clear = new String(Base64.encodeBase64(decValue));
return clear;
}
}
抛出的错误是:
javax.crypto.IllegalBlockSizeException:输入长度必须是16的倍数 用填充密码解密
有什么想法吗?
答案 0 :(得分:2)
感谢好的问题,答案和评论。 我对代码做了一些小改动,现在它对我来说就像一个魅力。
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import android.util.Base64;
public class AESTest {
public enum Mode {
CBC, ECB, CFB, OFB, PCBC
};
public enum Padding {
NoPadding, PKCS5Padding, PKCS7Padding, ISO10126d2Padding, X932Padding, ISO7816d4Padding, ZeroBytePadding
}
private static final String ALGORITHM = "AES";
private static final byte[] keyValue ="myKey".getBytes();
String decrypt(String valueToDec, Mode modeOption,
Padding paddingOption) throws GeneralSecurityException {
byte[] decodeBase64 = Base64.decode(valueToDec.getBytes(),0);
Key key = new SecretKeySpec(keyValue, ALGORITHM);
Cipher c = Cipher.getInstance("AES/ECB/NoPadding");
c.init(Cipher.DECRYPT_MODE, key);
byte[] encValue = c.doFinal(decodeBase64);
return new String(encValue).trim();
}
}
然后,我在Android Activity中使用了以下类:
AESTest aes=new AESTest();
String decrypted = aes.decrypt(myCipheredText,Mode.ECB,Padding.NoPadding);
请注意,python代码中的秘密和java代码中的mykey是相同的。
答案 1 :(得分:1)
您使用换页符(\f
)进行填充。我不知道这样做的标准填充方案。因此,我建议您在Java端选择NoPadding
,并准备从解密后获得的明文中删除\f
个字符。
由于你能够解密非填充的纯文本,然后它会证明你在两边都有相同的密钥材料(这是一个常见的问题,我很高兴我们可以从列表中删除)。
阅读the Python documentation,默认选择ECB模式。因此,请确保在Java端使用它。