AES 256-CBC上的密钥和iv问题
我从Python获得了加密的base64字符串。
格式为AES 256 CBC,但是当我尝试使用Android解密时,它将解密的字符串作为nil返回。
python
# coding=utf-8 import base64 from random import choice from string import letters try: from Crypto import Random from Crypto.Cipher import AES except ImportError: import crypto import sys sys.modules['Crypto'] = crypto from crypto.Cipher import AES from crypto import Random class AESCipher(object): def __init__(self, key): self.bs = 32 self.key = key def encrypt(self, raw): _raw = raw raw = self._pad(raw) print raw, ';' print _raw, ';' iv = "".join([choice(letters[:26]) for i in xrange(16)]) print " iv :", iv cipher = AES.new(self.key, AES.MODE_CBC, iv) return base64.b64encode(iv + cipher.encrypt(raw)) def decrypt(self, enc): enc = base64.b64decode(enc) iv = enc[:AES.block_size] cipher = AES.new(self.key, AES.MODE_CBC, iv) return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8') def _pad(self, s): a = (self.bs - len(s) % self.bs) b = chr(self.bs - len(s) % self.bs) return s + a * b @staticmethod def _unpad(s): return s[:-ord(s[len(s) - 1:])] def encrypt(k, t): o = AESCipher(k) return o.encrypt(t) def decrypt(k, t): o = AESCipher(k) return o.decrypt(t) def main(): k = "qwertyuiopasdfghjklzxcvbnmqwerty" s1 = "Hello World!" d2 = encrypt(k, s1) print " Password :", k print "Encrypted :", d2 print " Plain :", decrypt(k, d2) if __name__ == '__main__': main()
Java的
在这里,我使用https://github.com/fukata/AES-256-CBC-Example
final String aEcodedSting = "aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk"; String decrypted = AESUtil.decrypt(aEcodedSting);
当我尝试解密时,我得到了这个
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.vinu.aessamble/com.example.vinu.aessamble.MainActivity}: java.lang.RuntimeException: javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT
这是Python加密输出:
Password : qwertyuiopasdfghjklzxcvbnmqwerty Encrypted : aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk iv : iegejanpeybezgmy plainText : ser456&*(
如果有人可以使用其他图书馆解决此问题,请通知我。
有4个问题:
- python输出和java输入之间的区别
- 不同的IV和关键
- 不同的密钥创建
- 填充
1)目前你的python代码输出是iv + encrypted_data
的base64编码
return base64.b64encode(iv + cipher.encrypt(raw))
但是在java中你直接解密原始数据。
你应该这样修复
// Decode base64 byte[] array = Base64.decode(src); // Get only encrypted data (removing first 16 byte, namely the IV) byte[] encrypted = Arrays.copyOfRange(array, 16, array.length); // Decrypt data decrypted = new String(cipher.doFinal(encrypted));
2)您必须使用相同的IV和键进行输入和输出,因此您应该从python控制台输出中复制它们:
iv : qbmocwtttkttpqvv Password : qwertyuiopasdfghjklzxcvbnmqwerty Encrypted : anZxZHVpaWJpb2FhaWdqaCK0Un7H9J4UlXRizOJ7s8lchAWAPdH4GRf5tLAkCmm6 Plain : Hello World!
并粘贴在java代码中:
private static final String ENCRYPTION_KEY = "qwertyuiopasdfghjklzxcvbnmqwerty"; private static final String ENCRYPTION_IV = "qbmocwtttkttpqvv";
3)在python中你使用键作为字符串,但在java库中它在用于解密之前进行哈希处理,因此你应该改变你的makeKey()
方法:
static Key makeKey() { try { byte[] key = ENCRYPTION_KEY.getBytes("UTF-8"); return new SecretKeySpec(key, "AES"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; }
4)最后,您不需要在Java中使用"AES/CBC/PKCS5Padding"
指定填充,因为这样可以强制Cipher
自动填充。
您可以在decrypt()
方法中使用"AES/CBC/NoPadding"
,因此它应如下所示:
public static String decrypt(String src) { String decrypted = ""; try { Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv()); byte[] array = Base64.decode(src); byte[] encrypted = Arrays.copyOfRange(array, 16, array.length); decrypted = new String(cipher.doFinal(encrypted)); } catch (Exception e) { throw new RuntimeException(e); } return decrypted; }
使用base64和IV的Java输出:
encrypted: aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk decrypted: ser456&*(
编辑:
正如Artjom B. (谢谢)所建议的那样 ,最好直接从密文读取IV而不是AESUtil中的硬编码。
您的输入包括前16个字节的IV和最后16个字节的加密文本,因此您可以利用此function。
public static String decrypt(String src) { String decrypted = ""; try { Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); // Decode input byte[] array = Base64.decode(src); // Read first 16 bytes (IV data) byte[] ivData = Arrays.copyOfRange(array, 0, 16); // Read last 16 bytes (encrypted text) byte[] encrypted = Arrays.copyOfRange(array, 16, array.length); // Init the cipher with decrypt mode, key, and IV bytes array (no more hardcoded) cipher.init(Cipher.DECRYPT_MODE, makeKey(), new IvParameterSpec(ivData)); // Decrypt same old way decrypted = new String(cipher.doFinal(encrypted)); } catch (Exception e) { throw new RuntimeException(e); } return decrypted; }
而且,正如这里所说
Python代码使用32字节块大小进行填充,这意味着Java仍然无法解密所有可能密文的一半。 AES块大小为16个字节,应在Python实现中进行更改
您可以按如下所示更改Python类( AES.block_size
等于16):
class AESCipher(object): def __init__(self, key): self.bs = AES.block_size self.key = key
- 是什么导致错误“java.security.InvalidKeyException:参数缺失”?
- 服务响应速度慢时间:Java SecureRandom和/ dev / random
- 密码:IllegalBlockSizeException的原因是什么?
- Android:使用带有iv和密钥的AES 256位加密来加密字符串
- 使用Base64的AES会为同一纯文本生成不同的加密结果吗?
- 如何用Bouncy Castle生成对称密钥?
- AES / CBC / PKCS5Padding与AES / CBC / PKCS7Padding具有256个密钥大小的性能java
- 如何使用带有AES和PBE的Bouncy Castle轻量级API
- Java AES加密整个字符串