Getting Started : Authentication : JSON Web Token Authentication
 
JSON Web Token Authentication
After creating the P12 certificate (see "Creating a P12 Certificate for JSON Web Token," page 8), you must convert it to PEM format. First, you’ll need to set up your environment.
Environment Setup
To encrypt using AES 256, Use the ‘Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files’
-- local_policy.jar and US_export_policy.jar.
You can download these files from oracle website:
http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
Once you have these jars, replace the your_java_installation_directory/jre/lib/security jar files with these.
Converting the Certificate to PEM Format using OpenSSL
When you run the OpenSSL tool, the tool prompts for your password, which is your Merchant ID. By default, your Merchant ID is also the name of the file.
The output of the tool will be text in PEM format. PEM defines each section of the file with “BEGIN” and “END” markers which denote the start and end of the objects within the file.
Example OpenSSL Conversion
 
bash-4.1$ openssl pkcs12 -in MID.p12 -out MID.pem
Enter Import Password:
MAC verified OK
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
bash-4.1$ cat MID.pem
Bag Attributes
localKeyID: 01
friendlyName: serialNumber=4654139977230172554798,CN=MID
subject=/CN=MID/serialNumber=4654139977230172554798
issuer=/CN=CyberSourceCertAuth
-----BEGIN CERTIFICATE-----
MIIB4TCCAUqgAwIBAgIWNDY1NDEzOTk3NzIzMDE3MjU1NDc5ODANBgkqhkiG9w0B
AQUFADAeMRwwGgYDVQQDDBNDeWJlclNvdXJjZUNlcnRBdXRoMB4XDTE2MDYwODE5
MjYzN1oXDTE5MDYwODE5MjYzN1owOjEXMBUGA1UEAwwOSmFzb25FYXRvbkNvcnAx
HzAdBgNVBAUTFjQ2NTQxMzk5NzcyMzAxNzI1NTQ3OTgwgZ8wDQYJKoZIhvcNAQEB
BQADgY0AMIGJAoGBALJKm1u6AcDNZQljcAtaG5II+FVefBtQF+xETFhCK0EJWfLh
XUNxTZIDHbZsf11IzRfs10w5sXviv5Z3vtCg8C1rJKoUuoJ5EJsWaEeBVKL6kZ4K
KlOm5559KTPYBfwCP73Hbu2qMGxfUu01ZUsOyKcSEFY3rxH6IQ6Z//qMZY5tAgMB
AAEwDQYJKoZIhvcNAQEFBQADgYEAj09Zlljg7pO+A0OUGZrNlPDDoSXjrGV/fyvn
fXXE598ldLZZ52nnEx+jJ4SQF0KguTP7OQWhV+8I6SKVePUDkOm4KBDEPmcRvjA9
0pgLVO7kwKX0r5QX0dTgR/GvDJyGi7znOu6Pr16WIU1Qh9jdqEF1wRxpS7SN6Uf1
u4KNO1c=
-----END CERTIFICATE-----
Bag Attributes
friendlyName: serialNumber=4654139978710172554798,CN=CyberSource_SJC_US
subject=/CN=CyberSource_SJC_US/serialNumber=4654139978710172554798
issuer=/CN=CyberSourceCertAuth
-----BEGIN CERTIFICATE-----
MIIB5TCCAU6gAwIBAgIWNDY1NDEzOTk3ODcxMDE3MjU1NDc5ODANBgkqhkiG9w0B
AQUFADAeMRwwGgYDVQQDDBNDeWJlclNvdXJjZUNlcnRBdXRoMB4XDTE2MDYwODE5
MjYzN1oXDTE5MDYwODE5MjYzN1owPjEbMBkGA1UEAwwSQ3liZXJTb3VyY2VfU0pD
X1VTMR8wHQYDVQQFExY0NjU0MTM5OTc4NzEwMTcyNTU0Nzk4MIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQCfQlT5TzBwYVDeqgpueMOas6Rc3hbzz/1IPWbqy2yi
8WCORXeQpLOHnBzz9JeefJgat2MzhEqC/yel8rsqIWFGBSP/4vShlIUgq+yIjlEA
8XPvniPb6EFs2Vyhqrz5o90+8CSf+mmJU9QH/l0HPjas7JevM6M4tjsCD3q7gNbU
SQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJlAkv3Dw3u9o96W3f1JUC0Q+Z1NWDer
t2M+bvkXNBEiIi5dc4iRu4v/Axj+4OHBgt83eWXb2Htq5Sdj7XnF9aAS3q7ONUlS
59w/jPIm8e3ca9Zc/B9tAkyp8MNHrRTrGS3egDCroNwQnlkCD+gm6MYbvphNyIBg
3AnyvBvhr4Q0
-----END CERTIFICATE-----
Bag Attributes
friendlyName: CN=CyberSourceCertAuth
subject=/CN=CyberSourceCertAuth
issuer=/CN=CyberSourceCertAuth
-----BEGIN CERTIFICATE-----
MIIBxTCCAS6gAwIBAgIWMzI2OTA0NjIyMzgwMDE2ODYyNjIyMDANBgkqhkiG9w0B
AQUFADAeMRwwGgYDVQQDDBNDeWJlclNvdXJjZUNlcnRBdXRoMB4XDTEyMDExODE2
MzcwMloXDTIyMDExODE2MzcwMlowHjEcMBoGA1UEAwwTQ3liZXJTb3VyY2VDZXJ0
QXV0aDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuVMamKpEz+KaQk1ZujUY
ms6xO89gIsl/hm5hMf75Gc5GsOSGYD1UI34NZfrBW9mSoySPgXYBH0pq82X6pa7W
hKVZHzK/0AA7hnbKbrshyvHfv2lNjpHqRAmzwfA9gc9J3crTDXPq+tsQJjRpPhLd
M3+Paw6AxFtCeX4oX959mU8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQA0Do9T1RqU
 
