World Check One API : JAVA Program for GET request to /v2/groups: 401 Unauthorized Error

Hi All,

We are trying to call the WorldCheckOne API from the SAP, where we need to convert the XML to JSON and post the data to WorldCheckOne API.

For which, I have written a program to make a GET request to /v2/groups , I am getting HTTP 401 error. Not exactly sure what is causing the error, could you please check and let me know what is wrong in the below code:


FYI. But from postman it is working fine with the same API key and Secret.


package HMACSignatureAuthentication;


import com.sap.aii.mapping.api.*;

import org.json.*;

import java.io.*;

import java.text.SimpleDateFormat;

import java.time.ZonedDateTime;

import java.time.format.DateTimeFormatter;

import java.util.*;

import java.net.HttpURLConnection;

import java.net.URI;

import java.net.URL;

import javax.crypto.Mac;

import javax.crypto.spec.SecretKeySpec;

import java.security.InvalidKeyException;

import java.security.NoSuchAlgorithmException;


import static java.lang.String.format;

import static java.time.ZoneOffset.UTC;

import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;

import org.apache.commons.net.ntp.NTPUDPClient;

import java.net.InetAddress;


public class APICallWithHMACAuthentication extends AbstractTransformation {


private static final String HMAC_SHA_256 = "HmacSHA256";

private static final String CONTENT_TYPE_PREFIX = "application/json";

private static final String SIGNATURE_HEADERS_WITHOUT_CONTENT = "(request-target) host date";

private static final String SIGNATURE_HEADERS_WITH_CONTENT = "(request-target) host date content-type content-length";


private List<String> array_nodes = new ArrayList<>();

private boolean isSapEnvironment = false;


private static final String PROTOCOL = "https";

private static final String GATEWAY_HOST = "api-worldcheck.refinitiv.com";

private static final String GATEWAY_URL = "/v2/";


public static void main(String[] args) {



APICallWithHMACAuthentication authInstance = new APICallWithHMACAuthentication();

authInstance.isSapEnvironment = false;


try {

String keyId = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX";

String secretKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

String method = "groups"; // "cases/screeningRequest"; // groups

String requestMethod = "GET"; // GET

String sampleXml = "";

String result = authInstance.transformStandalone(secretKey, GATEWAY_HOST, method, requestMethod, sampleXml,

keyId);

System.out.println("Transformed Output: \n" + result);


} catch (Exception e) {

System.err.println("Error: " + e.getMessage());

e.printStackTrace();

}

}


public String transformStandalone(String secretKey, String host, String method, String requestMethod,

String inputXml, String keyId) throws Exception {

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

InputStream inputStream = new ByteArrayInputStream(inputXml.getBytes());

performTransformation(secretKey, host, method, requestMethod, inputStream, outputStream, keyId);

return outputStream.toString("UTF-8");

}


public void transform(TransformationInput transformationInput, TransformationOutput transformationOutput)

throws StreamTransformationException {

this.isSapEnvironment = true;

try {

APICallWithHMACAuthentication authInstance = new APICallWithHMACAuthentication();

String secretKey = transformationInput.getInputParameters().getString("Secret_Key");

String host = transformationInput.getInputParameters().getString("Host");

String method = transformationInput.getInputParameters().getString("Method");

String requestMethod = transformationInput.getInputParameters().getString("Request_Method");

String sampleXml = "";

String keyId = "";


InputStream inputStream = transformationInput.getInputPayload().getInputStream();

OutputStream outputStream = transformationOutput.getOutputPayload().getOutputStream();


authInstance.transformStandalone(secretKey, GATEWAY_HOST, method, requestMethod, sampleXml, keyId);


} catch (Exception e) {

if (isSapEnvironment && getTrace() != null) {

getTrace().addDebugMessage(e.getMessage());

}

throw new StreamTransformationException(e.toString());

}

}


public void performTransformation(String secretKey, String host, String method, String requestMethod,

InputStream inputStream, OutputStream outputStream, String keyId) throws Exception {


BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

StringBuilder sourceXml = new StringBuilder();

String line;


while ((line = br.readLine()) != null) {

sourceXml.append(line).append("\n");

}

br.close();


URI uri = new URI(PROTOCOL, null, GATEWAY_HOST, -1, GATEWAY_URL + method, null, null);

Map<String, String> authHeaders;

String jsonPayload = "";


System.out.println("URI: " + uri.toString());

System.out.println("sourceXML: " + sourceXml);


// Check if XML payload is empty

if (sourceXml.toString().trim().isEmpty()) {

// XML payload is empty, set empty JSON string

System.out.println("No XML payload provided. Skipping JSON transformation.");

// jsonPayload = "";

} else {

// XML payload exists, perform JSON transformation

JSONObject xmlJSONObj = XML.toJSONObject(sourceXml.toString());

array_nodes.add("arraynode");

xmlJSONObj = handleJSONData(xmlJSONObj);


// Attempt to remove root node if there is only one top-level key

Iterator<String> keys = xmlJSONObj.keys();

if (xmlJSONObj.length() == 1 && keys.hasNext()) {

String rootKey = keys.next(); // Get the root key

Object innerObject = xmlJSONObj.get(rootKey);


// Check if the inner object is a JSONObject (not an array or simple type)

if (innerObject instanceof JSONObject) {

xmlJSONObj = (JSONObject) innerObject; // Strip away the root by assigning inner JSON object

}

}


jsonPayload = xmlJSONObj.toString(4).replaceAll("\"xmlns:ns0\":\"your namespace\",", "")

.replaceAll("\"ns0:Message Type Name\":", "").trim();


}


String jsonBase64 = Base64.getEncoder().encodeToString(jsonPayload.getBytes());

System.out.println(jsonBase64);


authHeaders = generateAuthHeaders(keyId, secretKey, requestMethod, uri, "application/json", jsonPayload);


// Call API with either transformed JSON or empty payload

//postToAPI(uri, jsonBase64, authHeaders, requestMethod, outputStream);

postToAPI(uri, jsonPayload, authHeaders, requestMethod, outputStream);

}

/**

* Generates a {@link Map} with required authorization headers (Date, Authorization, Content-Type, Content-Length).

*

* @param apiKey the user's API key

* @param apiSecret the user's API secret

* @param httpMethod the HTTP method of a request (e.g. 'get', 'options', 'head', 'post', 'put', 'patch',

* or 'delete')

* @param uri the {@link URI} to send the request to (e.g. 'https://www.example.com/resources')

* @param payload payload/body of the request

* @return {@link Map} with all required headers for authorization (Date, Authorization, Content-Type,

* Content-Length)

*/


private Map<String, String> generateAuthHeaders(String apiKey, String apiSecret, String httpMethod, URI uri,

String contentType, String payload) {


Map<String, String> authHeaders = new HashMap<>();


Date now = new Date();

// Format the date to "EEE, dd MMM yyyy HH:mm:ss z"

SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");

dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); // Set time zone to GMT

// Print the formatted date

String date = dateFormat.format(now);


StringBuilder dataToSign = new StringBuilder(300).append("(request-target): ")

.append(httpMethod.toLowerCase()).append(' ')

.append(uri.getPath())

.append("\nhost: ").append(uri.getHost())

.append("\ndate: ").append(date);


validateContentType(contentType, payload);


if (payload != null) {

int contentLength = payload.length();

dataToSign.append("\ncontent-type: ").append(contentType)

.append("\ncontent-length: ").append(contentLength)

.append('\n').append(payload);

authHeaders.put("Content-Type", contentType);

authHeaders.put("Content-Length", String.valueOf(contentLength));

}

System.out.println(dataToSign);

System.out.println(payload);



String headers = payload == null ? SIGNATURE_HEADERS_WITHOUT_CONTENT : SIGNATURE_HEADERS_WITH_CONTENT;

String signature = signDataWithHmacSha256(apiSecret, dataToSign.toString());


String authorization = "Signature keyId=\"" + apiKey + "\",algorithm=\"hmac-sha256\",headers=\"" + headers

+ "\",signature=\"" + signature + '"';


authHeaders.put("Date", date);

authHeaders.put("Authorization", authorization);


return authHeaders;

}


