Reputation: 578
This is edited from original post:
From the docs:
Signing a Message The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the base64-decoded secret key on the prehash string timestamp + method + requestPath + body (where + represents string concatenation) and base64-encode the output. The timestamp value is the same as the CB-ACCESS-TIMESTAMP header.
Here is information from a key I deleted. This is from Coinbase Pro Sandbox:
publicKey:
06057d5b5e03d0f8587a248330402b21
passPhrase:
gcgs6k6rp0f
secretKey: EFAToD5heo66GIgZlT2TIZzJf8TYlmxyeRxRYDHTBv3lTt9XN6uaNS0RNAy0os/caR47x6EiPDOV3Ik+YzrfEA==
I'm using angular, specifically the node.js crypto-js library:
private generateSignaturePro(timestamp: string, method: string, resourceUrl: string, requestBody: string): string {
var prehash: string = timestamp + method + resourceUrl + requestBody;
var key = (Buffer.from(this.secretKey, 'base64')).toString();
return crypto.enc.Base64.stringify(crypto.HmacSHA256(prehash, key));
}
Server time is Time: 2019-05-20T19:01:38.711Z Epoch: 1558378898.711 (from /time endpoint)
here is my request and the server response:
Request:
Request URL: https://api-public.sandbox.pro.coinbase.com/accounts
Request Method: GET
Status Code: 400
Remote Address: 104.16.161.226:443
Referrer Policy: no-referrer-when-downgrade
Request Headers:
Provisional headers are shown
Accept: application/json, text/plain, */*
CB-ACCESS-KEY: 06057d5b5e03d0f8587a248330402b21
CB-ACCESS-PASSPHRASE: gcgs6k6rp0f
CB-ACCESS-SIGN: 0cc2BnQYdUhLucXSPwMTjpHjJ32G3RXSH44rSsEopvjAtY90uRCMVy6xUrzg/A/aRJBLqx390fcZc7lmJeP++g==
CB-ACCESS-TIMESTAMP: 1558378899
Referer: https://localhost:44342/dashboard
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36
Response Headers:
access-control-allow-headers: Content-Type, Accept, cb-session, cb-fp
access-control-allow-methods: GET,POST,DELETE,PUT
access-control-allow-origin: *
access-control-expose-headers: cb-before, cb-after, cb-gdpr
access-control-max-age: 7200
cache-control: no-store
cf-cache-status: MISS
cf-ray: 4da08f74ba97cf68-IAD
content-length: 31
content-type: application/json; charset=utf-8
date: Mon, 20 May 2019 19:01:38 GMT
etag: W/"1f-4RjKVp8I05+xcnQ5/G16yRoMSKU"
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
server: cloudflare
status: 400
strict-transport-security: max-age=15552000; includeSubDomains
vary: Accept-Encoding
x-content-type-options: nosniff
x-dns-prefetch-control: off
x-download-options: noopen
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
Response:
{"message":"invalid signature"}
What am I doing wrong?
EDIT: Changed method to the SHA 256 version. Still doesn't work.
Upvotes: 3
Views: 5278
Reputation: 56
Had the same issue here. For me the answer was to use luxon DateTime instead of the native js Date functions as shown in the coinbase docs.
Here is the typescript that works for me. You can use the results of this function to populate your request headers.
import crypto from 'crypto';
import { DateTime } from 'luxon';
export const auth = (
method: 'GET' | 'POST',
path: string,
body?: Record<string, unknown>
) => {
const timestamp = DateTime.utc().toMillis() / 1000;
let message = timestamp + method + path;
if (body) {
message += JSON.stringify(body);
}
const secret = Buffer.from('YOUR_SECRET','base64');
const hmac = crypto.createHmac('sha256', secret);
return {
'CB-ACCESS-KEY': 'YOUR_KEY',
'CB-ACCESS-PASSPHRASE': 'YOUR_PASSPHRASE',
'CB-ACCESS-SIGN': hmac.update(message).digest('base64'),
'CB-ACCESS-TIMESTAMP': timestamp.toString()
};
};
Upvotes: 1
Reputation: 2444
I ran into same issue and my code was same as yours basically. I changed to the following (c#) and it finally worked. Weird thing is coinbase pro is only exchange i have had issues with so far with the signature. In any case here is the code that worked for me. Hope this helps. Would have saved me hours
public string ComputeSignature(
HttpMethod httpMethod,
string secret,
double timestamp,
string requestUri,
string contentBody = "")
{
var convertedString = System.Convert.FromBase64String(secret);
var prehash = timestamp.ToString("F0", CultureInfo.InvariantCulture) + httpMethod.ToString().ToUpper() + requestUri + contentBody;
return HashString(prehash, convertedString);
}
private string HashString(string str, byte[] secret)
{
var bytes = Encoding.UTF8.GetBytes(str);
using (var hmaccsha = new HMACSHA256(secret))
{
return System.Convert.ToBase64String(hmaccsha.ComputeHash(bytes));
}
}
Upvotes: 3
Reputation: 2874
From the gdax-java (as it was named prior to "coinbase pro") library the generate signature method is:
String prehash = timestamp + method.toUpperCase() + requestPath + body;
byte[] secretDecoded = Base64.getDecoder().decode(secretKey);
keyspec = new SecretKeySpec(secretDecoded, "HmacSHA256");
sha256 = (Mac) GdaxConstants.SHARED_MAC.clone();
sha256.init(keyspec);
return Base64.getEncoder().encodeToString(sha256.doFinal(prehash.getBytes()));
At least on initial inspection the code you're using specifies using SHA512 rather than HmacSHA256, so I'd suspect that to be a probabale cause.
There is also more help with NodeJS in the right hand column here for generating the signatures. https://docs.pro.coinbase.com/#creating-a-request
Upvotes: 1