Ionic离线加密数据无法通过AWS KMS解密,报InvalidCiphertextException
问题:离线AES加密文件无法通过AWS KMS解密,报错InvalidCiphertextException
背景
我正在开发一款支持离线工作、可离线加密数据的Ionic移动应用,已经写好了加密服务,用CryptoJS的AES算法加密文件并输出加密文件,加密功能本身正常。但尝试用AWS CLI调用KMS的Decrypt操作解密时,一直报错:An error occurred (InvalidCiphertextException) when calling the Decrypt operation:,怀疑是离线加密的算法逻辑有问题,求指出错误所在和解决方向。
加密服务代码(TypeScript)
import { Injectable } from '@angular/core'; import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'; import * as CryptoJS from 'crypto-js'; import { environment } from 'src/environments/environment'; @Injectable({ providedIn: 'root', }) export class EncryptionService { private encryptionKey: string; constructor() { // 初始化加密密钥(Base64编码字符串) this.encryptionKey = environment.encryptionKey; } // AES加密文件内容的函数 public async encryptFile(fileName: string): Promise<void> { try { // 步骤1:读取文件内容 const file = await Filesystem.readFile({ path: fileName, directory: Directory.Documents, encoding: Encoding.UTF8, // 假设是文本文件(按需修改) }); let fileContents: string; // 步骤2:确保文件内容是字符串格式 if (typeof file.data === 'string') { fileContents = file.data; } else { throw new Error('不支持的文件格式,预期为字符串。'); } // 步骤3:用AES加密内容 const iv = CryptoJS.lib.WordArray.random(12); const encrypted = CryptoJS.AES.encrypt(fileContents, CryptoJS.enc.Base64.parse(this.encryptionKey), { iv: iv, format: CryptoJS.format.OpenSSL, // 使用OpenSSL格式 }); // 步骤4:合并IV和密文 const combinedData = iv.concat(encrypted.ciphertext); const encryptedDataBase64 = CryptoJS.enc.Base64.stringify(combinedData); console.log('加密后的数据:', encryptedDataBase64); // 步骤5:将加密内容写入文件系统 const encryptedFileName = `encrypted_${fileName}`; await Filesystem.writeFile({ path: encryptedFileName, data: encryptedDataBase64, directory: Directory.Documents, encoding: Encoding.UTF8, }); console.log('文件已加密并保存为:', encryptedFileName); } catch (error) { console.error('加密文件时出错:', error); } } }
用于解密的AWS CLI命令
ENCRYPTED_DATA=$(cat encrypted_sample.txt) DECRYPTED_DATA=$(aws kms decrypt --profile <profile-string-here> \ --key-id <key-id-string-here> \ --ciphertext-blob fileb://<(echo "$ENCRYPTED_DATA" | base64 --decode) \ --region eu-west-1 \ --query Plaintext \ --output text) echo "解密后的数据: $(echo "$DECRYPTED_DATA" | base64 --decode)"
错误原因分析
AWS KMS的Decrypt接口不是通用的AES解密工具,它只能处理两种情况的密文:
- 由KMS自身的Encrypt/GenerateDataKey等接口直接加密生成的密文
- 用KMS生成的数据密钥加密的内容,但需要配合KMS解密数据密钥的流程
你的代码是用本地自定义的AES密钥直接加密文件,生成的密文格式完全不符合KMS的要求——KMS无法识别这种没有关联元数据(比如密钥标识、加密上下文)的自定义密文,自然会抛出InvalidCiphertextException错误。
解决方向:采用信封加密模式
根据你离线加密的需求,正确的做法是使用信封加密,核心逻辑是:在线时用KMS生成数据密钥,离线时用本地数据密钥加密文件,解密时先通过KMS解密数据密钥,再用本地密钥解密文件。
调整后的加密流程(核心逻辑)
在线阶段:获取并加密数据密钥
调用KMS的GenerateDataKey接口,得到明文数据密钥(用于本地加密)和加密后的密钥(存储起来,后续解密用)// 需引入AWS SDK for JavaScript import * as AWS from 'aws-sdk'; async getEncryptedDataKey(): Promise<{ encryptedKey: string; plainKey: string }> { const kms = new AWS.KMS({ region: 'eu-west-1' }); const response = await kms.generateDataKey({ KeyId: '<你的KMS密钥ID>', KeySpec: 'AES_256' // 和CryptoJS使用的密钥长度匹配 }).promise(); // 转换为Base64方便存储 return { encryptedKey: CryptoJS.enc.Base64.stringify(response.CiphertextBlob), plainKey: CryptoJS.enc.Base64.stringify(response.Plaintext) }; }离线阶段:用本地数据密钥加密文件
保留你原有的AES加密逻辑,但改用KMS生成的明文数据密钥,同时推荐使用更安全的GCM模式:async encryptFileOffline(fileName: string, plainDataKey: string): Promise<void> { const file = await Filesystem.readFile({ path: fileName, directory: Directory.Documents, encoding: Encoding.UTF8, }); const fileContents = file.data as string; const iv = CryptoJS.lib.WordArray.random(12); const encrypted = CryptoJS.AES.encrypt(fileContents, CryptoJS.enc.Base64.parse(plainDataKey), { iv: iv, mode: CryptoJS.mode.GCM, // 推荐使用GCM认证加密模式 padding: CryptoJS.pad.Pkcs7 }); // 合并IV、密文、GCM认证标签(用于解密时验证完整性) const combinedData = iv.concat(encrypted.ciphertext).concat(encrypted.authTag); const encryptedDataBase64 = CryptoJS.enc.Base64.stringify(combinedData); // 写入加密文件,同时要保存对应的加密后数据密钥 await Filesystem.writeFile({ path: `encrypted_${fileName}`, data: encryptedDataBase64, directory: Directory.Documents, encoding: Encoding.UTF8, }); }
解密流程
- 读取加密文件和存储的加密后数据密钥
- 调用KMS Decrypt解密加密后的数据密钥,得到明文本地密钥
- 拆分加密文件中的IV、密文、认证标签
- 用明文密钥+IV+认证标签解密文件内容
关键注意事项
- 绝对不要把KMS主密钥(CMK)直接用于本地加密,必须使用数据密钥做信封加密
- 加密模式优先选GCM,比CBC更安全,且KMS支持该模式的数据密钥操作
- 确保CryptoJS的加密参数(密钥长度、IV长度、填充模式)和KMS生成的数据密钥完全匹配
内容的提问来源于stack exchange,提问作者The Sammie
相关产品推荐
相关产品推荐

