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

无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规范不完全匹配,或公钥处理存在问题,可按以下步骤修复:

  1. 规范公钥处理:从KMS获取的Base64公钥可能包含换行符等非Base64字符,需先清理后再解码,避免解析出错误的公钥。
  2. 显式指定加密Provider:不同JDK Provider的RSA OAEP实现可能存在差异,指定SunJCE Provider可确保符合RFC 8017标准,与AWS KMS的算法实现对齐。
  3. 明确字符编码:将明文转换为字节时指定UTF-8编码,避免平台默认编码不一致导致的解密后内容异常(虽不是解密失败的直接原因,但能保证数据一致性)。
  4. 验证公钥有效性:确认使用的Base64公钥来自目标CMK的GetPublicKey接口返回值,确保公钥与解密用的CMK完全对应。

经过上述修改后,加密生成的密文即可被AWS KMS的Decrypt接口正常解密。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 19:08:10