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个问题:

  1. python输出和java输入之间的区别
  2. 不同的IV和关键
  3. 不同的密钥创建
  4. 填充

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