4

通过使用 AES 算法进行字符串解密,我遇到了非常奇怪的问题。我的 C# 应用程序将加密数据(字符串)发送到 Java 应用程序。即使我使用相同的密钥字符串,解密也会导致异常:

javax.crypto.BadPaddingException:给定最终块在 com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:810) 处未正确填充

但是,仅当要加密的纯输入文本的长度(在 C# 端)超过 1393 个字符时...但如果长度等于 1393 个字符,或者小于 1393 个字符,则可以正常工作。

这是用于加密的 C# 代码:

    private static string Encrypt(string textToEncrypt, string key)
    {
        try
        {
            RijndaelManaged rijndaelCipher = new RijndaelManaged();
            rijndaelCipher.Mode = CipherMode.CBC;
            rijndaelCipher.Padding = PaddingMode.PKCS7;

            rijndaelCipher.KeySize = 0x80; // 256bit key
            rijndaelCipher.BlockSize = 0x80;
            byte[] pwdBytes = Encoding.UTF8.GetBytes(key);
            byte[] keyBytes = new byte[0x10];
            int len = pwdBytes.Length;
            if (len > keyBytes.Length)
            {
                len = keyBytes.Length;
            }
            Array.Copy(pwdBytes, keyBytes, len);
            rijndaelCipher.Key = keyBytes;
            rijndaelCipher.IV = keyBytes;
            ICryptoTransform transform = rijndaelCipher.CreateEncryptor();
            byte[] plainText = Encoding.UTF8.GetBytes(textToEncrypt);
            return Convert.ToBase64String(transform.TransformFinalBlock(plainText, 0, plainText.Length));
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

以及用于解密的 Java 代码:

public static String Decrypt(String text, String key) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] keyBytes = new byte[16];
    byte[] b = key.getBytes("UTF-8");
    int len = b.length;
    if (len > keyBytes.length) {
        len = keyBytes.length;
    }
    System.arraycopy(b, 0, keyBytes, 0, len);

    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

    BASE64Decoder decoder = new BASE64Decoder();
    byte[] results = cipher.doFinal(decoder.decodeBuffer(text));
    return new String(results, "UTF-8");
}

我尝试将 BASE64Decoder 从 apache 替换为 Base64 编解码器,但结果是一样的……我将不胜感激任何建议或想法。谢谢。

4

1 回答 1

1

如果您通过网络传输密文,您应该使用 消息验证码来确保密文的完整性。在尝试解密密文之前,应该验证 MAC。这将防止意外损坏和恶意篡改线路上的消息。在这种情况下,它应该可以帮助您确保您传输的密文与您接收的密文完全相同。

我重写了 java 解密端来进行 MAC 验证。它假定 MAC 是使用 HMACSHA1 算法生成的,该算法使用与 AES 密码相同的密钥材料初始化(注意:这只是一个示例,加密密钥和 HMAC 密钥在真实系统中应该不同)并添加到密文。消息的前 20 个字节应该是 MAC,紧随其后的是密文。

public static String Decrypt(String message, String key) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] keyBytes = new byte[16];
    byte[] b = key.getBytes("UTF-8");
    int len = b.length;
    if (len > keyBytes.length) {
        len = keyBytes.length;
    }
    System.arraycopy(b, 0, keyBytes, 0, len);

    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);

    byte[] messageBytes =  DatatypeConverter.parseBase64Binary(message);
    byte[] macBytes = new byte[20];
    byte[] ciphertext = new byte[messageBytes.length - 20];

    System.arraycopy(messageBytes, 0, macBytes, 0, macBytes.length);
    System.arraycopy(messageBytes, 20, ciphertext, 0, ciphertext.length);

    Mac mac = Mac.getInstance("HMACSHA1");
    mac.init(keySpec);

    verifyMac(mac.doFinal(ciphertext), macBytes);

    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

    byte[] results = cipher.doFinal(ciphertext);
    return new String(results, "UTF-8");
}

private static void verifyMac(byte[] mac1, byte[] mac2) throws Exception {
    MessageDigest sha = MessageDigest.getInstance("SHA1");
    byte[] mac1_hash = sha.digest(mac1);
    sha.reset();
    byte[] mac2_hash = sha.digest(mac2);

    if(!Arrays.equals(mac1_hash, mac2_hash)){
        throw new RuntimeException("Invalid MAC");
    }

}

只是一些关于安全实践的说明。使用键入密码的 UTF-8 字节不会给您的密钥提供足够的熵。如果您打算将密码用作加密密钥,则应使用密钥拉伸算法对其进行处理。PBKDF2是一个流行的选择。AES 密码的 IV 应该是随机生成的(使用加密安全的随机源)并与密​​文一起以明文形式传输。用于初始化 AES 密码的密钥应该不同于用于 MAC 的密钥。您可以使用 PBKDF2 从您提供的密码中创建两倍于您需要的密钥,并将前半部分用于 AES,将后半部分用于 HMAC-SHA1。最后一点,在验证 MAC 时,您应该验证 MAC 的哈希值,而不是直接验证 MAC(如verifyMac上面的方法)。验证 MA 直接暴露了可能的时序攻击向量。

于 2013-10-19T04:32:26.463 回答