'Coinbase Api Java POST request "Invalid Signature"
I am trying to send a POST request to the coinbase sandbox endpoint. When signing the request I always get an "Invalid Signature" response. It seems that coinbase requires the JSON message be base 64 encoded and sent as a part of the signature. I am fairly new to POST requests and have never signed a message before. Can someone please let me know what I am doing wrong. I have been stuck on this issue for a week so any input is much appreciated.
The relevant part of my code is below
public void postOrder() throws InvalidKeyException, NoSuchAlgorithmException, CloneNotSupportedException, ClientProtocolException, IOException {
String message = "{ \n"+
" \"size\":\"1.00\", \n"+
" \"price\":\"0.80\", \n"+
" \"side\":\"buy\", \n"+
" \"product_id\":\"BTC-USD\" \n"+
"}";
JSONObject json = new JSONObject(message);
message = json.toString();
try
{
String timestamp= Instant.now().getEpochSecond()+"";
String accessSign = getAccess(timestamp,"POST","/orders",message);
String apiKey = properties.getProperty("key");
String passphrase = properties.getProperty("passphrase");
URL url = new URL("https://api-public.sandbox.pro.coinbase.com/orders");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("accept", "application/json");
connection.setRequestProperty("content-type", "application/json; charset=UTF-8");
connection.setRequestProperty("CB-ACCESS-KEY", apiKey);
connection.setRequestProperty("CB-ACCESS-SIGN", accessSign);
connection.setRequestProperty("CB-ACCESS-TIMESTAMP", timestamp);
connection.setRequestProperty("CB-ACCESS-PASSPHRASE", passphrase);
connection.setRequestProperty("User-Agent", "Java Client");
try {
connection.getOutputStream().write(message.getBytes("UTF-8"));
OutputStream output = connection.getOutputStream();
output.write(param.getBytes("UTF-8"));
} catch (Exception e) {
System.out.println(e.getMessage());
}
String status = connection.getResponseMessage();
System.out.println("STATUS: "+status);
}catch(Exception e) {
System.out.println(e.getMessage());
}
return;
}
private String getAccess(String timestamp, String method, String path, String param) throws NoSuchAlgorithmException, InvalidKeyException, CloneNotSupportedException, IllegalStateException, UnsupportedEncodingException {
String secretKeyString = properties.getProperty("secret");
String prehash = timestamp+method+path+param;
byte[] secretKeyDecoded = Base64.getDecoder().decode(secretKeyString);
SecretKey secretKey = new SecretKeySpec(secretKeyDecoded, "HmacSHA256");
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
hmacSha256.init(secretKey);
return Base64.getEncoder().encodeToString(hmacSha256.doFinal(prehash.getBytes()));
}
Solution 1:[1]
I was able to get this working with using javax.crypto.* library functions. The changes I did were
- Took the API Key for the sandbox.
- To encoded the signature, I explicitly used UTF_8
Below is the code that worked for me with Coinbase sandbox API_KEY -
public String getCoinbaseHeaderSignature(
String timeStamp,
String method,
String requestPath,
String body
) throws NoSuchAlgorithmException, InvalidKeyException {
String data = timeStamp + method.toUpperCase() + requestPath + body;
byte[] key = Base64.getDecoder().decode(API_KEY);
SecretKey keySpec = new SecretKeySpec(key, "HmacSHA256");
// Get HmacSHA256 instance
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(keySpec);
return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes(StandardCharsets.UTF_8)));
}
For more details, refer to documentation https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-key-authentication
Solution 2:[2]
In my case, I utilized gdax-java example. I solved this issue by removing decimal values from timestamp that means I just used integer part of the timestamp value.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Sourabh |
Solution 2 | YUSUF YILDIZ |