为什么使用Android和JavaScript在AES / CBC / ZeroBytePadding中加密会为某些字符串返回不同的结果?

时间:2015-04-14 11:32:51

标签: javascript android encryption aes

这是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代码返回十六进制:

c88928b4542a8faa774f27670c563fdfcb8dcc8ae554d3dd05a86ac7869d91d36a170a4e093b75e2702e8374f206e7f4

Android代码返回十六进制:

C88928B4542A8FAA774F27670C563FDFCB8DCC8AE554D3DD05A86AC7869D91D36A170A4E093B75E2702E8374F206E7F4DF33A2FBEE7FD2DBC1ED0430F3E45DCF

这两个十六进制相似但在android代码广告中最后有更多字符,加密其他字符串如: {"login":[{"username":"25697","passwd":"asd"}]} 要么 {"login":[{"username":"25697","passwd":"asdfg"}]}返回相同的十六进制。我无法在Android代码中找到错误原因。

1 个答案:

答案 0 :(得分:1)

问题是基于BouncyCastle的Android的ZeroBytePadding将始终添加填充,但CryptoJS不会。

AES是块密码,因此仅在16字节的完整块上运行。 CryptoJS的ZeroPadding将只添加这么多0x00字节,直到达到块大小的倍数。另一方面,Android的ZerBytePadding也会这样做,但如果明文已经是块大小的倍数,那么它将添加一个完整的0x00字节块。

在您的情况下,第一个明文是48个字节,这意味着ZeroBytePadding将添加一个额外的块。

Android的密码基于BouncyCastle,它是discussed before,为什么BouncyCastle会这样做。它不会被“修复”。

我建议您使用PKCS#5 / PKCS#7填充(它们是相同的),这使您可以在明文末尾使用0x00字节,这是ZeroPadding无法实现的。


不要忘记您应该通过使用消息验证代码来防止操纵您的密文。例如,HMAC-SHA256使用不同密钥的密文。