private void validateContentType(String contentType, String payload) {

if (contentType != null && !contentType.startsWith(CONTENT_TYPE_PREFIX)) {

throw new IllegalArgumentException("Unsupported content type " + contentType);

}

if (contentType != null && payload == null) {

throw new IllegalArgumentException("The request payload(body) has not been provided");

}

if (contentType == null && payload != null) {

throw new IllegalArgumentException("The content type of request payload(body) has not been provided");

}

}


private String signDataWithHmacSha256(String apiSecret, String dataToSign) {

try {

Mac mac = Mac.getInstance(HMAC_SHA_256);

mac.init(new SecretKeySpec(apiSecret.getBytes(), HMAC_SHA_256));

byte[] hmac = mac.doFinal(dataToSign.getBytes());

return Base64.getEncoder().encodeToString(hmac);

} catch (NoSuchAlgorithmException | IllegalArgumentException | InvalidKeyException e) {

throw new RuntimeException(format("Unable to sign user: %s with data: %n%s", apiSecret, dataToSign), e);

}

}


private void postToAPI(URI uri, String payload, Map<String, String> authHeaders, String requestMethod,

OutputStream outputStream) throws Exception {

HttpURLConnection connection = null;

try {

// Open connection

connection = (HttpURLConnection) uri.toURL().openConnection();

connection.setRequestMethod(requestMethod.toUpperCase());

connection.setDoOutput(true);


System.out.println(requestMethod.toUpperCase() + " " + uri.toString());


// Set request headers

for (Map.Entry<String, String> entry : authHeaders.entrySet()) {

connection.setRequestProperty(entry.getKey(), entry.getValue());

System.out.println(entry.getKey() + ": " + entry.getValue());

}


System.out.println(payload);


// Write payload to output stream

try (OutputStream os = connection.getOutputStream()) {

os.write(payload.getBytes("UTF-8"));

os.flush();

}


// Get response code

int responseCode = connection.getResponseCode();

System.out.println("Response Code: " + responseCode);


// Check for successful response code (200-299)

if (responseCode >= 200 && responseCode < 300) {

try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {

String inputLine;

StringBuilder response = new StringBuilder();

while ((inputLine = in.readLine()) != null) {

response.append(inputLine);

}

// Write response to output stream

outputStream.write(response.toString().getBytes("UTF-8"));

}

} else {

// Handle non-successful response

throw new IOException(

"HTTP error code: " + responseCode + ", Response message: " + connection.getResponseMessage());

}


} catch (IOException e) {

// Handle IOException (network issues, read/write errors)

System.err.println("IOException occurred: " + e.getMessage());

throw e; // Re-throwing the exception to be handled by the caller

} catch (Exception e) {

// Handle any other exceptions that might occur

System.err.println("Unexpected exception occurred: " + e.getMessage());

throw e; // Re-throwing the exception to be handled by the caller

} finally {

if (connection != null) {

connection.disconnect(); // Ensure the connection is closed

}

}

}


