Using WebHooks to verify the authenticity of transactions enables you to receive notifications about transaction events. In theory, anyone can call your WebHook endpoint and send transaction event data. To make sure that a transaction event occurred and that the request came from Cybersource, it is good practice to verify that the signature embedded in a WebHook request is authentic. Cybersource signs every request using a private key that is based on asymmetric cryptography. You can verify the signature in a request by using a public key that is loaded securely from the downloadable certificate.
Verifying a Request
  1. Download the certificate using the described API endpoint. To avoid making repeated remote calls, the recommendation is to cache the endpoint.
  2. Extract the expiry date and public key from the certificate.
  3. Verify the expiry date.
  4. Extract the JWT token from the WebHook request.
  5. Verify the signature of the request using the public key acquired in step 2.
  6. Extract the
    claims value.
  7. Calculate a SHA-256 hash of the request body.
  8. Compare the hash value with the
    claims value. The values must be equal in an authentic (non-tampered) request.
Downloading the Certificate
Request this API endpoint to download the certificate:
{ -----BEGIN CERTIFICATE----- (encrypted information) -----END CERTIFICATE----- }
Extracting Information from the Certificate
To read information in the certificate, such as the signing authority or expiration (expiry) date, use this command:
openssl x509 -in certificate.pfx -text
Or, use this Java code:
import java.security.cert.X509Certificate; FileInputStream file = new FileInputStream("PathToCertificate"); CertificateFactory factory = CertificateFactory.getInstance("X.509"); X509Certificate certificate = (X509Certificate)factory.generateCertificate(file); PublicKey publicKey = certificate.getPublicKey(); Date expirationDate = certificate.getNotAfter(); Date startDate = certificate.getNotBefore();
Extracting the JWT Token
Every request includes an authorization header that contains the JWT token:
Authorization: Bearer <token>
. The recommendation is to use an external library for parsing and verifying the JWT token. The token structure adheres to the JWT standard. Note that the Claims
field in the token contains an SHA-256 hash of the request body, which is used to verify that the body content was not modified.
Header: { "kid": "<uuid>", "alg": "RS256", "typ": "JWT" } Claims: { "iat": <timestamp>, "iss": "payworks", "digest": "<digest>", "digestAlgorithm": "SHA-256" } Signature: RS256Hash(base64Encode(header) + “.” + base64Encode(claims), private_key)