You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Play提示不安全AES加密模式,改用GCM后解密失败如何解决?

解决AES加密安全合规与解密失效问题

问题根源

原代码使用Cipher.getInstance("AES")会默认采用ECB模式,这是Google Play明确禁止的不安全加密模式(ECB不提供随机性,相同明文会生成相同密文,易被破解)。而切换到AES/GCM/NoPadding后解密失败,是因为GCM属于认证加密模式,需要随机生成的初始化向量(IV)和加密生成的认证标签,原代码未处理这两个要素的存储与传递,导致解密时无法还原原始数据。

解决方案

1. 修改加密逻辑(符合安全要求)

生成随机IV、初始化GCM模式Cipher,将IV、密文、认证标签组合后存储:

override suspend fun encryption(imageView: ImageView, name: String, uid: String, x: Firestore, type: String) {
    val bitmap = (imageView.drawable as BitmapDrawable).bitmap
    val height = bitmap.height
    val width = bitmap.width
    val real = "$name/$height/$width"
    
    // 更新Firestore数据(保留原有逻辑)
    Firebase.firestore.collection("profiles").document(uid)
        .update(
            "pending", FieldValue.arrayUnion(real),
            type, FieldValue.arrayRemove(name),
            "isverified_verqreq", "falset"
        )
        .addOnCompleteListener {
            x.middle()
            
            // Bitmap转字节数组(保留原有逻辑)
            val size = bitmap.rowBytes * bitmap.height
            val byteBuffer = ByteBuffer.allocate(size)
            bitmap.copyPixelsToBuffer(byteBuffer)
            val byteArray = byteBuffer.array()
            
            // 读取Firebase存储的密钥(保留原有逻辑)
            val storageRef3 = FirebaseStorage.getInstance().reference.child("/image/$uid/key/")
            val key = File.createTempFile("temp", ".txt")
            storageRef3.getFile(key)
                .addOnSuccessListener {
                    x.middle2()
                    val key2 = key.readBytes()
                    
                    // --- 加密核心修改逻辑 ---
                    // 生成12字节随机IV(GCM推荐长度,保证随机性)
                    val iv = ByteArray(12)
                    SecureRandom().nextBytes(iv)
                    
                    // 初始化GCM模式Cipher
                    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
                    val keySpec = SecretKeySpec(key2, "AES")
                    cipher.init(Cipher.ENCRYPT_MODE, keySpec, GCMParameterSpec(128, iv))
                    
                    // 执行加密并获取认证标签
                    val encryptedData = cipher.doFinal(byteArray)
                    val tag = cipher.getGCMTag()
                    
                    // 组合IV、密文、标签:IV(12字节) + 密文 + 标签(16字节)
                    val combinedData = ByteArray(iv.size + encryptedData.size + tag.size)
                    System.arraycopy(iv, 0, combinedData, 0, iv.size)
                    System.arraycopy(encryptedData, 0, combinedData, iv.size, encryptedData.size)
                    System.arraycopy(tag, 0, combinedData, iv.size + encryptedData.size, tag.size)
                    // --- 加密核心修改逻辑结束 ---
                    
                    // 上传组合后的数据(替换原加密字节数组)
                    val file = File.createTempFile(name, null)
                    file.writeBytes(combinedData)
                    val storageReference = FirebaseStorage.getInstance()
                        .getReference("/image/$uid/$name/")
                    storageReference.putFile(Uri.fromFile(file))
                        .addOnSuccessListener {
                            x.sucess(name)
                        }
                        .addOnFailureListener {
                            x.failure()
                        }
                }
        }
}

2. 对应修改解密逻辑

解密时拆分存储的组合数据,提取IV、密文、标签,再初始化GCM模式Cipher解密:

// 示例解密函数(需根据业务逻辑调整)
fun decryptImageData(encryptedCombinedData: ByteArray, key2: ByteArray): ByteArray {
    // 拆分数据:IV(12字节)、密文、标签(16字节)
    val iv = ByteArray(12)
    val tag = ByteArray(16)
    val encryptedData = ByteArray(encryptedCombinedData.size - iv.size - tag.size)
    
    System.arraycopy(encryptedCombinedData, 0, iv, 0, iv.size)
    System.arraycopy(encryptedCombinedData, iv.size, encryptedData, 0, encryptedData.size)
    System.arraycopy(encryptedCombinedData, iv.size + encryptedData.size, tag, 0, tag.size)
    
    // 初始化GCM解密模式
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    val keySpec = SecretKeySpec(key2, "AES")
    cipher.init(Cipher.DECRYPT_MODE, keySpec, GCMParameterSpec(128, iv))
    
    // 验证标签并解密
    cipher.updateAAD(ByteArray(0)) // 无额外认证数据时传入空数组
    return cipher.doFinal(encryptedData)
}

关键注意事项

  • IV必须随机唯一:每次加密都要生成新IV,禁止重复使用,否则会破坏GCM安全性。
  • 密钥安全:Firebase存储的AES密钥需通过安全方案管理(如KMS),避免明文暴露。
  • 参数一致性:解密端必须使用与加密端完全一致的算法、IV长度、标签长度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:25:04