Converting the Certificate to PEM Format using a Java API Conversion
You can use Bouncy Castle JCE to perform a Java API conversion of the P12 certificate into the PEM format.
Example Java API Conversion
 
private static X509Certificate initializeCertificate(MerchantConfig merchantConfig) throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, UnrecoverableEntryException, ConfigException {
if(merchantConfig != null && merchantConfig.getKeyAlias() != null && merchantConfig.getKeyFile() != null) {
KeyStore merchantKeyStore = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
merchantKeyStore.load(new FileInputStream(merchantConfig.getKeyFile()), merchantConfig.getKeyPassword().toCharArray());
String merchantKeyAlias = null;
Enumeration enumKeyStore = merchantKeyStore.aliases();
while(enumKeyStore.hasMoreElements()) {
merchantKeyAlias = (String)enumKeyStore.nextElement();
if(merchantKeyAlias.contains(merchantConfig.getKeyAlias())) {
break;
}
}
PrivateKeyEntry keyEntry = (PrivateKeyEntry)merchantKeyStore.getEntry(merchantKeyAlias, new PasswordProtection(merchantConfig.getKeyPassword().toCharArray()));
return (X509Certificate)keyEntry.getCertificate();
} else {
throw new ConfigException("merchant config fields missing: key alias, key file");
}
}
private static RSAPrivateKey initializePrivateKey(MerchantConfig merchantConfig) throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, UnrecoverableEntryException, ConfigException {
if(merchantConfig != null && merchantConfig.getKeyAlias() != null && merchantConfig.getKeyFile() != null) {
KeyStore merchantKeyStore = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
merchantKeyStore.load(new FileInputStream(merchantConfig.getKeyFile()), merchantConfig.getKeyPassword().toCharArray());
String merchantKeyAlias = null;
Enumeration enumKeyStore = merchantKeyStore.aliases();
while(enumKeyStore.hasMoreElements()) {
merchantKeyAlias = (String)enumKeyStore.nextElement();
if(merchantKeyAlias.contains(merchantConfig.getKeyAlias())) {
break;
}
}
PrivateKeyEntry keyEntry = (PrivateKeyEntry)merchantKeyStore.getEntry(merchantKeyAlias, new PasswordProtection(merchantConfig.getKeyPassword().toCharArray()));
return (RSAPrivateKey)keyEntry.getPrivateKey();
} else {
throw new ConfigException("merchant config fields missing: key alias, key file");
}
}
 
