May 02, 2025
In many Salesforce implementations, data security is a top priority. Whether you work with sensitive customer information or internal business secrets, it is necessary to protect data on rest. Salesforce provides strong equipment to handle encryption, and in this blog, we dive into a practical, custom-built example using trigger, Apex crypto methods, and digital signature.
Before implementing the encryption and digital signature logic, let’s first set up the required custom object Test__c with its fields, and create a self-signed certificate in Salesforce named For_Encryption to be used for signing and verifying data.
Test__c
(Salesforce will automatically append __c
to the name for custom objects).
Now that the custom object is created, we need to add the following fields:
Test__c
object.Repeat the same steps to create another Long Text Area field called "Description2", with the API Name Description2__c. This will hold the encrypted and signed data.
To digitally sign data in Apex using the Crypto.signWithCertificate
method, you need a certificate stored in Salesforce. Follow these steps to create a self-signed certificate:
For Encryption
For_Encryption
(this is the API name used in your Apex class).
We want to automatically encrypt or decrypt a field (Description1__c
) on a custom object (Test__c
) based on the value of a checkbox field (Check_Box__c
). If the checkbox is checked, the field should be encrypted and signed. If it's unchecked, we want to decrypt and verify the signature before restoring the original data.
trigger TestTrigger on Test__c (before update) {
if(Trigger.isBefore) {
if(Trigger.isUpdate) {
List<Test__c> testsNew = Trigger.New;
List<Test__c> testsOld = Trigger.Old;
if(testsNew[0].Check_Box__c != testsOld[0].Check_Box__c) {
if(testsNew[0].Check_Box__c == true) {
TestHandlerException.onCheck(testsNew[0]);
}
else {
TestHandlerException.onUncheck(testsNew[0]);
}
}
}
}
}
This before update
trigger watches for changes to the Check_Box__c
field. If a user checks or unchecks it:
Checked → encrypt the data in Description1__c
and store the encrypted content in Description2__c with signature
.
Unchecked → decrypt the data back into Description1__c
after signature verification.
public class TestHandlerException extends Exception {
static String algorithmName = 'AES128';
static String CertName = 'For_Encryption';
static String signAlgorithmName = 'RSA';
public static void onCheck(Test__c testRecord) {
try {
Blob data = Blob.valueOf(testRecord.Description1__c);
Blob key = Crypto.generateAesKey(128);
Blob encryptedData = Crypto.encryptWithManagedIV(algorithmName, key, data);
Blob signedEncryptedData = Crypto.signWithCertificate(signAlgorithmName, encryptedData, CertName);
String base64Data = EncodingUtil.base64Encode(encryptedData);
String base64Signed = EncodingUtil.base64Encode(signedEncryptedData);
String base64Key = EncodingUtil.base64Encode(key);
Blob specialChar = Blob.valueOf('!@#');
String base64SpecialChar = EncodingUtil.base64Encode(specialChar);
Integer length = testRecord.Description1__c.length();
testRecord.Description1__c = testRecord.Description1__c.subString(0,3) + '**********' + testRecord.Description1__c.subString(length-3,length);
testRecord.Description2__c = base64Data + base64SpecialChar + base64Signed + base64SpecialChar + base64Key;
}
catch(Exception e) {
testRecord.addError('Error while Encryption.');
}
}
public static void onUncheck(Test__c testRecord) {
try {
String encryptedData = testRecord.Description2__c;
Blob specialChar = Blob.valueOf('!@#');
String base64SpecialChar = EncodingUtil.base64Encode(specialChar);
List<String> splittedData = encryptedData.split(base64SpecialChar);
Blob data = EncodingUtil.base64Decode(splittedData[0]);
Blob signedKey = EncodingUtil.base64Decode(splittedData[1]);
Blob key = EncodingUtil.base64Decode(splittedData[2]);
Boolean verified = false;
verified = Crypto.verify(signAlgorithmName, data, signedKey, CertName);
if(verified) {
testRecord.Description1__c = Crypto.decryptWithManagedIV(algorithmName, key, data).toString();
testRecord.Description2__c = '';
}
else {
throw new TestHandlerException('Error While Verifying Data With Certificate.');
}
}
catch(Exception e) {
testRecord.addError(e);
}
}
}
What onCheck() method does:
Description1__c
into a binary Blob
.Crypto.encryptWithManagedIV
.For_Encryption
.!@#
) between the encoded parts.Description1__c
for privacy (shows only the first and last 3 characters).Description2__c
.
What onUncheck() method does:
Description2__c
using the encoded !@#
separator.Blob
.Description1__c
.Output: