Getting Started : Authentication : HTTP Signature Authentication
 
HTTP Signature Authentication
All requests to the CyberSource REST API must be authenticated. This section explains how to use headers in the request. Before you begin, you will need three pieces of information from the CyberSource Business Center.
Table 3 Required Information from the Business Center
Information
Description
Serial number/ key ID
The serial number of the signing certificate/key pair. Obtain this in the Business Center’s Key Management area. For more information, see Creating a Shared Secret Key for HTTP Signature.
Shared Secret Key
Obtain this in the Business Center’s Key Management area. For more information, see Creating a Shared Secret Key for HTTP Signature.
Merchant ID
This is the ID of the merchant account that generated the shared secret key in the Business Center.
Signing a POST Request
Let’s start by taking a look at the end result, and then we’ll break it down piece by piece. Below is an example of a payment request. Headers are in bold font and signature parameters are in bold and italic font. Note that the Signature heading contains a signature parameter. Extra spaces have been added for readability.
Example POST Request
 
POST https://api.cybersource.com/pts/v2/payments
HTTP/1.1
Content-Type: application/json

v
-c-merchant-id: merchant123
Host: api.cybersource.com
Date: Wed, 25 Dec 2017 00:23:05 GMT
Digest: SHA-256=bena9bhB3Jy4uPvfu1tAC0uN8AuzzM+xjqmDwR5//EA=

Signature: keyid="4853015631630181645627", algorithm="SHA256withRSA", headers="host date (request-target) digest v-c-merchant-id", signature="q+j2M0fIQ1lIJJnjVtSISR0IZMTnury5m+XqZDNvCsHeF4WxhdOotqIIqqzEUoXMDcwPXVOUNJYXBBn1TaWLSisCO9ZIvVZghuJV3VwW1xqg9Rtyu6rxDZ1fLv+GMo4gUEV9y3sJq7cfJ0HZRN05ud/FRZYTeMlVWC5hXE2WRqhbtPg="
 
v-c-merchant-id
Required. Contains the merchant ID for the account that generated the shared secret key in the Business Center. See "Creating a Shared Secret Key for HTTP Signature," page 9.
Example:
v-c-merchant-id: merchant123
Host
The CyberSource environment server to which the request is sent. Use the Sandbox environment for testing and Production environment for processing real transactions.
Sandbox Host Name
apitest.cybersource.com
Production Host Name
api.cybersource.com
Date
Date and time the message originated, using GMT format, as defined by RFC7231.
Example:
Date: Wed, 25 Dec 2017 00:23:05 GMT
Code Snippet
String gmtDateTime = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneId.of("GMT")));
Digest
HMAC-SHA256 hash of the HTTP request’s payload(messageBody), which is Base64- encoded and normalized as a Digest HTTP header. You must concatenate SHA-256=” with the digest string, as shown below.
Example
 
