You need to enable JavaScript to run this app. 如何在Kotlin端解密Python的AES/CBC加密字符串及存储传输问题?
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin端解密Python的AES/CBC加密字符串及存储传输问题?

Python AES/CBC 加密对接 Kotlin 端实现说明

核心疑问校验

  • 你最初提出的「将加密内容以Base64字符串格式存入数据库、客户端解码为ByteArray后解密」的方案逻辑完全通顺,是跨语言二进制内容传输存储的通用可行方案,没有本质问题。
  • API层面因为JSON是纯文本协议,二进制内容必须经Base64编码后才能嵌入传输,这个转换流程无法跳过。

现有代码的互通问题定位

你当前贴的Python类定义和Kotlin解密代码完全无法实现端到端互通,核心问题如下:

  • Python端存在语法错误:class AESCipher(object, key): 是不合法写法,key是构造函数参数,不属于类的继承列表,正确写法应为 class AESCipher(object):,key通过__init__方法传入。
  • IV逻辑完全不匹配:Python端加密时将随机生成的IV直接拼在密文头部(输出结构为16字节IV + 实际密文),根本没有独立存储固定IV的逻辑。你在Kotlin代码里实现的从本地SharedPreferences读取预置固定IV的逻辑,和Python端的输出结构完全错位。
  • 密钥派生逻辑不匹配:Python端通过对原始共享密钥做SHA256哈希,直接取32字节摘要作为AES实际密钥。而Kotlin端当前是通过Java序列化反序列化读取SecretKey对象,两边生成的密钥字节完全不一致,无法解密。
  • 冗余逻辑多余:Kotlin解密方法里循环将解密字节转字符拼接StringBuilder的操作没有实际作用,可直接删除。
  • 序列化存取方案兼容风险高:用ObjectInputStream反序列化SecretKey、IV的方式,不仅没有必要,还会因为JDK版本差异、类序列化ID不匹配出现运行时崩溃故障。AES密钥、IV本质都是固定长度字节数组,直接存原始字节即可,不需要走Java序列化流程。

优化方案与正确实现

存储与传输的开销权衡

你后续尝试去掉Python端Base64编码、直接返回原始二进制密文的优化方向,实际权衡逻辑如下:

  • 数据库层面:完全可以用BLOB类型直接存储IV+密文的原始二进制内容,相比存Base64字符串可节省约33%的存储空间,这个收益是真实有效的。
  • API传输层面:JSON协议本身不支持嵌入原始二进制数据,无论数据库层存的是字符串还是BLOB,只要走JSON接口传输,就必须将二进制内容临时转成Base64字符串嵌入到JSON字段中,否则会出现编码损坏、乱码、传输截断的问题。
    • 这种场景下你的优化仍然有价值:数据库层的存储成本实实在在降低了,只是传输层的Base64编码是绕不开的通用开销,并不会因为数据库存BLOB就省掉这部分转换成本。

修正后的Kotlin端解密实现

完全对齐Python端的加密逻辑的Kotlin代码如下:

import android.util.Base64
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import java.security.MessageDigest

/**
 * 对接Python端AES/CBC/PKCS7加密的解密方法
 * @param encryptedBase64 API返回的Base64编码加密字符串
 * @param sharedSecret 双方约定的共享密钥
 */
fun decryptFromServer(encryptedBase64: String, sharedSecret: String): String {
    // 1. 解码API传来的Base64内容,整体结构为 16字节IV + 密文
    val fullData = Base64.decode(encryptedBase64, Base64.DEFAULT)
    // 拆分IV和实际密文
    val iv = fullData.copyOfRange(0, 16)
    val cipherText = fullData.copyOfRange(16, fullData.size)

    // 2. 对齐Python端的密钥生成逻辑:对共享密钥做SHA256哈希取32字节作为AES密钥
    val sha256Digest = MessageDigest.getInstance("SHA-256")
    val keyBytes = sha256Digest.digest(sharedSecret.toByteArray(Charsets.UTF_8))
    val secretKey = SecretKeySpec(keyBytes, "AES")

    // 3. 初始化CBC模式解密:AES块大小为16字节时,Java的PKCS5Padding和Python手写的PKCS7填充完全等价
    val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
    val ivSpec = IvParameterSpec(iv)
    cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)

    // 4. 解密并转为UTF-8字符串
    val decryptedBytes = cipher.doFinal(cipherText)
    return String(decryptedBytes, Charsets.UTF_8)
}

内容的提问来源于stack exchange,提问作者Houman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.28 04:06:00