无AWS SDK纯Java用AWS KMS公钥加密后解密失败排查
问题:纯Java环境使用AWS KMS非对称CMK公钥加密后无法被KMS解密
密钥规格
- 密钥类型:非对称(Asymmetric)
- 密钥来源:AWS KMS
- 密钥规格:RSA_2048
- 密钥用途:加密和解密
- 支持加密算法:RSAES_OAEP_SHA_1、RSAES_OAEP_SHA_256
Java加密代码
import java.security.KeyFactory; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.spec.OAEPParameterSpec; import javax.crypto.spec.PSource; import java.security.spec.MGF1ParameterSpec; import java.nio.charset.StandardCharsets; public class KMSEncrypt { // 将Base64编码的公钥字符串转换为PublicKey对象 public static PublicKey getPublicKeyFromString(String base64PublicKey) throws Exception { // 清理公钥字符串中的非Base64字符(如换行符) String cleanPublicKey = base64PublicKey.replaceAll("\\s+", ""); byte[] keyBytes = Base64.getDecoder().decode(cleanPublicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); // 显式指定SunJCE Provider,确保算法实现符合标准 KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SunJCE"); return keyFactory.generatePublic(keySpec); } // 使用公钥加密明文 public static String encryptData(PublicKey publicKey, String plaintext) throws Exception { // 显式指定SunJCE Provider和加密算法 Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "SunJCE"); OAEPParameterSpec oaepParams = new OAEPParameterSpec( "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT ); cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams); // 指定UTF-8编码,避免平台默认编码差异 byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(ciphertext); } public static void main(String[] args) throws Exception { String base64PublicKey = "my_public_key"; PublicKey publicKey = getPublicKeyFromString(base64PublicKey); String plaintext = "Sensitive Data"; String encryptedData = encryptData(publicKey, plaintext); System.out.println(encryptedData); } }
问题现象
上述Java代码可正常生成Base64格式密文,但通过以下Python代码调用AWS KMS解密时,抛出InvalidCiphertextException('An error occurred (InvalidCiphertextException) when calling the Decrypt operation: ')异常。使用KMS自身加密的密文可正常解密,推测Java端加密配置存在问题,且无法使用AWS SDK、不能添加外部依赖。
Python解密代码
import boto3 from botocore.exceptions import BotoCoreError, ClientError import base64 import logging logger = logging.getLogger(__name__) AWS_REGION_NAME = "your-region" def decrypt_kms(cipher_text: str, key_id: str) -> str: session = boto3.session.Session() kms_client = session.client(service_name="kms", region_name=AWS_REGION_NAME) try: encrypted_data = base64.b64decode(cipher_text) response = kms_client.decrypt( CiphertextBlob=encrypted_data, KeyId=key_id, EncryptionAlgorithm="RSAES_OAEP_SHA_256", ) # 解密后的明文(字节格式) decrypted_data = response["Plaintext"] # 将字节转换为字符串(假设UTF-8编码) return decrypted_data.decode("utf-8") except (BotoCoreError, ClientError) as error: rep = repr(error) logger.error(rep) return None
排查与解决方案
导致解密失败的核心原因通常是Java端加密参数与AWS KMS的RSAES_OAEP_SHA_256规范不完全匹配,或公钥处理存在问题,可按以下步骤修复:
- 规范公钥处理:从KMS获取的Base64公钥可能包含换行符等非Base64字符,需先清理后再解码,避免解析出错误的公钥。
- 显式指定加密Provider:不同JDK Provider的RSA OAEP实现可能存在差异,指定
SunJCEProvider可确保符合RFC 8017标准,与AWS KMS的算法实现对齐。 - 明确字符编码:将明文转换为字节时指定
UTF-8编码,避免平台默认编码不一致导致的解密后内容异常(虽不是解密失败的直接原因,但能保证数据一致性)。 - 验证公钥有效性:确认使用的Base64公钥来自目标CMK的
GetPublicKey接口返回值,确保公钥与解密用的CMK完全对应。
经过上述修改后,加密生成的密文即可被AWS KMS的Decrypt接口正常解密。
内容的提问来源于stack exchange,提问作者Saurabh Khirwal
相关产品推荐
相关产品推荐