HTTP Headers
Table 2 HTTP Headers for JSON Web Token 
Header Name
Description
Example
JWT header section
kid
Serial Number or thumbprint for merchant’s alias/CN name, corresponding to the key (.p12) used to digitally sign the JWS.
Optional.
5015325034050177096965
x5c
The "x5c" (X.509 certificate chain) Header Parameter contains the X.509 public key certificate or certificate chain corresponding to the key(.p12) used to digitally sign the JWS.
Required.
MIICZTCCAc6gAwIBAgIWNTAxNTMyNTAzNDA1MDE3NzA5Njk2NTANBgkqhkiG9w0B
AQsFADAeMRwwGgYDVQQDDBNDeWJlclNvdXJjZUNlcnRBdXRoMB4XDTE3MDczMTIw
MjE0M1oXDTE5MDczMTIwMjE0M1owOjEXMBUGA1UEAwwOcmVzdG1lcmNoX2FjY3Qx
HzAdBgNVBAUTFjUwMTUzMjUwMzQwNTAxNzcwOTY5NjUwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQC+BtdMcokaKwTes9O+snqjWsehO0/Y4CJrwo4cdGCa
fRO6ows3mC1xmpSYttTKIdeGR1qOYpTSeY9uJh2cCfayMKXmhQf4d5v8pNDnmYBM
GBwU1wWjlYsMLFbcPFWjdPtKlou2RIWZ566YWIbWCBf+G4dHkA0D7Csp3FT3lnUN
2me4lJklysfqp1X8q3JDqVIQFVAduamhWCYMRS5Kg8sKwMjldGQUby88S+MOwcCA
3HOHT64barvPId2ExqAqvfyBCPAR49V6/ywDVsH9SyHewg121ckMcA9g55tIqdsy
HGRZN9fs20kMyqEP78L4cxfisuVQC8knlfZtdcqVAY7FAgMBAAEwDQYJKoZIhvcN
AQELBQADgYEAZ9NWN0BNVEhtVLqldmEfMRqJCqfzFNq1UyQEu3CJOjPhN9dYq8Jl
aD6d1mRpybeaclvHN/F/06RjZW9zujlDFE3X0qanU3Rkj7nStiDUEmj0F35Ew2ek
4VezUXnZ/SMLvWEA6DG2sjSFCCuIot3mLJ3lI4AQSQSBSazhQec75Rk=
alg
Identifier of the hashing algorithm.
Required.
RS256
v-c-merchant-id
Merchant ID assigned in the CyberSource Business Center.
Required.
v-c-merchant-id: restmercht_id
JWT payload section
iat
The date and time that the message was originated. Date can be in any for timezone.
HTTP-date format is defined by RFC7231:
String gmtDateTime = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneId.of("GMT")));
Required.
iat: Thru, 15 June 2017 08:12:31 GMT
Digest
SHA256 encryption of the payload that is Base64 encoded.
Required.
example_payload:
{
"profileId": "11111111-1111-1111-1111-111111111111",
"encryptionType": "RsaOaep256"
}
 
SHA256_hash_of_example_payload = 2b4fee10da8c5e1feaad32b014021e079fe4afcf06af223004af944011a7cb65c
# The hash has Base64 encoded Digest header in RFC3230 defined format of "Digest: HASH-ALG=BASE64(SHA56_hash_of_example_payload)“ =
tP7hDajF4f6q0ysBQCHgef5K/PBq8iMASvlEARp8tl=
 
Digest: tP7hDajF4f6q0ysBQCHgef5K/PBq8iMASvlEARp8tl=
 
