python - 如何在 Kotlin 中解密 AES/CBC 加密字符串?
问题描述
我在服务器上有这个 Python 方法将字符串加密为字节(AES/CBC)。
class AESCipher(object, key):
def __init__(self, key):
self.bs = AES.block_size
self.key = hashlib.sha256(key.encode()).digest()
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw.encode()))
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):
return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)
encrypt() 的输出是bytes
这样的:b'PMgMOkBkciIKfWy/DfntVMyAcKtVsM8LwEwnTYE5IXY='
我想将其存储到数据库中,并通过 API 将其作为字符串发送到 Kotlin。在那里,我想通过相同的共享密钥对其进行解密。
- 我以什么格式将上面的字节保存到数据库中?
- 到达 Kotlin 客户端后,如何将该字符串转换为
ByteArray
?
我的理论是我必须将字节作为base64字符串存储在数据库中。另一方面,我必须将字符串作为 base64 解码为字节。这种方法正确吗?加密/解密是否会像下面的代码那样端到端地工作?
fun decrypt(context:Context, dataToDecrypt: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
val ivSpec = IvParameterSpec(getSavedInitializationVector(context))
cipher.init(Cipher.DECRYPT_MODE, getSavedSecretKey(context), ivSpec)
val cipherText = cipher.doFinal(dataToDecrypt)
val sb = StringBuilder()
for (b in cipherText) {
sb.append(b.toChar())
}
return cipherText
}
fun getSavedSecretKey(context: Context): SecretKey {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)
val strSecretKey = sharedPref.getString("secret_key", "")
val bytes = android.util.Base64.decode(strSecretKey, android.util.Base64.DEFAULT)
val ois = ObjectInputStream(ByteArrayInputStream(bytes))
val secretKey = ois.readObject() as SecretKey
return secretKey
}
fun getSavedInitializationVector(context: Context) : ByteArray {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)
val strInitializationVector = sharedPref.getString("initialization_vector", "")
val bytes = android.util.Base64.decode(strInitializationVector, android.util.Base64.DEFAULT)
val ois = ObjectInputStream(ByteArrayInputStream(bytes))
val initializationVector = ois.readObject() as ByteArray
return initializationVector
}
更新
我已尝试按照建议删除 Base64 以消除内存开销。
Python:
def encrypt(self, raw):
raw = self._pad(raw)
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(raw.encode())
所以这已经不可能了。
enc = AESCipher('abc').encrypt("myLife")
value_to_save_in_db = enc.decode("utf8")
所以我需要找到一种方法将字节数组直接存储在数据库中。我想我应该能够做到这一点。但是仍然存在一些挑战,例如如何通过 API 将字节数组作为 JSON 的一部分发送到 android 设备。我想我必须再次将其转换为 Base64 字符串。不知道在那种情况下我是否有所收获......
解决方案
以下 Kotlin 代码:
val decrypted = decrypt("blEOKMQtUbNOzJbvEkL2gNhjF+qQ/ZK84f2ADu8xyUFme6uBhNYqvEherF/RRO9YRImz5Y04/ll+T07kqv+ExQ==");
println(decrypted);
解密 Python 代码的密文。这里decrypt()
是:
fun decrypt(dataToDecryptB64 : String) : String {
// Base64 decode Python data
val dataToDecrypt = Base64.getDecoder().decode(dataToDecryptB64)
// Separate IV and Ciphertext
val ivBytes = ByteArray(16)
val cipherBytes = ByteArray(dataToDecrypt.size - ivBytes.size)
System.arraycopy(dataToDecrypt, 0, ivBytes, 0, ivBytes.size)
System.arraycopy(dataToDecrypt, ivBytes.size, cipherBytes, 0, cipherBytes.size)
// Derive key
val keyBytes = MessageDigest.getInstance("SHA256").digest("abc".toByteArray(Charsets.UTF_8))
// Decrypt
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(keyBytes, "AES"), IvParameterSpec(ivBytes))
val cipherText = cipher.doFinal(cipherBytes)
return String(cipherText, Charsets.ISO_8859_1)
}
为此,密文是使用已发布的 Python 类生成的AESCipher
,如下所示:
plaintext = 'The quick brown fox jumps over the lazy dog'
cipher = AESCipher('abc')
ciphertext = cipher.encrypt(plaintext)
print(ciphertext.decode('utf8')) # Base64 string, which can be stored e.g. in a DB
我应用了最初发布的 Python 实现,它使用 SHA256 派生密钥。但是,如果密钥来自密码,出于安全原因,不应使用 SHA256,而是应使用可靠的密钥导出函数,例如 Argon2 或 PBKDF2。
Kotlin 代码首先对 Python 数据进行 Base64 解码,然后将 IV 和实际密文分开。然后,通过生成密码的 SHA256 哈希来导出密钥。最后解密数据。
当前的 Python 代码 Base64 对数据进行编码,以便可以将其作为字符串存储在数据库中。或者,可以修改 Python 代码,以便不执行 Base64 编码,并且可以存储原始数据(这需要更少的内存,Base64开销:33%)。
根据选择的解决方案,Kotlin 代码可能需要也可能不需要 Base64 解码数据。
推荐阅读
- python-3.x - 如何在 TinyDB 中只返回键的值
- c# - C# WinForms - ListViewItem 图像未显示
- python - 谁能在下面的代码中解释“out = self(images)”的作用
- c# - 将字符串字段反序列化为属性对象
- python - 无法在 python 中打印简单的斐波那契数列?
- javascript - axios post 请求正在发送 Content-Type: multipart/form-data 的请求标头,导致未定义的 req.body
- android - 使用 JsonReader.setLenient(true) 在第 1 行第 1 列路径 $ 接受格式错误的 JSON。我怎样才能解决这个问题
- qt - 在 Qt5.12 中,我试图让绘制的水平标题正确调整大小
- ios - Unity 使用 Google 的 Firebase Firestore 构建返回许多错误
- ebay-api - ebay API - 获取促销列表