Making an API Request
Implementation Guide
This page will help you to:
- Prepare an API request payload and perform payload encryption
- Generate and sign a client token (JWT)
- Steps required to process a response
Making an API Request
For each request, prepare the following:
- Prepares the plain request content in (JSON structure).
- Encrypts the HTTP request body using Bank's Public Key.
- Generates Digital Signature (Bearer Token).
- Sends HTTP Request.
- Processes the response message.
The following section contains an implementation guide with examples.
Overview on API Request Journey
public static void main(String[] args) throws Exception {
// 1. Prepare Request to HSBC API to HSBC API Services
System.out.println("----- #Start of API Sample Code Steps# -----");
HttpMethod httpMethod = POST; //Input ENUM. Value: POST, PUT, PATCH, GET, DELETE
ApiSchema apiSchema = EDGE; //Input ENUM. Value: EDGE, GTRF. "EDGE" for API Version >= 3.0.0. Otherwise, for previous version, input "GTRF".
System.out.println("-----#1. Start of loading Security Credentials-----");
String bankPgpPublicKeyFile = "src/main/resources/security-credentials/Trade-Finance-Release-Sept-23-Prod/hsbc-public.key"; //Input bank public key file location
String clientPgpPrivateKeyFile = "src/main/resources/security-credentials/Trade-Finance-Release-Sept-23-Prod/client-private.key";// Input client private key file location
String clientPrivateKeyPassphrase = "1password"; //Input passPhrase of your key
// 1.1 Preparing PGP Keys
System.out.println("#1.1. Preparing PGP Keys...");
// 1.1.1 Retrieves Bank PGP Public Keys
System.out.println("#1.1.1 Retrieving Bank PGP public keys...");
List<PGPPublicKey> bankPgpPublicKeys = getPublicKeys(bankPgpPublicKeyFile);
System.out.println("#1.1.2 Setting one of the Bank PGP Public Keys as encryption key...");
PGPPublicKey bankPgpPublicKey = getEncryptionKey(apiSchema, bankPgpPublicKeys);
// 1.1.2. Retrieves Client PGP Private Keys
System.out.println("#1.2.1 Retrieving Client PGP private keys...");
List<PGPPrivateKey> clientPgpPrivateKeys = getPrivateKeys(clientPgpPrivateKeyFile, clientPrivateKeyPassphrase);
System.out.println("#1.2.2 Setting one of the Client PGP Private Keys as signature key...");
PGPPrivateKey clientPgpPrivateKey = clientPgpPrivateKeys.get(0);
System.out.println("-----End of #1. loading Security Credentials-----\n");
// 2. Prepare Request Payload
System.out.println("-----Start of #2. Preparing the HTTP Request Payload...");
// 2.1 Prepare Request JSON String
String textToEncrypt = null;
if (httpMethod != GET) {
textToEncrypt = "{\"paramObjectKey001\":{\"paramKey001\": \"paramValue001\",\"paramKey002\": \"paramValue002\",...}}";
}
System.out.println("#2.1. Request Content JSON String: \n" + textToEncrypt);
HashMap<String, String> payloadWrappers = new HashMap<String, String>();
Boolean xHsbcCryptoSignature = false;
switch (apiSchema) {
case EDGE:
xHsbcCryptoSignature = true;
payloadWrappers.put("request", "encryptedRequestBase64");
payloadWrappers.put("response", "encryptedResponseBase64");
break;
case GTRF:
xHsbcCryptoSignature = false;
payloadWrappers.put("request", null);
payloadWrappers.put("response", null);
break;
}
// 2.2 Generate Request Payload
System.out.println("#2.2. Start of Generating Request Payload");
String requestPayload = generateRequestPayload(httpMethod, payloadWrappers, textToEncrypt, bankPgpPublicKey, clientPgpPrivateKey, xHsbcCryptoSignature);
System.out.println("-----End of #2. Preparing the HTTP Request Payload...\n");
// 3. Generate Authorization Token
System.out.println("-----Start of #3. Generating Authorization Token-----");
String clientProfileId = "TAAS000000001"; // API Profile ID Bank Assigned to you.
String oboProfileId = "customer001"; //Customer's ID on your platform. Input null if you are a Direct Client.
HashMap<String, Object> additionalTokenClaims = (HashMap<String, Object>) apiSettings.get("additionalTokenClaims");
HashMap<String, String> authTokens = generateAuthTokens(httpMethod, apiSchema, clientProfileId, oboProfileId, additionalTokenClaims, requestPayload, bankPgpPublicKey, clientPgpPrivateKey, xHsbcCryptoSignature);
System.out.println("-----End of of #3. Generating Authorization Token-----\n");
// 4. Generate Request
System.out.println("-----Start of #4. Executing API Request-----");
//4.1 Generate URI Parameters if required:
HashMap<String, String> requestUriParameters = new HashMap<String, String>() {
{
//put("paramKey001", "paramValue001"); //Input only if required
}
};
//4.2 Generate Request Headers
String clientHostingRegion = "SG"; // Your Platform or Business Operating Region, In ISO3166-alpha-2
HashMap<String, String> additionalRequestHeaders = new HashMap<String, String>() {
{
//put("headerKey001", "headerValue001"); //Input only if required
}
};
HashMap<String, String> requestHeaderJson = generateRequestHeaders(apiSchema, authorizationToken, clientHostingRegion, oboProfileId, additionalRequestHeaders);
//4.3 Send API Request and save the response as String
String apiEndpointUrl = "https://sandbox.corporate-api.hsbc.com/......"; //Input your target URL
String responseMessage = doRequest(httpMethod, apiEndpointUrl, requestUriParameters, requestHeaderJson, requestPayload);
System.out.println("-----End of #4. Executing API Request-----\n");
//5. Process Response Message from Bank and save the output as String
System.out.println("-----Start of #5 Start of Processing API Response-----");
String processedMessage = processResponseMessage(payloadWrappers, responseMessage, bankPgpPublicKeys, clientPgpPrivateKeys, xHsbcCryptoSignature);
System.out.println("-----End of #5 Start of Processing API Response-----\n");
System.out.println("----- #End of API Sample Code Steps# -----");
}
Step 1 - Specify API Request Operations and load security credentials
- Specifies Http Method and API Version
-
Steps
- Specify
HTTP Method
for your target API Endpoint (Enum values). - Specify API Version for your target API Endpoint.
- For API Version >= 3.0.0, Input Enum Value
EDGE
. - For API Version < 3.0.0, Input Enum Value
GTRF
.
- For API Version >= 3.0.0, Input Enum Value
JAVA Code Example:
HttpMethod httpMethod = POST; //Input ENUM. Value: POST, PUT, PATCH, GET, DELETE ApiSchema apiSchema = EDGE; //Input ENUM. Value: EDGE, GTRF. "EDGE" for API Version >= 3.0.0. Otherwise, for previous version, input "GTRF".
- Specify
- Specifies Key Path
-
Steps
- Specify String variable which points to the key path and name of PGP keys stored in your system.
- Specify String variable for your client private key passPhrase.
JAVA Code Example:
String bankPgpPublicKeyFile = "src/main/resources/security-credentials/Trade-Finance-Release-Sept-23-Prod/hsbc-public.key"; //Input bank public key file location String clientPgpPrivateKeyFile = "src/main/resources/security-credentials/Trade-Finance-Release-Sept-23-Prod/client-private.key";// Input client private key file location String clientPrivateKeyPassphrase = "1password"; //Input passPhrase of your key
- Loads Bank Public Key from File
-
Steps
- Call sample java method
getPublicKeys
which would take the above fileName string as and input and output a list ofPGPPublicKey
. - Declare
bankPgpPublicKey
viagetEncryptionKey
.
List<PGPPublicKey> bankPgpPublicKeys = getPublicKeys(bankPgpPublicKeyFile); PGPPublicKey bankPgpPublicKey = getEncryptionKey(apiSchema, bankPgpPublicKeys);
JAVA Code Example for reference method using via Bouncy Castle
public static List<PGPPublicKey> getPublicKeys(String fileName) { System.out.println(" #a. Retrieving Keys from: " + fileName); List<PGPPublicKey> pgpPublicKeys = new ArrayList<PGPPublicKey>(); try { InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName)); InputStream decoderStream = PGPUtil.getDecoderStream(inputStream); PGPPublicKeyRingCollection ringCollection = new PGPPublicKeyRingCollection(decoderStream, new JcaKeyFingerprintCalculator()); PGPPublicKey pgpPublicKey = null; System.out.println(" #b. IDs of keys retrieved: "); for (PGPPublicKeyRing ring : ringCollection) { for (Iterator<PGPPublicKey> iterator = ring.getPublicKeys(); iterator.hasNext(); ) { pgpPublicKey = iterator.next(); System.out.println(" " + Long.toHexString(pgpPublicKey.getKeyID()).toUpperCase()); pgpPublicKeys.add(pgpPublicKey); } } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (PGPException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return pgpPublicKeys; }
public static PGPPublicKey getEncryptionKey(String apiSchema, List<PGPPublicKey> pgpPublicKeys) { PGPPublicKey pgpPublicKey = null; switch (apiSchema) { case EDGE: pgpPublicKey = pgpPublicKeys.get(0); break; case GTRF: for (Iterator<PGPPublicKey> it = pgpPublicKeys.iterator(); it.hasNext(); ) { pgpPublicKey = it.next(); if (pgpPublicKey.isEncryptionKey() && !pgpPublicKey.isMasterKey()) { break; } } } System.out.println(" #a. ID of encryption key:\n " + Long.toHexString(pgpPublicKey.getKeyID()).toUpperCase()); return pgpPublicKey; }
- Call sample java method
- Loads Client Private Key from file
-
Steps
- Call method
getPrivateKeys
which would take the above fileName string as and input and output a list ofPGPPrivateKey
. - Declare
clientPgpPrivateKey
by using one from theList<PGPPrivateKey>
.
List<PGPPrivateKey> clientPgpPrivateKeys = getPrivateKeys(clientPgpPrivateKeyFile, clientPrivateKeyPassPhrase); PGPPrivateKey clientPgpPrivateKey = clientPgpPrivateKeys.get(0);
JAVA Code Example for reference method using via Bouncy Castle
public static List<PGPPrivateKey> getPrivateKeys(String fileName, String keyPassPhrase) { System.out.println(" #a. Retrieving Keys from: " + fileName); List<PGPPrivateKey> pgpPrivateKeys = new ArrayList<PGPPrivateKey>(); try { InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName)); InputStream decoderStream = PGPUtil.getDecoderStream(inputStream); PGPSecretKeyRingCollection ringCollection = new PGPSecretKeyRingCollection(decoderStream, new JcaKeyFingerprintCalculator()); PGPSecretKey pgpSecretKey = null; for (PGPSecretKeyRing ring : ringCollection) { for (Iterator<PGPSecretKey> iterator = ring.getSecretKeys(); iterator.hasNext(); ) { pgpSecretKey = iterator.next(); System.out.println(" #b. IDs of keys retrieved: "); pgpPrivateKey = pgpSecretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().build(keyPassPhrase.toCharArray())); System.out.println(" " + Long.toHexString(pgpPrivateKey.getKeyID()).toUpperCase()); pgpPrivateKeys.add(pgpPrivateKey); } } return pgpPrivateKeys; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (PGPException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return pgpPrivateKeys; }
- Call method
Step 2 - Preparing Request Payload
- Prepare Request JSON String
-
Steps
JSONObject requestJson = new JSONObject() { { put("data",new JSONObject(){ { put("paramKey001","paramValue001"); put("paramKey002","paramValue002"); } }); } }; String textToEncrypt = null; if (httpMethod != GET) { textToEncrypt = requestJson.toString(); //textToEncrypt = "{\"data\":{\"paramKey001\": \"paramValue001\",\"paramKey002\": \"paramValue002\",...}}"; }
- Encrypts and Generates HTTP Request Payload
-
Steps :
- Convert the
textToEncrypt
(JSON String prepared in Step 1) toByteArrayInputSteam
- Encrypt the above stream using bank public key.
- Optionally, compress the above encrypted data and sign it with client's private key.
- Base64 Encodes the above encrypted data.
- Wrap the Base64 Strings into JSON Format.
String textToEncrypt = null; if (httpMethod != GET) { textToEncrypt = requestJson.toString(); //textToEncrypt = "{\"data\":{\"paramKey001\": \"paramValue001\",\"paramKey002\": \"paramValue002\",...}}"; } System.out.println("#2.1. Request Content JSON String: \n" + textToEncrypt); HashMap
payloadWrappers = new HashMap ,>(); Boolean xHsbcCryptoSignature = false; switch (apiSchema) { case EDGE: xHsbcCryptoSignature = true; payloadWrappers.put("request", "encryptedRequestBase64"); payloadWrappers.put("response", "encryptedResponseBase64"); break; case GTRF: xHsbcCryptoSignature = false; payloadWrappers.put("request", null); payloadWrappers.put("response", null); break; } // 2.2 Generate Request Payload System.out.println("#2.2. Start of Generating Request Payload"); String requestPayload = generateRequestPayload(httpMethod, payloadWrappers, textToEncrypt, bankPgpPublicKey, clientPgpPrivateKey, xHsbcCryptoSignature); System.out.println("-----End of #2. Preparing the HTTP Request Payload...\n"); ,>JAVA Code Example for reference method
public static String generateRequestPayload(HttpMethod httpMethod, HashMap<String, String> payloadWrappers, String textToEncrypt, PGPPublicKey bankPgpPublicKey, PGPPrivateKey clientPgpPrivateKey, Boolean xHsbcCryptoSignature) { String requestPayload = null; switch (httpMethod) { case GET: requestPayload = ""; break; case POST: String bankPgpKeyId = Long.toHexString(bankPgpPublicKey.getKeyID()).toUpperCase(); String base64EncryptedString = encrypt(textToEncrypt, bankPgpPublicKey, clientPgpPrivateKey, xHsbcCryptoSignature); System.out.println(" #a. Request Payload: "); String payloadWrapper = payloadWrappers.get("request"); if (payloadWrapper == null) { System.out.println(" #a.1. API Schema: GTRF - Encrypted Base64 String WITHOUT payload wrapper, used for APIs versions < 3.0.0\n #c.2. Encryption Scheme: Bank public key encrypted WITHOUT Client Private Key Signed\n #c.3 Encryption Key Id (Bank PGP Public Key): " + bankPgpKeyId); requestPayload = base64EncryptedString; } else { System.out.println(" #a.1. API Schema: EDGE - Encrypted Base64 String WITH payload wrapper, it is used for API version >=3"); JSONObject requestPayloadJson = new JSONObject(); try { if (xHsbcCryptoSignature == true) { String clientPgpKeyId = Long.toHexString(clientPgpPrivateKey.getKeyID()).toUpperCase(); System.out.println(" #a.2. Encryption Scheme: Bank public key encrypted WITHOUT Client Private Key Signed\n #a.3.1 Encryption Key Id (Bank PGP Public Key): " + bankPgpKeyId + "\n #a.3.2 Signing Key Id (Client PGP Private Key): " + clientPgpKeyId); } else { System.out.println(" #a.2. Encryption Scheme: Bank public key encrypted WITHOUT Client Private Key Signature\n #a.3 Encryption Key Id (Bank PGP Public Key): " + bankPgpKeyId); } requestPayloadJson.put(payloadWrapper, base64EncryptedString); requestPayload = requestPayloadJson.toString(); } catch (JSONException e) { e.printStackTrace(); } } } System.out.println(" #a.4 Request Payload String: \n" + requestPayload + "\n"); return requestPayload; }
public static String encrypt(String textToEncrypt, PGPPublicKey bankPublicKey, PGPPrivateKey clientPrivateKey, boolean isVerifySignature) { if (StringUtils.isBlank(textToEncrypt)) { return ""; } ByteArrayOutputStream actualOutput = new ByteArrayOutputStream(); try { ByteArrayInputStream inputStream = new ByteArrayInputStream(textToEncrypt.getBytes()); OutputStream out = new ArmoredOutputStream(actualOutput); PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator( new JcePGPDataEncryptorBuilder(PGPEncryptedData.AES_256).setWithIntegrityPacket(true).setSecureRandom( new SecureRandom()) .setProvider(new BouncyCastleProvider())); encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(bankPublicKey).setProvider(new BouncyCastleProvider())); OutputStream encryptedOut = encGen.open(out, new byte[inputStream.available()]); PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator(); if (isVerifySignature == true) { PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP); OutputStream compressedData = comData.open(encryptedOut); PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder( clientPrivateKey.getPublicKeyPacket().getAlgorithm(), PGPUtil.SHA512).setProvider(new BouncyCastleProvider())); sGen.init(PGPSignature.BINARY_DOCUMENT, clientPrivateKey); Iterator<String> it = bankPublicKey.getUserIDs(); if (it.hasNext()) { PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); spGen.setSignerUserID(false, it.next()); sGen.setHashedSubpackets(spGen.generate()); } sGen.generateOnePassVersion(false).encode(compressedData); // bOut OutputStream lOut = lGen.open(compressedData, PGPLiteralData.BINARY, "Sample-Data", new Date(), new byte[inputStream.available()]); //bOut byte[] data = IOUtils.toByteArray(inputStream); lOut.write(data); sGen.update(data); lOut.close(); lGen.close(); sGen.generate().encode(compressedData); comData.close(); compressedData.close(); } else { OutputStream lOut = lGen.open(encryptedOut, PGPLiteralData.BINARY, "Sample-Data", new Date(), new byte[inputStream.available()]); //bOut byte[] data = IOUtils.toByteArray(inputStream); lOut.write(data); lOut.close(); lGen.close(); } encryptedOut.close(); encGen.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } catch (PGPException e) { e.printStackTrace(); } return Base64.toBase64String(actualOutput.toByteArray()); }
- Convert the
Step 3 - Generate and Sign the Authentication Token
- Authentication Token components
-
The bearer token HSBC processes to authenticate your request is in Json Web Token (JWT) Format.
The below table defines the fields to be provided in the JWT, all fields are mandatory.
Code Name Description In Http Header Example typ Token Type Type of Token - Always set as "JWT" JWT header - Authorization
- X-HSBC-Trade-Finance-Token
“JWT” kid Key Id Key ID of Client PGP Key which will be used to sign this token JWT header "900864F8C11EB743" alg Signing Algorithm Algorithm Used to Sign this token JWT header "PS256" ver Version Always set as 1.0 JWT header "1.0" jti JWT ID Unique ID to represent each API request's token. The format is UUID JWT payload "74760410-f963-11e8-b2a3-1bb26e1e5b69" iat Issue At Timestamp this toekn JWT payload 1565150776 sub Subject Who is sending this request. It will always be the Client Profile ID HSBC assigned to your platform. JWT payload Client/ PARTNER Profile ID aud Audience Who is receiving this request. It's a static value. JWT payload "baas" obo:sub On behalf of / subject This field is used to identify End Customer on your platform for this particular request. Required only if you are a partner of HSBC. JWT payload “obo” : { “sub” : "CUSTOMER001" } payload_hash Payload hash value Hash Value of the HTTP Request Payload. This is used by bank to verify your HTTP Request Payload are not tempered. JWT payload Authorization (HTTP Method = "POST" Only) "1dab559f1f4bf4f3ca..." payload_hash_alg Payload hash algorithm Algorithm Used to perform Payload Hashing JWT payload "RSASHA256" tradeAccounts Trade Accounts Trade Account Number JWT payload X-HSBC-Trade-Finance-Token
Required only for the for the following APIs:
- Bank Guarantee
- Buyer Loans
- Seller Loans
- TradePay
- Import Letters of Credit
- Import Colection
- Export Letters of Credit
- Export Collection
"LS0tLS1CRUdJTi...." - Generating Token
-
Steps to prepare the Token:
- Generate Hashmap for JWT header according to fields specified above.
- Specify the signing algorithm of this token.
- Generate Hashmap for JWT claims according to fields specified above.
- Specify payloadHashAlgorithm and generate the payloadHash with requestPayload as input .
- Convert Your (Client) PGP Private Key to RSA.
- Use JwtBuilder to generate the token.
String oboProfileId = "customer001"; //Customer's ID on your platform. Input null if you are a Direct Client. String token = generateAuthToken(httpMethod, apiSchema, clientProfileId, oboProfileId, requestPayload, clientPgpPrivateKey);
Code example of reference method
public static HashMap<String, String> generateAuthTokens(String httpMethod, String apiSchema, String clientProfileId, String onBehalfOf, HashMap<String, Object> additionalTokenClaims, String requestPayload, PGPPublicKey bankPgpPublicKey, PGPPrivateKey clientPgpPrivateKey, boolean xHsbcCryptoSignature) { try { System.out.println(" #a. Using HashMap to build token structure..."); HashMap<String, String> token = new HashMap<String, String>(); // header HashMap<String, Object> headers = new HashMap<String, Object>(); headers.put("kid", Long.toHexString(clientPgpPrivateKey.getKeyID()).toUpperCase()); headers.put("typ", "JWT"); headers.put("ver", "1.0"); headers.put("alg", TOKEN_ALG); // claims PrivateKey clientRsaPrivateKey = new JcaPGPKeyConverter().getPrivateKey(clientPgpPrivateKey); HashMap<String, Object> claims = new HashMap<String, Object>(); claims.put("jti", UUID.randomUUID().toString()); claims.put("iat", System.currentTimeMillis() / 1000); claims.put("sub", clientProfileId); //API Profile ID Bank Assigned to you during on-boarding if (apiSchema == (ApiSchema.Edge.SCHEMA_NAME)) { claims.put("aud", "baas"); // Obo, optional, only used when an API request is send on behalf of an End Customer if (onBehalfOf != null) { HashMap<String, Object> obo = new HashMap<String, Object>(); obo.put("sub", onBehalfOf); //End Customer ID, provided by your platform during on-boarding claims.put("obo", obo); } } else if (apiSchema == (ApiSchema.Gtrf.SCHEMA_NAME)) { claims.put("aud", "GTRF.MKT"); } //if (requestPayload != null) { if (httpMethod != HttpGet.METHOD_NAME) { String payloadHash = DatatypeConverter.printHexBinary(MessageDigest.getInstance(TOKEN_PAYLOAD_HASH_ALG).digest(requestPayload.getBytes())) .toLowerCase(); claims.put("payload_hash", payloadHash); claims.put("payload_hash_alg", clientRsaPrivateKey.getAlgorithm() + TOKEN_PAYLOAD_HASH_ALG.replace("-", "")); } // print token object printAuthToken(headers, claims); System.out.println(" #b. Building JWT and Signed by: " + clientRsaPrivateKey.toString()); JwtBuilder authorizationBuilder = Jwts.builder().setHeader(headers).setClaims(claims).signWith(clientRsaPrivateKey, SIGNATURE_ALG); //System.out.println(" #b. Building JWT and Signed by: " + Base64.getEncoder().encodeToString(pem.getEncoded())); //JwtBuilder jwtBuilder = Jwts.builder().setHeader(headers).setClaims(claims).signWith(pem, SIGNATURE_ALG); String authorization = authorizationBuilder.compact(); System.out.println(" #c. Generated Authorization Token:\nJWS " + authorization); if (apiSchema == (ApiSchema.Edge.SCHEMA_NAME)) { HashMap<String, Object> xHsbcTradeFinanceClaims = new HashMap<String, Object>() { { put("jti", claims.get("jti")); put("iat", claims.get("iat")); put("sub", claims.get("sub")); put("aud", claims.get("aud")); if (claims.containsKey("obo")) { put("obo", claims.get("obo")); } } }; for (Map.Entry<String, Object> entry : additionalTokenClaims.entrySet()) { if (entry.getKey() == "claimsNoEncrypt") { Map<String, Object> xHsbcTradeFinanceClaimsNoEncrypt = (Map<String, Object>) entry.getValue(); for (Map.Entry<String, Object> innerEntry : xHsbcTradeFinanceClaimsNoEncrypt.entrySet()) { xHsbcTradeFinanceClaims.put(innerEntry.getKey(), innerEntry.getValue()); } } if (entry.getKey() == "claimsToEncrypt") { Map<String, Object> xHsbcTradeFinanceClaimsToEncrypt = (Map<String, Object>) entry.getValue(); for (Map.Entry<String, Object> innerEntry : xHsbcTradeFinanceClaimsToEncrypt.entrySet()) { xHsbcTradeFinanceClaims.put(innerEntry.getKey(), encrypt(innerEntry.getValue().toString(), bankPgpPublicKey, clientPgpPrivateKey, xHsbcCryptoSignature)); } } } printAuthToken(headers, xHsbcTradeFinanceClaims); JwtBuilder xHsbcTradeFinanceBuilder = Jwts.builder().setHeader(headers).setClaims(xHsbcTradeFinanceClaims).signWith(clientRsaPrivateKey, SIGNATURE_ALG); String xHsbcTradeFinance = xHsbcTradeFinanceBuilder.compact(); System.out.println(" #c. Generated X-HSBC-Trade-Finance-Token:\nJWS " + xHsbcTradeFinance); token.put("xHsbcTradeFinance", xHsbcTradeFinance); } token.put("authorization", authorization); return token; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (PGPException e) { e.printStackTrace(); } return null; }
public static void printAuthToken(Map<String, Object> header, Map<String, Object> claims) { System.out.println(" #a.1. Token Headers:"); for (Map.Entry<String, Object> entry : header.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } System.out.println(); System.out.println(" #a.2. Token Claims:"); for (Map.Entry<String, Object> entry : claims.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } System.out.println(); }
- Generate Hashmap for JWT header according to fields specified above.
Step 4 - Sending HTTP Request
- Generates Request Headers
-
Steps:
- Optionally, declare a Hashmap and put additional request headers.
- Declare a Hashmap to generate complete request headers via generateRequestHeaders
public static final HashMap<String, String> additionalRequestHeaders = new HashMap<String, String>() { { put("additionalRequestHeaderKey001", "additionalRequestHeaderValue001"); put("additionalRequestHeaderKey002", "additionalRequestHeaderValue002"); ... } }; HashMap<String, String> requestHeaders = generateRequestHeaders(apiSchema, token, clientHostingRegion, oboProfileId, additionalRequestHeaders);
JAVA Code Example for reference method
public static HashMap<String, String> generateRequestHeaders(String apiSchema, String token, String clientHostingRegion, String oboProfileId, HashMap<String, String> additionalRequestHeaders) { String contentType; HashMapString, String> requestHeaders = new HashMapString, String>(); if (clientHostingRegion == "CN") { contentType = "text-plain"; } else { contentType = "application/json"; } switch (apiSchema) { case ("EDGE"): requestHeaders.put("Authorization", "JWS ".concat(token.get("authorization").toString())); requestHeaders.put("X-HSBC-Trade-Finance-Token", "JWS ".concat(token.get("xHsbcTradeFinance").toString())); requestHeaders.put("X-HSBC-countryCode", clientHostingRegion); requestHeaders.put("Content-Type", contentType); requestHeaders.put("X-HSBC-Request-Correlation-Id", String.valueOf(UUID.randomUUID())); if (httpMethod != HttpGet.METHOD_NAME) { requestHeaders.put("X-HSBC-Request-Idempotency-Key", requestId); } requestHeaders.put("X-HSBC-Crypto-Signature", Boolean.toString(xHsbcCryptoSignature)); requestHeaders.putAll(additionalRequestHeaders); break; case ("GTRF"): requestHeaders.put("Authorization", "JWS ".concat(token)); requestHeaders.put("CountryCode", clientHostingRegion); requestHeaders.put("Content-Type", contentType); requestHeaders.put("requestId", String.valueOf(UUID.randomUUID()).replace("-", "")); requestHeaders.put("requestTime", ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); requestHeaders.put("schemaVersion", "1.0.0"); requestHeaders.putAll(additionalRequestHeaders); break; } printHttpRequestHeaders(requestHeaders); //print request headers return requestHeaders; }
public static void printHttpRequestHeaders(HashMap
headers) { for (HashMap.Entry ,>entry : headers.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } System.out.println(); } ,> - Sends HTTP Request
-
Steps:
- Declares a String httpMethod for your corresponding HTTP Request Operations.
- Values can be "POST", "PUT", "PATCH", "GET", "DELETE".
- Declares a String for the corresponding API endpoint URL.
- Declares a Hashmap requestUriParameters and put request URI Parameters if any.
- Otherwise, declare an empty Hashmap.
- Declare a String responseMessage to save the output string from doRequest method.
String httpMethod = "POST", String apiEndpointUrl = "https://%API_ENDPOINT_URL%; HashMap <String, String> requestUriParameters = HashMap <String, String>() { { put("requestUriParameterKey001", "requestUriParameterValue001"); put("requestUriParameterKey002", "requestUriParameterValue002"); ... } } String responseMessage = doRequest(httpMethod, apiEndpointUrl, requestUriParameters, requestHeaders, requestPayload);
JAVA Code Example for reference method using org.apache.http
public static String doRequest(String httpMethod, String apiEndpointUrl, HashMap<String, String> requestUriParameters, HashMap<String, String> requestHeaderJson, String requestPayload) { System.out.println("-----#Preparing API Request#-----"); String textToDecrypt = null; HttpHost proxyHost = null; RequestConfig config = null; HttpGet getRequest = new HttpGet(apiEndpointUrl); HttpPost postRequest = new HttpPost(apiEndpointUrl); for (Map.Entry<String, String> entry : requestHeaderJson.entrySet()) { switch (httpMethod) { case ("GET"): getRequest.setHeader(entry.getKey(), entry.getValue()); break; case ("POST"): postRequest.setHeader(entry.getKey(), entry.getValue()); break; } } SSLContext sslContext = null; try { sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { return true; } }).build(); SSLConnectionSocketFactory sslcsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); CloseableHttpClient client; switch (ClientConfig.proxyMode) { case (0): config = RequestConfig.custom().build(); client = HttpClients.custom().setSSLSocketFactory(sslcsf).build(); break; case (1): proxyHost = new HttpHost(ClientConfig.proxyHost, ClientConfig.proxyPort); config = RequestConfig.custom().setProxy(proxyHost).build(); client = HttpClients.custom().setSSLSocketFactory(sslcsf).build(); break; case (2): proxyHost = new HttpHost(ClientConfig.proxyHost, ClientConfig.proxyPort); config = RequestConfig.custom().setProxy(proxyHost).build(); CredentialsProvider credsProvider = new BasicCredentialsProvider(); //Setting the credentials credsProvider.setCredentials(new AuthScope(ClientConfig.proxyHost, ClientConfig.proxyPort), new UsernamePasswordCredentials(ClientConfig.proxyUser, ClientConfig.proxyPassword)); client = HttpClients.custom().setSSLSocketFactory(sslcsf).setDefaultCredentialsProvider(credsProvider).build(); break; default: throw new IllegalStateException("Unexpected value: " + ClientConfig.proxyMode); } CloseableHttpResponse response = null; switch (httpMethod) { case ("GET"): getRequest.setConfig(config); URI uri = getRequestUriParameters(getRequest, requestUriParameters); getRequest.setURI(uri); System.out.println("#a. HTTP Request: " + getRequest.getRequestLine() + "\n#b. HTTP Headers:\n" + Arrays.toString(getRequest.getAllHeaders()) + "\n"); response = client.execute(getRequest); break; case ("POST"): postRequest.setConfig(config); System.out.println("#a. HTTP Request: " + postRequest.getRequestLine() + "\n#b. HTTP Headers:\n" + Arrays.toString(postRequest.getAllHeaders()) + "\n"); postRequest.setEntity(new StringEntity(requestPayload, "UTF-8")); response = client.execute(postRequest); break; } System.out.println("------ #Processing Response# -----\nHTTP Response Code: " + response.getStatusLine().getStatusCode()); HttpEntity entity = response.getEntity(); textToDecrypt = EntityUtils.toString(entity); return textToDecrypt; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; }
- Declares a String httpMethod for your corresponding HTTP Request Operations.
Processing a Response
Responses are returned with the relating HTTP code and an encrypted response message.
- Processing a Response
-
Steps to process a response:
String decryptedMessage = processResponseMessage(payloadWrappers, responseMessage, bankPgpPublicKeys, clientPgpPrivateKeys, isVerifySignature);
- For a successful response, the response provided will be encrypted and Base64 encoded wrapped in Json Structure
- Typically it looks like
{"encryptedResponseBase64":"eyJ2ZXIiOiIxL...."}
- In some earlier versions of API, there are no payload wrappers. It looks like
eyJ2ZXIiOiIxL....
- Typically it looks like
- Extract the Base64 representation and decodes it.
- Decrypt the decoded payload with your private key.
- The decrypted message looks like
{ "data": { "paramKey001": "paramValue001", "paramKey002": "paramValue002", ... } }
- The decrypted message looks like
JAVA Code Example for reference method:
public static String processResponseMessage(HashMap<String, String> payloadWrappers, String responseMessage, List<PGPPublicKey> bankPgpPublicKeys, List<PGPPrivateKey> clientPgpPrivateKeys, Boolean isVerifySignature) { String payloadWrapper = payloadWrappers.get("response"); String textToDecrypt = null; String processedMessage = null; try { boolean isValidJson = isValidJson(responseMessage); JSONObject responseMessageJson = new JSONObject(responseMessage); if (responseMessageJson.has(payloadWrapper)) { textToDecrypt = responseMessageJson.getString(payloadWrapper); if (isVerifySignature == true) { System.out.println(" #a.1. Encryption Scheme: Client Public Key Encrypted WITH Bank Private Key Signed\n#a.2. Response Message Schema: EDGE\nEncrypted Base64 String WITH payload wrapper, it is used for API version >=3:"); } else { System.out.println(" #a.1. Encryption Scheme: Client Public Key Encrypted WITHOUT Bank Private Key Signed\n#a.2. Response Message Schema: EDGE\nEncrypted Base64 String WITH payload wrapper, it is used for API version >=3:"); } } else { processedMessage = responseMessageJson.toString(); System.out.println(" #b. Processed Message: \n" + responseMessageJson.toString(4)); return processedMessage; } } catch (JSONException jsonException) { try { boolean isBase64 = MessageLevel.isBase64(responseMessage); textToDecrypt = responseMessage; } catch (IllegalArgumentException e) { return responseMessage; } } processedMessage = decrypt(textToDecrypt, bankPgpPublicKeys, clientPgpPrivateKeys, isVerifySignature); try { System.out.println(" #b. Processed Message: \n" + new JSONObject(processedMessage).toString(4)); } catch (JSONException e) { e.printStackTrace(); } return processedMessage; }
public static String decrypt(String textToDecrypt, List<PGPPublicKey> bankPublicKeys, List<PGPPrivateKey> clientPrivateKeys, boolean isVerifySignature) { if (StringUtils.isBlank(textToDecrypt)) { return ""; } try { ByteArrayOutputStream actualOutput = new ByteArrayOutputStream(); String epicStrNew = new String(Base64.decode(textToDecrypt)); JcaKeyFingerprintCalculator keyFingerPrintCalculator = new JcaKeyFingerprintCalculator(); BufferedInputStream dataStream = new BufferedInputStream(IOUtils.toInputStream(epicStrNew)); InputStream decoderStream = PGPUtil.getDecoderStream(dataStream); PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(decoderStream, keyFingerPrintCalculator); PGPEncryptedDataList pgpEncryptedDataList = (PGPEncryptedDataList) pgpObjectFactory.nextObject(); System.out.println("pgpEncryptedDataList" + pgpEncryptedDataList); if (pgpEncryptedDataList != null) { // find the matching public key encrypted data packet. PGPPublicKeyEncryptedData pgpPublicKeyEncryptedData = null; // build decryptor factory PublicKeyDataDecryptorFactory dataDecryptorFactory = null; for (Object pgpEncData : pgpEncryptedDataList) { PGPPublicKeyEncryptedData pkEnc = (PGPPublicKeyEncryptedData) pgpEncData; for (PGPPrivateKey clientPrivateKey : clientPrivateKeys) { if (pkEnc.getKeyID() == clientPrivateKey.getKeyID()) { pgpPublicKeyEncryptedData = pkEnc; dataDecryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC") .build(clientPrivateKey); break; } } } if (pgpPublicKeyEncryptedData == null || dataDecryptorFactory == null) { // Public key used to encrypt data does not correspond to private key provided throw new IllegalStateException("matching encrypted data not found"); } InputStream clear = pgpPublicKeyEncryptedData.getDataStream(dataDecryptorFactory); PGPObjectFactory plainFact = new PGPObjectFactory(clear, new JcaKeyFingerprintCalculator()); Object message = plainFact.nextObject(); // Signature related PGPSignatureList signatureList = null; PGPOnePassSignatureList onePassSignatureList = null; PGPCompressedData compressedData = null; while (message != null) { if (message instanceof PGPCompressedData) { compressedData = (PGPCompressedData) message; plainFact = new PGPObjectFactory(compressedData.getDataStream(), keyFingerPrintCalculator); message = plainFact.nextObject(); } if (message instanceof PGPLiteralData) { // have to read it and keep it somewhere. Streams.pipeAll(((PGPLiteralData) message).getInputStream(), actualOutput); } else if (message instanceof PGPOnePassSignatureList) { onePassSignatureList = (PGPOnePassSignatureList) message; } else if (message instanceof PGPSignatureList) { signatureList = (PGPSignatureList) message; } else { throw new PGPException("message unknown message type."); } message = plainFact.nextObject(); } // check data decrypts okay if (!pgpPublicKeyEncryptedData.verify()) { throw new RuntimeException("Enc data is invalid!"); } if (isVerifySignature != true) { actualOutput.close(); clear.close(); return actualOutput.toString(); } else { if (onePassSignatureList == null || signatureList == null) { throw new SignatureException("PGP Signatures not found."); } else { boolean signatureVerified = false; for (int i = 0; i < onePassSignatureList.size(); i++) { PGPOnePassSignature ops = onePassSignatureList.get(0); PGPSignature signature = signatureList.get(0); for (PGPPublicKey bankPublicKey : bankPublicKeys) { ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider(new BouncyCastleProvider()), bankPublicKey); ops.update(actualOutput.toByteArray()); if (ops.verify(signature)) { signatureVerified = true; break; } } } if (!signatureVerified) { throw new SignatureException("Signature verification failed"); } } actualOutput.close(); clear.close(); } return actualOutput.toString(); } return epicStrNew; } catch (PGPException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SignatureException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; }
- For a successful response, the response provided will be encrypted and Base64 encoded wrapped in Json Structure