Code Snippet:
MessageDigest signatureString = MessageDigest.getInstance("SHA-256"); byte[] digestBytes = signatureString.digest(messageBody.getBytes()); String bluePrint = Base64.getEncoder().encodeToString(digestBytes);
digestAlgorithm
Signature algorithm used. SHA256 if an asymmetric key is used.
Required.
digestAlgorithm: SHA-256
Signature and token
Signature
The header and the payload created is BASE64 Encoded. Join the resulting encoded strings together with a period (.) in between them. In our pseudo code, this joined string is assigned to data. To get the JWT signature, the data string is hashed with RS256, with the secret key using the hashing algorithm specified in the JWT header.
Required.
// signature algorithm
 
data = base64urlEncode( header ) + “.” + base64urlEncode( payload )
signature = RS256Hash( data, secret ) ;
JWT Token
With All three components, We can create
JWT token = header.payload.signature
Combine the header and payload and signature with periods (.) separating them. We use the base64url encoded versions of the header and of the payload, and the signature.
JWT Token = base64url(header) + “.” + base64url(payload) + “.” + base64url(Signature)
 
// header eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
 
// payload eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ
 
// signature
-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
 
// JWT Token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzc
Encoding and Hashing the Digest
 
if(requestBody != null && !requestBody.isEmpty()) {
MessageDigest jwtBody = MessageDigest.getInstance("SHA-256");
byte[] Headers = jwtBody.digest(requestBody.getBytes());
e = Base64.getEncoder().encodeToString(Headers);
}
 
Preparing the Payload
 
String jwtBody = "{\n \"digest\":\"" + e + "\",\n \"digestAlgorithm\":\"SHA-256\",\n \"iat\":\"" + DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneId.of("GMT"))) + "\"\n} \n\n";
 
HashMap customHeaders = new HashMap();
customHeaders.put(v-c-merchant-id, merchantConfig.getMerchantID());
 
String jwsSignatureValue = sign(jwtBody, rsaPrivateKey, x509Certificate, customHeaders);
 
Generating the JWT Token – Header, Payload and Signature
 
private sign(String content, PrivateKey privateKey, X509Certificate x509Certificate, Map<String, ? extends Object> customHeaders) {
if(!this.isNullOrEmpty(content) && x509Certificate != null && privateKey != null) {
String serialNumber = null;
String serialNumberPrefix = "SERIALNUMBER=";
String principal = x509Certificate.getSubjectDN().getName().toUpperCase();
int beg = principal.indexOf(serialNumberPrefix);
if(beg >= 0) {
int x5cBase64List = principal.indexOf(",", beg);
if(x5cBase64List == -1) {
x5cBase64List = principal.length();
}

serialNumber = principal.substring(beg + serialNumberPrefix.length(), x5cBase64List);
} else {
serialNumber = x509Certificate.getSerialNumber().toString();
}

ArrayList x5cBase64List1 = new ArrayList();

try {
x5cBase64List1.add(Base64.encode(x509Certificate.getEncoded()));
} catch (CertificateEncodingException var16) {
logger.error("can\'t signAndEncrypt the payload", var16);
return null;
}

RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)privateKey;
Payload payload = new Payload(content);
JWSHeader jwsHeader = (new com.nimbusds.jose.JWSHeader.Builder(JWSAlgorithm.RS256)).customParams(customHeaders).keyID(serialNumber).x509CertChain(x5cBase64List1).build();
JWSObject jwsObject = new JWSObject(jwsHeader, payload);

try {
RSASSASigner joseException = new RSASSASigner(rsaPrivateKey);
jwsObject.sign(joseException);
if(!jwsObject.getState().equals(com.nimbusds.jose.JWSObject.State.SIGNED)) {
logger.error("Payload signing failed.");
return null;
} else {
return jwsObject;
}
} catch (JOSEException var15) {
logger.error("can\'t signAndEncrypt the payload", var15);
return null;
}
} else {
logger.error("empty or null content or Private key or public certificate is null");
return null;
}
}