为什么在 AES/CBC/ZeroBytePadding 中使用 Android 和 JavaScript returns 加密某些字符串会产生不同的结果?
Why encrypting in AES/CBC/ZeroBytePadding with Android and JavaScript returns different results for some strings?
这是JavaScript中的代码:
<html>
<head>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/pad-zeropadding-min.js"></script>
<!-- jquery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<div id="0"></div>
<div id="1"></div>
<div id="2"></div>
<script>
var key = CryptoJS.enc.Utf8.parse('32_length_String');
var iv = CryptoJS.enc.Utf8.parse('32_length_String');
var message = '{"login":[{"username":"25697","passwd":"asdf"}]}';
$('#0').text("Message: "+message);
var encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv, padding: CryptoJS.pad.ZeroPadding, mode: CryptoJS.mode.CBC});
$('#1').text("Encrypted BASE64: "+encrypted);
$('#2').text("Encrypted HEX: "+encrypted.ciphertext);
</script>
</body>
</html>
这是 android 代码:
private static final String ZERO_PADDING_KEY = "32_length_String";
private static final String IV = "32_length_String";
private static final String CIPHER_ALGORITHM = "AES/CBC/ZeroBytePadding";
public static String encryptAESURL(String url) {
try {
byte[] key = ZERO_PADDING_KEY.getBytes("UTF-8");
SecretKeySpec sks = new SecretKeySpec(key, "AES");
byte[] iv = Arrays.copyOf(IV.getBytes("UTF-8"), 16);
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, sks, ivspec);
byte[] encryptedText = cipher.doFinal(url.getBytes("UTF-8"));
String resul = bytesToHex(encryptedText);
url = resul;
return resul;
} catch (Exception e) {
Log.e("ENCRYPT ERROR", e.getMessage());
e.printStackTrace();
// throw new CryptoException("Unable to decrypt", e);
}
return url;
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
问题在于使用字符串:{"login":[{"username":"25697","passwd":"asdf"}]}
JavaScript代码returns十六进制:
c88928b4542a8faa774f27670c563fdfcb8dcc8ae554d3dd05a86ac7869d91d36a170a4e093b75e2702e8374f206e7f4
Android编码returns十六进制:
C88928B4542A8FAA774F27670C563FDFCB8DCC8AE554D3DD05A86AC7869D91D36A170A4E093B75E2702E8374F206E7F4DF33A2FBEE7FD2DBC1ED0430F3E45DCF
这两个十六进制相似,但在 android 代码中,末尾添加了更多字符,并加密了其他字符串,例如:
{"login":[{"username":"25697","passwd":"asd"}]}
要么
{"login":[{"username":"25697","passwd":"asdfg"}]}
returns 一样的十六进制。我找不到 Android 代码中的错误原因。
问题是Android基于BouncyCastle的ZeroBytePadding总是会添加填充,但CryptoJS不会。
AES 是一种块密码,因此仅对 16 字节的完整块进行操作。 CryptoJS 的 ZeroPadding 只会添加这么多的 0x00 字节,直到达到块大小的倍数。 Android 的 ZerBytePadding 另一方面会做同样的事情,但如果明文已经是块大小的倍数,那么它将添加一个完整的 0x00 字节块。
在你的例子中,第一个明文是 48 个字节,这意味着 ZeroBytePadding 将添加一个额外的块。
Android的密码是基于BouncyCastle的,原来是discussed before,为什么BouncyCastle会这样。不会是 "fixed".
我建议您使用 PKCS#5/PKCS#7 填充(它们相同),这样您就可以在明文末尾使用 have 0x00 字节,这在 ZeroPadding 中是不可能的。
不要忘记您应该通过使用消息验证码来防止您的密文被操纵。例如,在具有不同密钥的密文上使用 HMAC-SHA256。
这是JavaScript中的代码:
<html>
<head>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/pad-zeropadding-min.js"></script>
<!-- jquery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<div id="0"></div>
<div id="1"></div>
<div id="2"></div>
<script>
var key = CryptoJS.enc.Utf8.parse('32_length_String');
var iv = CryptoJS.enc.Utf8.parse('32_length_String');
var message = '{"login":[{"username":"25697","passwd":"asdf"}]}';
$('#0').text("Message: "+message);
var encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv, padding: CryptoJS.pad.ZeroPadding, mode: CryptoJS.mode.CBC});
$('#1').text("Encrypted BASE64: "+encrypted);
$('#2').text("Encrypted HEX: "+encrypted.ciphertext);
</script>
</body>
</html>
这是 android 代码:
private static final String ZERO_PADDING_KEY = "32_length_String";
private static final String IV = "32_length_String";
private static final String CIPHER_ALGORITHM = "AES/CBC/ZeroBytePadding";
public static String encryptAESURL(String url) {
try {
byte[] key = ZERO_PADDING_KEY.getBytes("UTF-8");
SecretKeySpec sks = new SecretKeySpec(key, "AES");
byte[] iv = Arrays.copyOf(IV.getBytes("UTF-8"), 16);
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// encryption pass
cipher.init(Cipher.ENCRYPT_MODE, sks, ivspec);
byte[] encryptedText = cipher.doFinal(url.getBytes("UTF-8"));
String resul = bytesToHex(encryptedText);
url = resul;
return resul;
} catch (Exception e) {
Log.e("ENCRYPT ERROR", e.getMessage());
e.printStackTrace();
// throw new CryptoException("Unable to decrypt", e);
}
return url;
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
问题在于使用字符串:{"login":[{"username":"25697","passwd":"asdf"}]}
JavaScript代码returns十六进制:
c88928b4542a8faa774f27670c563fdfcb8dcc8ae554d3dd05a86ac7869d91d36a170a4e093b75e2702e8374f206e7f4
Android编码returns十六进制:
C88928B4542A8FAA774F27670C563FDFCB8DCC8AE554D3DD05A86AC7869D91D36A170A4E093B75E2702E8374F206E7F4DF33A2FBEE7FD2DBC1ED0430F3E45DCF
这两个十六进制相似,但在 android 代码中,末尾添加了更多字符,并加密了其他字符串,例如:
{"login":[{"username":"25697","passwd":"asd"}]}
要么
{"login":[{"username":"25697","passwd":"asdfg"}]}
returns 一样的十六进制。我找不到 Android 代码中的错误原因。
问题是Android基于BouncyCastle的ZeroBytePadding总是会添加填充,但CryptoJS不会。
AES 是一种块密码,因此仅对 16 字节的完整块进行操作。 CryptoJS 的 ZeroPadding 只会添加这么多的 0x00 字节,直到达到块大小的倍数。 Android 的 ZerBytePadding 另一方面会做同样的事情,但如果明文已经是块大小的倍数,那么它将添加一个完整的 0x00 字节块。
在你的例子中,第一个明文是 48 个字节,这意味着 ZeroBytePadding 将添加一个额外的块。
Android的密码是基于BouncyCastle的,原来是discussed before,为什么BouncyCastle会这样。不会是 "fixed".
我建议您使用 PKCS#5/PKCS#7 填充(它们相同),这样您就可以在明文末尾使用 have 0x00 字节,这在 ZeroPadding 中是不可能的。
不要忘记您应该通过使用消息验证码来防止您的密文被操纵。例如,在具有不同密钥的密文上使用 HMAC-SHA256。