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.