I'm writing an apex class to receive incoming AES256 encrypted data. I have a private key safely stored, and the incoming data includes an iv. My logs show me that the retrieved private key is 32 bytes, and the iv is 16 bytes.

22:39:11.4 (12736525)|VARIABLE_ASSIGNMENT|[20]|iv|BLOB(16 bytes)
22:39:11.4 (42204647)|VARIABLE_ASSIGNMENT|[35]|key|BLOB(32 bytes)

My cipherText is 426 bytes. Is that required to be a multiple of 16?

22:39:11.4 (42415528)|VARIABLE_ASSIGNMENT|[39]|cipherText|BLOB(426 bytes)

The line that is throwing the error is this:

Blob decryptedBlob = Crypto.decrypt('AES256', key, iv, cipherText);

22:39:11.4 (42576983)|METHOD_ENTRY|[40]||System.Crypto.decrypt(String, Blob, Blob, Blob)

I'm getting this error: Input length must be multiple of 16 when decrypting with padded. I'm unclear which input must be a multiple of 16. Is it the cipherText? The cipherText I'm using is generated with the Crypto.encrypt() method.

_________________________ EDIT on Day 2 ____________

Thanks @identigral for the links. Good to have the background understanding. My takeaway is that YES the input cipherText does need to be a multiple of 16 bytes.

For reference, here's the doc on the encryption they're using.

The roundtrip encrypt/decrypt works fine, but when I try to test it with a Postman call to the webhook endpoint, that's where I continue to get the error.

I'm taking my JSON data, encrypting it, then I was using EncodingUtil.base64Encode() to encode the encrypted blob, and then sending that string in the httpPost. That gives me the error. However, if I use EncodingUtil.convertToHex() and update my Apex to convertFromHex() it works.

//this is how I'm generating values to test in the httpPost
Blob exampleIv = Blob.valueOf('Example of IV123');
String encodedIv = EncodingUtil.base64Encode(exampleIv);
System.debug('🍓 encodedIv: ' + encodedIv);
List<SecurePay_Key__mdt> keys = [SELECT Private_Key__c FROM SecurePay_Key__mdt WHERE Label = 'Testing'];
String secretKeyString = keys[0].Private_Key__c;
System.debug('🔑 Found key in custom settings: ' + secretKeyString);
Blob key = EncodingUtil.base64Decode(secretKeyString);
Blob data = Blob.valueOf('{"order_id":6,"fields":{"receive_date":"2021-06-29 15:30:45","amount":2.79,"status":"Pending","first_name":"Lisa","last_name":"Simpson","email":"[email protected]","street_address":"23 Main St","supplemental_address_1":"Mosley Corner","city":"Springfield","state":"CA","postal_code":90210,"prefix":"Mr.","suffix":"I","affiliation":"Crusty\'s Clown College","uni":"uni y","heard_from":"Milhouse","is_test":1}}');
Blob encrypted = Crypto.encrypt('AES256', key, exampleIv, data);
String hexString = EncodingUtil.convertToHex(encrypted);
System.debug('🔒 hexString: ' + hexString);
String encodedString = EncodingUtil.base64Encode(encrypted);
System.debug('🔒 encodedString: ' + encodedString);

I just tried this on a whim because the error message said 'multiple of 16' and hexidecimal... The problem is I don't have control of the incoming httpPost, and they're just using base64Encoding.

Here's my Apex on the receiving end of the webhook:

global without sharing class InboundProcessor {
    //create a method to receive HTTP Post
    global static void handleInboundWebhook() {
        System.debug('🍔 Inbound Webhook');
        Boolean isTest = false;
        Blob iv = null;
        //create rest response
        RestResponse res = RestContext.response;
        //map the parameters
        Map<String, String> params = RestContext.request.params;
        //check for the encoded iv in the params
            System.debug('🔑 Found iv in params:' + params.get('iv'));
            //decode the iv
            iv = EncodingUtil.base64Decode(params.get('iv'));
            isTest = params.get('isTest') == 'true' ? true : false;
                System.debug('🔑 Test mode');
        //get the private key
        String label = isTest ? 'Testing' : 'Production';
        List<SecurePay_Key__mdt> keys = [SELECT Private_Key__c FROM SecurePay_Key__mdt WHERE Label = :label];
        if(keys.size() > 0){
            String secretKeyString = keys[0].Private_Key__c;
            System.debug('🔑 Found key in custom settings: ' + secretKeyString);
            Blob key = EncodingUtil.base64Decode(secretKeyString);
                //decode and decrypt the data using AES256
                String encryptedString = params.get('fields');
                Blob cipherText = EncodingUtil.base64Decode(encryptedString);
                // Blob cipherText = EncodingUtil.convertFromHex(encryptedString);
                Blob decryptedBlob = Crypto.decrypt('AES256', key, iv, cipherText);
                System.debug('🍒 Decrypted blob: ' + decryptedBlob.toString());
                String decodedString = decryptedBlob.toString();
                System.debug('🔑 Decrypted string: ' + decodedString);
                //fire the platform event

                // Set the response values for Web Service
                RestContext.response.statusCode = 200;
                RestContext.response.addHeader('Content-Type', 'text/html;charset=utf-8');
            } catch(Exception e){
                System.debug('❌ Error: ' + e.getMessage());
                System.debug('❌ Error: ' + e.getStackTraceString());
                res.statusCode = 500;
                res.responseBody = Blob.valueOf(e.getMessage());
            System.debug('❌ No key found');
            res.statusCode = 500;
            res.responseBody = Blob.valueOf('No key found');

1 Answer 1


I found the issue. The encrypt/decrypt works fine in exec anon, but not when I was testing it with an httpPost. I had to set my parameter values to ENCODE, then it works.

For example, when I entered my parameters & values in Postman, the encrypted string included plus signs: ... 7zZlYh+g/IPb0W .... When I set the parameter value to Encode, it changes it to ... 7zZlYh%2Bg/IPb0W ....

Then it works.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .