我从Python获得了加密的base64字符串。
格式为AES 256 CBC,但是当我尝试使用Android解密时,它会将解密后的字符串返回为nil。
# 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()
我在这里使用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&*(
当有人可以使用其他图书馆解决此问题时,请通知我。
答案 0 :(得分:4)
有4个问题:
1)目前你的python代码输出是iv + encrypted_data
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)最后,您不需要在"AES/CBC/PKCS5Padding"
的java中指定填充,因为这样可以强制Cipher
自动填充。
您可以在"AES/CBC/NoPadding"
方法中使用decrypt()
,因此它应如下所示:
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个字节的加密文本,因此您可以利用此功能。
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;
}
此外,正如所说here
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