public JSONObject handleJSONData(JSONObject jsonObj) {

try {

String[] keys = (String[]) jsonObj.keySet().toArray(new String[0]);


for (String key : keys) {

if (array_nodes.contains(key)) {

jsonObj = forceToJSONArray(jsonObj, key);

}

if (jsonObj.get(key) instanceof JSONArray) {

JSONArray jsonArray = jsonObj.getJSONArray(key);

for (int i = 0; i < jsonArray.length(); i++) {

jsonArray.put(i, handleJSONData(jsonArray.getJSONObject(i)));

}

jsonObj.put(key, jsonArray);

} else if (jsonObj.get(key) instanceof JSONObject) {

jsonObj.put(key, handleJSONData(jsonObj.getJSONObject(key)));

} else if (jsonObj.get(key) instanceof Number) {

jsonObj.put(key, jsonObj.get(key).toString());

}

}

} catch (Exception e) {

if (isSapEnvironment && getTrace() != null) {

getTrace().addDebugMessage("Exception while updating payload: " + e.getMessage());

} else {

System.err.println("Exception while updating payload: " + e.getMessage());

}

}

return jsonObj;

}


public static JSONObject forceToJSONArray(JSONObject jsonObj, String key) throws JSONException {

Object obj = jsonObj.opt(key);

if (obj instanceof JSONObject) {

JSONArray jsonArray = new JSONArray();

jsonArray.put((JSONObject) obj);

jsonObj.put(key, jsonArray);

} else if (obj != null) {

JSONArray jsonArray = new JSONArray();

for (int i = 0; i < ((JSONArray) obj).length(); i++) {

jsonArray.put(((JSONArray) obj).getJSONObject(i));

}

jsonObj.put(key, jsonArray);

}

return jsonObj;

}

}


Thanks

Sai


Answers