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
相关产品推荐
相关产品推荐

