pythonjavaencryptionrsaamazon-kms

How to encrypt data for AWS KMS using public key in plain java without awssdk


Hi I am trying to encrypt text using a cmk public key generated in AWS KMS in plain java without using awssdk:

The specs for the key i have generated look like this -

enter image description here

Key type:Asymmetric
Origin:AWS KMS
Key spec:RSA_2048
Key usage:Encrypt and decrypt
Encryption algorithms:RSAES_OAEP_SHA_1,RSAES_OAEP_SHA_256

Heres the java code i have written -

    // Convert a Base64-encoded public key string into a PublicKey object
    public static PublicKey getPublicKeyFromString(String base64PublicKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(base64PublicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }

    // Encrypt the plaintext using the public key
    public static String encryptData(PublicKey publicKey, String plaintext) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);

        byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
        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);
    }

It works fine and generates a cipher text output in base64 format.

But the problem is when i try to decrypt it using aws kms service, its throwing InvalidCiphertextException('An error occurred (InvalidCiphertextException) when calling the Decrypt operation: ')

Decryption block in python looks like this:

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",
        )

        # The decrypted plaintext (as bytes)
        decrypted_data = response["Plaintext"]

        # Convert decrypted data to string (assuming UTF-8 encoding)
        return decrypted_data.decode("utf-8")
    except (BotoCoreError, ClientError) as error:
        rep = repr(error)
        logger.error(rep)
        return None

Any help on what im doing wrong would be very useful thanks!

Java side encryption works fine and generates a cipher text output in base64 format. But the generated output does not get encryptes successfully using the kms service on python side.

When i try to decrypt it using aws kms service in python side, its throwing: InvalidCiphertextException('An error occurred (InvalidCiphertextException) when calling the Decrypt operation: ')

In python when i use the kms service itself to encrypt, the decryption method above decrypts it fine. Which makes me think there's some issue with the encryption config i am putting in on the java side.

Edit: I can't use awssdk because the service where i am adding the encryption part does not allow adding external dependencies.


Solution

  • Just in case someone else stumbles upon this in the future, here's what ended up working for me -

    Java side encryption method (no awssdk or external library) -

    public static String encryptData(String plainText, PublicKey publicKey) throws GeneralSecurityException {
        AlgorithmParameters parameters = AlgorithmParameters.getInstance("OAEP");
        AlgorithmParameterSpec specification = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
        parameters.init(specification);
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey, parameters);
        byte[] encryptD = cipher.doFinal(plainText.getBytes());
        return Base64.getEncoder().encodeToString(encryptD);
    }
    

    Python side decryption method using aws library boto3 -

    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"]
    
            return decrypted_data.decode("utf-8")
        except (BotoCoreError, ClientError) as error:
            rep = repr(error)
            logger.error(rep)
            return None
    

    Using above code, data encrypted on java side decrypts successfully on the python side.