How to perform Base64 on top of HASH in Java

I tried the following but still get 401 Unauthorized: [no body]

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add("access_key", accessKey);
        long currentTimestamp = (System.currentTimeMillis() / 1000) - 10;
        headers.add("timestamp", String.valueOf(currentTimestamp));
        String randomSalt = generateRandomAlphanumericString();
        headers.add("salt", randomSalt);

        JSONObject body = new JSONObject();
        body.put("amount", 123.45);
        body.put("complete_payment_url", "http://example.com/complete");
        body.put("country", "ID");
        body.put("currency", "IDR");
        body.put("error_payment_url", "IDR");
        body.put("merchant_reference_id", "IDR");
        body.put("cardholder_preferred_currency", "IDR");
        body.put("language", "en");
        body.put("metadata", "something");
        body.put("payment_method_types_include", List.of("id_ovo_ewallet",
            "id_dana_ewallet",
            "id_linkaja_ewallet"));

        String tobeSigned = HttpMethod.POST.toString().toLowerCase() + "/v1/checkout" + randomSalt + currentTimestamp + accessKey
            + secretKey + body;

        log.info("to_sign {}",tobeSigned);
        String encodedString = encode(secretKey,tobeSigned);
        log.info("rapyd_signature {}", encodedString);
        headers.add("signature", encodedString);

        HttpEntity<String> entity = new HttpEntity<>(body.toString(), headers);

        ResponseEntity<String> result = restTemplate.exchange(baseUrl + "/checkout", HttpMethod.POST, entity, String.class);

Use this example from the docs - Message Security (rapyd.net)
Once the body is created, try to do a string replacement of spaces. (You’ll get a signature mismatch if you don’t - in some cases)

body.replace(" ", "");
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Random;

public class GetPOS {
    
    public static String hash256(String data) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(data.getBytes());
        return bytesToHex(md.digest());
    }
    
    public static String bytesToHex(byte[]bytes) {
        StringBuffer result = new StringBuffer();
        for (byte byt: bytes)
            result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }
    
    public static String hmacDigest(String msg, String keyString, String algo) {
        String digest = null;
        try {
            SecretKeySpec key = new SecretKeySpec((keyString).getBytes("ASCII"), algo);
            Mac mac = Mac.getInstance(algo);
            mac.init(key);
            
            byte[]bytes = mac.doFinal(msg.getBytes("UTF-8"));
            
            StringBuffer hash = new StringBuffer();
            for (int i = 0; i < bytes.length; i++) {
                String hex = Integer.toHexString(0xFF & bytes[i]);
                if (hex.length() == 1) {
                    hash.append('0');
                }
                hash.append(hex);
            }
            digest = hash.toString();
        } catch (UnsupportedEncodingException e) {
            System.out.println("hmacDigest UnsupportedEncodingException");
        }
        catch (InvalidKeyException e) {
            System.out.println("hmacDigest InvalidKeyException");
        }
        catch (NoSuchAlgorithmException e) {
            System.out.println("hmacDigest NoSuchAlgorithmException");
        }
        return digest;
    }
    
    public static String givenUsingPlainJava_whenGeneratingRandomStringUnbounded_thenCorrect() {
        int leftLimit = 97;   // letter 'a'
        int rightLimit = 122; // letter 'z'
        int targetStringLength = 10;
        Random random = new Random();
        StringBuilder buffer = new StringBuilder(targetStringLength);
        for (int i = 0; i < targetStringLength; i++) {
            int randomLimitedInt = leftLimit + (int)
                (random.nextFloat() * (rightLimit - leftLimit + 1));
            buffer.append((char)randomLimitedInt);
        }
        String generatedString = buffer.toString();
        
        return (generatedString);
    }

    public static void main(String[]args)throws Exception {
        try {
            System.out.println("GetPOS Start");
                  String httpMethod = "get";                           // get|put|post|delete - must be lowercase
                  String urlPath = "/v1/" + "data/countries";          // hardkeyed for this example
            String salt = givenUsingPlainJava_whenGeneratingRandomStringUnbounded_thenCorrect(); // Randomly generated for each request.
            long timestamp = System.currentTimeMillis() / 1000L; // Unix time (seconds).
            String accessKey = "AAAAAAAAAAA";                    // The access key received from Rapyd.
            String secretKey = "SSSSSSSSSSS";                    // Never transmit the secret key by itself.
                  bodyString = "";                                     // Always empty for GET; strip nonfunctional whitespace.
                                                                       // Must be a String or an empty String.
            String toEnc = httpMethod + urlPath + salt + Long.toString(timestamp) + accessKey + secretKey + bodyString;
            System.out.println("String to be encrypted::" + toEnc);
            String StrhashCode = hmacDigest(toEnc, secretKey, "HmacSHA256");
            String signature = Base64.getEncoder().encodeToString(StrhashCode.getBytes());
            HttpClient httpclient = HttpClients.createDefault();
            
            try {
                HttpGet httpget = new HttpGet("/v1/data/countries");
                
                httpget.addHeader("Content-Type", "application/json");
                httpget.addHeader("access_key", accessKey);
                httpget.addHeader("salt", salt);
                httpget.addHeader("timestamp", Long.toString(timestamp));
                httpget.addHeader("signature", signature);
                
                // Create a custom response handler
                ResponseHandler < String > responseHandler = new ResponseHandler < String > () {
                     @ Override
                    public String handleResponse(
                        final HttpResponse response)throws ClientProtocolException,
                    IOException {
                        int status = response.getStatusLine().getStatusCode();
                        HttpEntity entity = response.getEntity();
                        return entity != null ? EntityUtils.toString(entity) : null;
                    }
                };
                String responseBody = httpclient.execute(httpget, responseHandler);
                System.out.println("----------------------------------------");
                System.out.println(responseBody);
            }
            finally {
            }
        } catch (Exception e) {
        }
    }
}
1 Like

In addition to solution posted @sandy_inspires. These are my own observations in your body parameters. You will need to enter correct value where appropriate

1.) The error_payment_url should be a working url under http or https. Entering anything other thing like localhost will create a no body error message

2.) and Many more.
Finally, here is a clear documentations on how to create a checkout page. checkout page creation doc.

I hope this will also help you in trouble shooting you error

2 Likes

@CharlesDorsett ,you are right. decimal points is not a problem. After I trouble shoot my php codes, I found out they are some whitespaces and little err on my code from my own end. I have fixed it and everything is working great now. Thanks for pointing me out in the right direction…

1 Like

Glad got it working now. Thanks everyone!

4 Likes