How to generate JWT token for the IOS devices

RajeshS45028713
Tera Contributor

Hi Guys,

ServiceNow supports RS256 for JWT signing, but Apple APNs requires ES256 using a .p8 private key.

Is there any way to generate an ES256 JWT in ServiceNow using the Apple .p8 key (or after converting it to JKS), without using a MID Server or external service?

Any help would be appreciated. Thanks!

1 REPLY 1

Pavan Srivastav
ServiceNow Employee

ServiceNow's native JWT framework (sn_auth.GlideJWTAPI and the JWT Provider infrastructure) is hardcoded and is simply not exposed as a signing algorithm in the platform's JWT utilities as of recent releases.

The .p8 file Apple provides is a raw PKCS#8 EC private key — and even if you converted it to JKS, the JKS would contain an EC key entry that ServiceNow's JWT signer would still refuse to use because the algorithm gap is the blocker, not the key format.

 

Manual JWT Construction via GlideTextEncoder + Java crypto (Best viable option)

ServiceNow Rhino/server-side JS has access to underlying Java classes. You can construct the ES256 JWT manually by reaching into java.security:

 
 
javascript
function generateApnsJWT(teamId, keyId, privateKeyPem) {
    
    // 1. Build header and payload
    var header = {
        alg: "ES256",
        kid: keyId
    };
    var payload = {
        iss: teamId,
        iat: Math.floor(new Date().getTime() / 1000)
    };

    // 2. Base64URL encode header and payload
    function base64UrlEncode(str) {
        var encoded = GlideStringUtil.base64Encode(str);
        return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
    }

    var headerEncoded  = base64UrlEncode(JSON.stringify(header));
    var payloadEncoded = base64UrlEncode(JSON.stringify(payload));
    var signingInput   = headerEncoded + '.' + payloadEncoded;

    // 3. Load the EC private key via Java
    var Base64     = java.util.Base64;
    var KeyFactory = java.security.KeyFactory;
    var Signature  = java.security.Signature;
    var PKCS8EncodedKeySpec = java.security.spec.PKCS8EncodedKeySpec;

    // Strip PEM headers from .p8 content
    var pemClean = privateKeyPem
        .replace('-----BEGIN PRIVATE KEY-----', '')
        .replace('-----END PRIVATE KEY-----', '')
        .replace(/\s+/g, '');

    var keyBytes   = Base64.getDecoder().decode(pemClean);
    var keySpec    = new PKCS8EncodedKeySpec(keyBytes);
    var keyFactory = KeyFactory.getInstance('EC');
    var privateKey = keyFactory.generatePrivate(keySpec);

    // 4. Sign with SHA256withECDSA
    var signer = Signature.getInstance('SHA256withECDSA');
    signer.initSign(privateKey);
    signer.update(new java.lang.String(signingInput).getBytes('UTF-8'));
    var derSignature = signer.sign();

    // 5. Convert DER-encoded signature to raw R||S format (required for JWT ES256)
    var rawSignature = derToJoseSignature(derSignature);
    var sigEncoded   = Base64.getUrlEncoder().withoutPadding().encodeToString(rawSignature);

    return signingInput + '.' + sigEncoded;
}

// DER → JOSE (R||S) conversion — critical step Apple requires
function derToJoseSignature(derBytes) {
    // DER structure: 0x30 [total-len] 0x02 [r-len] [r] 0x02 [s-len] [s]
    var der    = Java.from(derBytes); // convert to JS array
    var offset = 3;                   // skip 0x30, length, 0x02
    var rLen   = der[offset++];
    var r      = der.slice(offset, offset + rLen);
    offset    += rLen + 1;            // skip 0x02
    var sLen   = der[offset++];
    var s      = der.slice(offset, offset + sLen);

    // Pad or trim to exactly 32 bytes each
    function to32Bytes(arr) {
        while (arr.length > 32) arr = arr.slice(1); // trim leading zero padding
        while (arr.length < 32) arr.unshift(0);     // left-pad if short
        return arr;
    }

    var result = to32Bytes(r).concat(to32Bytes(s));
    return Java.to(result, 'byte[]');
}

⚠️ The DER → JOSE conversion step is the most commonly missed part. Java's SHA256withECDSA outputs DER-encoded signatures, but JWT ES256 requires the raw concatenated R||S format. Skipping this will produce a JWT that looks valid but Apple will always reject.