messageBody =
  {
    "paymentInformation": {
      "card": {
        "expirationYear": "2031",
        "number": "4111111111111111",
        "securityCode": "123",
        "expirationMonth": "12",
        "type": "002"
    }
  }
digestString = BASE64( HMAC-SHA256 ( messageBody));
Digest: “SHA-256=“ + digestString;
 
Code Snippet
 
#Creating the SHA256 String
MessageDigest hashString = MessageDigest.getInstance("SHA-256");
byte[] digestBytes = hashString.digest(messageBody.getBytes());
#Creating the BASE64 String
String digetString = Base64.getEncoder().encodeToString(digestBytes);
#Creating the Digest
String Digest = "SHA-256="+ digestString;
 
Signature
The Signature header contains various parameters that must be submitted in order. Note that one of the parameters is called signature. Be careful not to confuse the header with its parameters. Let’s take a look at the full signature header before examining its parameters.
 
Signature: keyid="4853015631630181645627", algorithm="HmacSHA256", headers="host date (request-target) digest v-c-merchant-id", signature="q+j2M0fIQ1lIJJnjVtSISR0IZMTnury5m+XqZDNvCsHeF4WxhdOotqIIqqzEUoXMDcwPXVOUNJYXBBn1TaWLSisCO9ZIvVZghuJV3VwW1xqg9Rtyu6rxDZ1fLv+GMo4gUEV9y3sJq7cfJ0HZRN05ud/FRZYTeMlVWC5hXE2WRqhbtPg="
 
keyid
The serial number of the signing certificate/key pair. Obtain this in the Business Center’s Key Management area. For more information, see Creating a Shared Secret Key for HTTP Signature.
algorithm
The signature algorithm used. The value should be HmacSHA256.
headers
Contains the headers that are submitted in the HTTP request, separated by spaces. Headers include:
host
date
request-target—an HTTP method followed by a resource.
(request-target): method path
For example:
(request-target): post /pts/v2/payments
digest
v-c-merchant-id
Signature
The Base64-encoded signature of the headers listed above. The example below contains line breaks for readability.
 
signature = BASE64 ( host: apitest.cybersource.com
date: Sat, 8 Jul 2017 01:08:05 GMT
(request-target): post /pts/v2/payments
digest: SHA-256=/P58nNyRHCicPrDhJW8niEDMSnYCMRsITm17INuaq6E=
v-c-merchant-id: restmerch_acct )
 
Signing a GET Request
Let’s start by taking a look at the end result, and then we’ll break it down piece by piece. Below is an example of a request for the details of a single payment. Headers are in bold font and signature components are in bold and italic font. Note that the Signature heading contains a signature parameter. Extra spaces have been added for readability.
Example GET Request
 
GET https://api.cybersource.com/pts/v2/payments/98347878329748239
HTTP/1.1
Content-Type: application/json

v-c-merchant-id: merchant123
Host: api.cybersource.com
Date: Wed, 25 Dec 2017 00:23:05 GMT

Signature: keyid="4853015631630181645627", algorithm="SHA256withRSA", headers="host date (request-target) v-c-merchant-id", signature="q+j2M0fIQ1lIJJnjVtSISR0IZMTnury5m+XqZDNvCsHeF4WxhdOotqIIqqzEUoXMDcwPXVOUNJYXBBn2F333F51TaWLSisCO9ZIvVZghuJV3VwW1xqg9Rtyu6rxDZ1fLv+GMo4gUEV9y3sJq7cfJ0HZRN05ud/FRZYTeMlVWC5hXE2WRqhbtPg="
 
Code Snippet
The Shared Secret Key should be Base64-encoded and used to sign the signature parameter.
 
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(“YOUR SHARED SECRET KEY”), "HmacSHA256");
HttpSignatureHeader httpSignatureHeader =
HttpSignature.createHttpSignatureHeaders(messageBody, map, secretKey);
 
Note
‘map’ above is the HashMap of all the five headers discussed below.
 
v-c-merchant-id
Required. Contains the merchant ID for the account that generated the shared secret key in the Business Center. See "Creating a Shared Secret Key for HTTP Signature," page 9.
Example:
v-c-merchant-id: merchant123
Date
Date and time the message originated, using GMT format, as defined by RFC7231.
Example:
Date: Wed, 25 Dec 2017 00:23:05 GMT
Code Snippet
String gmtDateTime = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneId.of("GMT")));
Host
The CyberSource environment server to which the request is sent. Use the Sandbox environment for testing and Production environment for processing real transactions.
Sandbox Host Name
apitest.cybersource.com
Production Host Name
api.cybersource.com
Code Snippet
 
#Creating the SHA256 String
MessageDigest hashString = MessageDigest.getInstance("SHA-256");
byte[] digestBytes = hashString.digest(messageBody.getBytes());
#Creating the BASE64 String
String digetString = Base64.getEncoder().encodeToString(digestBytes);
#Creating the Digest
String Digest = "SHA-256="+ digestString;
 
Signature
The Signature header contains various parameters that must be submitted in order. Note that one of the parameters is called signature. Be careful not to confuse the header with its parameter. Let’s take a look at the full signature header before examining its parameters.
 
Signature: keyid="4853015631630181645627", algorithm="HmacSHA256", headers="host date (request-target) digest v-c-merchant-id", signature="q+j2M0fIQ1lIJJnjVtSISR0IZMTnury5m+XqZDNvCsHeF4WxhdOotqIIqqzEUoXMDcwPXVOUNJYXBBn1TaWLSisCO9ZIvVZghuJV3VwW1xqg9Rtyu6rxDZ1fLv+GMo4gUEV9y3sJq7cfJ0HZRN05ud/FRZYTeMlVWC5hXE2WRqhbtPg="
 
keyid
The serial number of the signing certificate/key pair. Obtain this in the Business Center’s Key Management area. For more information, see Creating a Shared Secret Key for HTTP Signature.
algorithm
The signature algorithm used. The value should be HmacSHA256.
headers
Contains the headers that are submitted in the HTTP request, separated by spaces. Headers include:
host
date
request-target—Request-target is an HTTP method followed by a resource.
(request-target): method path
For example:
(request-target): post /pts/v2/payments
digest
v-c-merchant-id
Signature
The Base64-encoded signature of the headers listed above. The example below contains line breaks for readability.
 
