how to use encryption keys to encrypt a string

riddhijain32
Mega Explorer

We have been given a public key and we need to encrypt a given field and a private key to decrypt the filed. The user having the specific access role can only be able to view the decrypted value of the field in the form.

We know of GlideEncrypter api which uses 3DES method to encrypt by default.

How do we use custom encryption here, assuming that the encryption keys are RSA keys ?

2 REPLIES 2

Naveen20
ServiceNow Employee

GlideEncrypter won't work here since it's symmetric (3DES), and you need asymmetric RSA (public key encrypts, private key decrypts). There are two practical approaches in ServiceNow.

Approach 1: GlideCertificateEncryption (Recommended)

This is ServiceNow's built-in API designed for certificate-based (asymmetric) encryption.

Step 1 — Store RSA keys in sys_certificate

Navigate to System Definition → Certificates and create two records: one for the public key/certificate (PEM format) and one for the private key. Note down both sys_id values.

Step 2 — Encrypt on insert/update (Before Insert/Update Business Rule)

(function executeRule(current, previous) {
    if (current.u_sensitive_field.changes()) {
        var plainText = current.getValue('u_sensitive_field');
        var gce = new GlideCertificateEncryption();
        var publicCertSysId = 'SYS_ID_OF_PUBLIC_CERT';
        var encrypted = gce.generateMac(publicCertSysId, plainText);
        // Store the encrypted (Base64) value back
        current.setValue('u_sensitive_field', encrypted);
    }
})(current, previous);

Note: The exact method names on GlideCertificateEncryption can vary across releases. Check your instance's API docs — common methods include generateMac, encrypt, and their decryption counterparts. If those aren't available in your release, Approach 2 is the fallback.

Step 3 — Decrypt for display (role-gated Display Business Rule)

(function executeRule(current, previous) {
    if (gs.hasRole('x_custom.decrypt_viewer')) {
        var gce = new GlideCertificateEncryption();
        var privateKeySysId = 'SYS_ID_OF_PRIVATE_KEY';
        var encryptedVal = current.getValue('u_sensitive_field');
        var decrypted = gce.decrypt(privateKeySysId, encryptedVal);
        current.u_sensitive_field.setDisplayValue(decrypted);
    }
})(current, previous);

Approach 2: Java Crypto Packages (Global Scope only)

If you need full control over the RSA algorithm, padding, and key format, you can use Java's javax.crypto.Cipher directly in a global-scope Script Include. This won't work in scoped apps due to Java package restrictions.

var RSACrypto = Class.create();
RSACrypto.prototype = {

    initialize: function() {
        this.ALGORITHM = 'RSA/ECB/PKCS1Padding';
    },

    encryptWithPublicKey: function(plainText, publicKeyPEM) {
        try {
            // Strip PEM headers and decode Base64
            var keyStr = publicKeyPEM
                .replace('-----BEGIN PUBLIC KEY-----', '')
                .replace('-----END PUBLIC KEY-----', '')
                .replaceAll('\\n', '');

            var Base64   = Packages.java.util.Base64;
            var keyBytes = Base64.getDecoder().decode(keyStr);

            // Build the RSA public key object
            var KeyFactory          = Packages.java.security.KeyFactory;
            var X509EncodedKeySpec  = Packages.java.security.spec.X509EncodedKeySpec;
            var keySpec = new X509EncodedKeySpec(keyBytes);
            var pubKey  = KeyFactory.getInstance('RSA').generatePublic(keySpec);

            // Encrypt
            var Cipher = Packages.javax.crypto.Cipher;
            var cipher = Cipher.getInstance(this.ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);

            var plainBytes     = new Packages.java.lang.String(plainText).getBytes('UTF-8');
            var encryptedBytes = cipher.doFinal(plainBytes);

            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (e) {
            gs.error('RSACrypto.encrypt failed: ' + e.getMessage());
            return null;
        }
    },

    decryptWithPrivateKey: function(encryptedB64, privateKeyPEM) {
        try {
            var keyStr = privateKeyPEM
                .replace('-----BEGIN PRIVATE KEY-----', '')
                .replace('-----END PRIVATE KEY-----', '')
                .replaceAll('\\n', '');

            var Base64   = Packages.java.util.Base64;
            var keyBytes = Base64.getDecoder().decode(keyStr);

            // Build the RSA private key object (PKCS#8 format)
            var KeyFactory           = Packages.java.security.KeyFactory;
            var PKCS8EncodedKeySpec  = Packages.java.security.spec.PKCS8EncodedKeySpec;
            var keySpec = new PKCS8EncodedKeySpec(keyBytes);
            var privKey = KeyFactory.getInstance('RSA').generatePrivate(keySpec);

            // Decrypt
            var Cipher = Packages.javax.crypto.Cipher;
            var cipher = Cipher.getInstance(this.ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privKey);

            var encryptedBytes = Base64.getDecoder().decode(encryptedB64);
            var decryptedBytes = cipher.doFinal(encryptedBytes);

            return new Packages.java.lang.String(decryptedBytes, 'UTF-8') + '';
        } catch (e) {
            gs.error('RSACrypto.decrypt failed: ' + e.getMessage());
            return null;
        }
    },

    type: 'RSACrypto'
};

Usage in a Business Rule (encrypt on save):

var rsa = new RSACrypto();
var pubKeyPEM = gs.getProperty('x_app.rsa_public_key'); // store PEM in sys_properties
var encrypted = rsa.encryptWithPublicKey(current.getValue('u_sensitive_field'), pubKeyPEM);
current.setValue('u_sensitive_field', encrypted);

Usage in a Display Business Rule (decrypt for authorized roles):

if (gs.hasRole('x_custom.decrypt_viewer')) {
    var rsa = new RSACrypto();
    var privKeyPEM = gs.getProperty('x_app.rsa_private_key');
    var decrypted  = rsa.decryptWithPrivateKey(current.getValue('u_sensitive_field'), privKeyPEM);
    current.u_sensitive_field.setDisplayValue(decrypted);
}

Key considerations

RSA size limitation — RSA can only encrypt data smaller than the key size minus padding overhead (e.g., ~245 bytes for a 2048-bit key with PKCS1 padding). If your field value could be larger, use a hybrid approach: generate a random AES session key, encrypt the data with AES, then encrypt the AES key with RSA. Store both together.

Key storage security — Storing the private key PEM in sys_properties is convenient but not ideal. The sys_certificate table is better because it has tighter ACLs out of the box. You can also use GlideEncrypter to double-wrap the private key at rest.

Scoped app constraintPackages.java.* is only available in global scope. If you're building a scoped app, you'll need a global Script Include marked as accessible from scopes, or stick with Approach 1.

Field type choice — Use a field type of String (large enough) or Large String rather than the built-in Password2 type, since Password2 hooks into ServiceNow's own encryption context and would conflict with your custom RSA logic.

Tanushree Maiti
Kilo Patron

Hi @riddhijain32 

 

To Encrypt a string ,  use Password2 encryption with the Key Management Framework (KMF).

Password2 encryption with the Key Management Framework (KMF) 

 

Check this as well, Cannot decrypt my password2 any longer, now what?

 

Please mark this response as Helpful & Accept it as solution if it assisted you with your question.
Regards
Tanushree Maiti
ServiceNow Technical Architect
Linkedin: