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

标签 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>

这是安卓代码:

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 代码中的问题,为什么会发生这种情况。

最佳答案

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

AES 是一种 block 密码,因此仅对 16 字节的完整 block 进行操作。 CryptoJS 的 ZeroPadding 只会添加这么多的 0x00 字节,直到达到 block 大小的倍数。另一方面,Android 的 ZerBytePadding 会做同样的事情,但如果明文已经是 block 大小的倍数,那么它将添加一个完整的 0x00 字节 block 。

在您的例子中,第一个明文是 48 个字节,这意味着 ZeroBytePadding 将添加一个额外的 block 。

Android的密码是基于BouncyCaSTLe的,是discussed before ,为什么BouncyCaSTLe会这样做。它不会是“固定的”。

我建议您使用 PKCS#5/PKCS#7 填充(它们相同),这使您能够在明文末尾使用 0x00 字节,这在 ZeroPadding 中是不可能的。


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

关于javascript - 为什么使用 Android 和 JavaScript 在 AES/CBC/ZeroBytePadding 中加密会为某些字符串返回不同的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29626232/

相关文章:

javascript - 从使用 React 和 Webpack 构建的网站的 url 中删除 .html 扩展名

javascript - 在页面加载时需要在特定字段上设置光标并自动打开键盘而不触摸文本字段

javascript - Google Apps 脚本 HTML 服务 : Scroll to top of window

android - 如何在应用程序中获取android系统日期和时间格式

asp.net - AES 加密和 key 存储?

performance - 如何加速java卡加密/解密(RSA)

php - 在 PHP 中加密和解密密码、文件的完美方法?

javascript - 动画网格无法在 Babylon.js 中加载

android - 如何确定android中的设备启动事件

java - 您是否需要自定义适配器来将不同的 subview 添加到 ListView