signature = BASE64 ( host: apitest.cybersource.com
date: Sat, 8 Jul 2017 01:08:05 GMT
(request-target): get /pts/v2/payments/98347878329748239
v-c-merchant-id: restmerch_acct )
 
Code Examples
Adding Headers
Example 1 Adding HTTP Headers
HashMap map = new HashMap();
map.put("keyid", merchantConfig.getKeyId());
map.put("host", merchantConfig.getRequestHost());
map.put("v-c-merchant-id", merchantId);
map.put("(request-target)", merchantConfig.getRequestTarget());
String hdrRequestDigest = " digest";
map.put("headers", "host date (request-target)" + hdrRequestDigest + " " + "v-c-merchant-id");
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(merchantConfig.getSecretKey()), "HmacSHA256");
HttpSignatureHeader httpSignatureHeader = HttpSignature.createHttpSignatureHeaders(requestBody, map, secretKey);
Connection connection = Connection.getInstance(merchantConfig, logger);
connection.setHeader("Content-Type", "application/json");
connection.setHeader("date", (String)httpSignatureHeader.getHeaders().get("date"));
connection.setHeader("digest", (String)httpSignatureHeader.getHeaders().get("digest"));
connection.setHeader("host", (String)httpSignatureHeader.getHeaders().get("host"));
connection.setHeader("v-c-merchant-id", (String)httpSignatureHeader.getHeaders().get("v-c-merchant-id"));
connection.setHeader("signature", httpSignatureHeader.getSignatureHeader());
logger.log("REQUEST ", "Signed headers... " + connection.getHeaderMap().toString());
return connection.post(requestBody);
Creating a Signature Header
Example 2 Creating a Signature Header
public static HttpSignatureHeader createHttpSignatureHeaders(String messageBody, Map<String, String> headers, SecretKey secretKey) {
HashMap parsed = new HashMap();
headers.forEach((k, v) -> {
parsed.put(k.toLowerCase().trim(), v);
});
String signatureString = prepareSignatureCreationString(messageBody, parsed);
Mac aKeyId = Mac.getInstance("HmacSHA256");
aKeyId.init(secretKey);
aKeyId.update(signatureString.getBytes());
byte[] aHeaders = aKeyId.doFinal();
String base64EncodedSignature = Base64.getEncoder().encodeToString(aHeaders);

parsed.put("algorithm", "HmacSHA256");
String aKeyId1 = (String)parsed.get("keyid");
String aHeaders1 = (String)parsed.get("headers");
StringBuilder signatureHeaderValue = new StringBuilder();
signatureHeaderValue.append("keyid=\"" + aKeyId1 + "\"");
signatureHeaderValue.append(", algorithm=\"HmacSHA256\"");
signatureHeaderValue.append(", headers=\"" + aHeaders1 + "\"");
signatureHeaderValue.append(", signature=\"" + base64EncodedSignature + "\"");
return new HttpSignatureHeader(parsed, signatureHeaderValue.toString());
} else {
LOGGER.error("Null or empty signature String value");
return null;
}
 
private static String prepareSignatureCreationString(String messageBody, Map<String, String> parsed) {
String keyid = (String)parsed.get("keyid");
String headers = (String)parsed.get("headers");
String host = (String)parsed.get("host");
if(keyid != null && !keyid.isEmpty() && headers != null && !headers.isEmpty() && host != null && !host.isEmpty()) {
gmtDateTime = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneId.of("GMT")));
parsed.put("date", gmtDateTime);

if(isNotEmpty(messageBody)) {
String bluePrint;
try {
MessageDigest signatureString = MessageDigest.getInstance("SHA-256");
byte[] digestBytes = signatureString.digest(messageBody.getBytes());
bluePrint = Base64.getEncoder().encodeToString(digestBytes);
} catch (NoSuchAlgorithmException var12) {
LOGGER.error("HttpSignature:createHttpSignatureHeaders - Cannot create digest");
return null;
}

parsed.put("digest", "SHA-256=" + bluePrint);
}
}
String[] splitHeader = headers.split(" ");
signatureStringer signatureString = new signatureStringer();
String[] splitHeader2 = splitHeader;
for(int headerLoop = 0; headerLoop < splitHeader.length; ++headerLoop) {
String s = splitHeader2[headerLoop];
if(!parsed.containsKey(s)) {
LOGGER.error("HttpSignature:createHttpSignatureHeaders - Missing passed or calculated headers");
return null;
}
 
signatureString.append('\n');
signatureString.append(s);
signatureString.append(": ");
signatureString.append((String)parsed.get(s));
}
 
signatureString.delete(0, 1);
return signatureString.toString();
}