C#对使用AES的Java加密/解密的好奇行为
通过使用AES算法的字符串解密,我有一个非常奇怪的问题.我的C#应用??程序将加密数据(字符串)发送到
Java应用程序.即使我使用相同的密钥字符串,解密也会导致异常:
javax.crypto.BadPaddingException:给定最终块未正确填充 但是只有当要加密的普通输入文本的长度(在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,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,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编解码器,但结果是一样的…… 解决方法
如果您通过网络传输密文,则应使用
message authentication code确保其完整性.在尝试解密密文之前,应验证MAC.这将防止意外破坏和恶意篡改线路上的消息.在这种情况下,它应该可以帮助您确保传输的密文与您接收的密文完全相同.
我重写了java解密端进行MAC验证.它假定MAC是使用HMACSHA1算法生成的,该算法使用与AES密码相同的密钥材料进行初始化(注意:这仅是一个示例,加密密钥和HMAC密钥在实际系统中应该是不同的)并且预先附加到密文.消息的前20个字节应该是MAC,然后是紧随其后的密文. public static String Decrypt(String message,"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,macBytes,macBytes.length); System.arraycopy(messageBytes,20,ciphertext,ciphertext.length); Mac mac = Mac.getInstance("HMACSHA1"); mac.init(keySpec); verifyMac(mac.doFinal(ciphertext),macBytes); cipher.init(Cipher.DECRYPT_MODE,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直接暴露可能的定时攻击向量. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |