Proper REST signatures

I’m seeing a lot of signature related quesstions lately. So I’d like to take a second to just post this example to hopefully catch some of the quesstions before they are asked.

First thing you’ll need is a functional signature method. This requires several inputs, the most important of which is your api key, not the secret key that for whatever reason seems to be a popular choice.

​ ​private​ ​String​ ​signMessage​(​String​ ​timestamp​, ​String​ ​method​, ​String​ ​path​, ​String​ ​body​) ​//​ ENCRYPTED HASH SECURITY SIGNATURE BUILDER
​ ​throws​ ​NoSuchAlgorithmException​, ​InvalidKeyException​ {

​ ​String​ prehash ​=​ timestamp ​+​ method ​+​ path ​+​ body;
​ ​Mac​ sha256_HMAC ​=​ ​Mac​.​getInstance(​"​HmacSHA256​"​);
​ ​byte​ secretDecoded ​=​ ​Base64​.​getDecoder()​.​decode(​this​.​coinbaseAPIKey);
​ ​SecretKeySpec​ secret_key ​=​ ​new​ ​SecretKeySpec​(secretDecoded, ​"​HmacSHA256​"​);
​ sha256_HMAC​.​init(secret_key);
​ ​return​ ​Base64​.​getEncoder()​.​encodeToString(sha256_HMAC​.​doFinal(prehash​.​getBytes()));
​ }

The second most important thing you need to be sure of is your request headers, both content and accept headers be to be set properly as well as the security credentials. Failure to set any of these parameters will result in an invalid signature response from the server.

​ ​String​ timeStamp ​=​ ​Instant​.​now()​.​getEpochSecond() ​+​ ​"​"​;
​ request ​=​ ​HttpRequest​.​newBuilder()
​ .uri(​URI​.​create(​String​.​join(​"​"​, coinbaseProBaseURL, endpoint)))
​ .header(​"​CB-ACCESS-KEY​"​, coinbaseSecretKey)
​ .header(​"​CB-ACCESS-PASSPHRASE​"​, coinbasePassphrase)
​ .header(​"​CB-ACCESS-SIGN​"​, signMessage(timeStamp, ​"​POST​"​, endpoint, requestBody))
​ .header(​"​CB-ACCESS-TIMESTAMP​"​, timeStamp)​.​header(​"​Accept​"​, ​"​application/json​"​)
​ .header(​"​Content-Type​"​, ​"​application/json​"​)
​ .header(​"​Accept​"​, ​"​application/json​"​)
​ .method(​"​POST​"​, ​HttpRequest​.​BodyPublishers​.​ofString(requestBody))
​ .build();

@arood I knew I had this problem myself having the key positions switched. The wording on this page is misleading as to the keys respective positions:

Fantastic post and thank you for the notes on the documents @cleggink - we really appreciate your continued contributions and insights on these topics.

1 Like

I’ve been able to make a signature to place an order, but when I make a signature to cancel an order, I get an invalid signature message as a response, even though the same procedure is used to make the signature.

Has anyone tried to cancel all orders with the DELETE method? If so, have you had success making a valid signature?

One possible issue is body. What is body for canceling all orders?
Is it just {"profile_id": "key"} ?

If you are attempting my method shown here then when not using a body you should pass “” (nothing) and not a null value. This will negate the value in the prehash. I’ve not had a problem with the delete function in this regard.

I finally got it to work. Here’s what I learned:

  • As @cleggink indicated, body must be an empty string.
  • As in placing an order, the signature is made from the Coinbase secret, not from the key.
  • The URL doesn’t need anything after “/orders” – i.e. no “?profile_id=”. The Coinbase API website is a little confusing on this point: deleteorders

All your points are correct. The profile id parameter is never required, or necessary if your not using multiple api keys or accounts.

Been getting invalid signature errors that just started the past week or so. PowerShell functions haven’t changed, so not sure what’s going on:

FUNCTION Base64-Encode($string) {
$conversion = [System.Text.Encoding]::ASCII.GetBytes($string)
RETURN [System.Convert]::ToBase64String($conversion)
}

FUNCTION Base64-Decode($string) {
$conversion = [System.Convert]::FromBase64String($string)
RETURN [System.Text.Encoding]::ASCII.GetString($conversion)
}

FUNCTION hmac($message, $secret) {
$hmacsha = NEW-OBJECT System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Convert]::FromBase64String($secret)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
$signature = [Convert]::ToBase64String($signature)
RETURN $signature
}

FUNCTION Submit-Request($request) {
$unixEpochStart = GET-DATE -Date “01/01/1970”
$now = GET-DATE
$timestamp = (NEW-TIMESPAN -Start $unixEpochStart -End $now.ToUniversalTime()).TotalSeconds
$timestamp = ([math]::Round($timestamp, 3)).ToString()
$prehash = $timestamp + $request.method.ToUpper() + $request.url + $request.body
$signature_b64 = hmac -message $prehash -secret $request.secret
$header = @{
“CB-ACCESS-KEY” = $request.key
“CB-ACCESS-SIGN” = $signature_b64
“CB-ACCESS-TIMESTAMP” = $timestamp
“CB-ACCESS-PASSPHRASE” = $request.passphrase
“Content-Type” = ‘application/json’
}
$uri = $request.endpoint + $request.url
IF ($request.method.ToUpper() -eq ‘POST’) {
$response = Invoke-RestMethod -Method $request.method -Uri $uri -Headers $header -Body $request.body
}
ELSE {
$response = Invoke-RestMethod -Method $request.method -Uri $uri -Headers $header
}
RETURN $response
}

@Charlatat, I just ran your hmac function in PowerShell, and didn’t get the same result that I got in Javascript or AHK, which gave valid signatures.

It might not matter, but I didn’t understand your hmac function too well. Specifically, I didn’t understand why $hmacsha.key didn’t appear on the next line.

I didn’t originally create these functions, but I’m assuming that the hmac function sets the “key” property of the $hmacsha object, then the next line generates a hash of the $hmacsha object and puts it into the $signature variable.

All calls work successfully, even BUY/SELL, but those are the only ones that generate the invalid signature error (which terminates my script). Any suggestions would be appreciated.

I don’t know PowerShell, but the signature generated by your script didn’t match the signatures generated by other working scripts.

Look at the suggestions in this authentication thread, showing Python and CryptoJS snippets.

The whole Python code I got to work is here.

I prefer AHK, and two AHK experts helped me generate a working sig, which you can see in this thread.

I’m at work so I don’t have the time to con thru your code right now. But if the key you’re referring to is coinbase api key then you need to set that yourself.