I am trying to list accounts with a C program using libcurl and windows cryptography functions. I keep getting unauthorized. It might have something to do with the function I use to generate the signature. Here’s my complete code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <Windows.h>
#include "curl/curl.h"
#define VERSION "v1.0"
// Coinbase API credentials
#define API_KEY "xxxxxxxxxxxxxxxx"
#define SECRET_KEY "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#define TIMESTAMP_SIZE 20
#define HASH_SIZE 32
#define HEX_SIZE (2 * HASH_SIZE + 1)
// Function to generate a timestamp
void generateTimestamp(char* timestamp) {
time_t currentTime = time(NULL);
snprintf(timestamp, TIMESTAMP_SIZE, "%ld", (long)currentTime);
}
// Function to compute HMAC-SHA256
void createSignature(const char* method, const char* requestPath, const char* body, const char* secretKey, char* signature) {
char timestamp[TIMESTAMP_SIZE];
generateTimestamp(timestamp);
// Concatenate timestamp + method + requestPath + body
char payload[512];
snprintf(payload, sizeof(payload), "%s%s%s%s", timestamp, method, requestPath, body);
// Initialize Windows Crypto API
HCRYPTPROV hCryptProv = NULL;
HCRYPTHASH hHash = NULL;
if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
fprintf(stderr, "CryptAcquireContext failed\n");
return;
}
if (!CryptCreateHash(hCryptProv, CALG_SHA_256, 0, 0, &hHash)) {
fprintf(stderr, "CryptCreateHash failed\n");
CryptReleaseContext(hCryptProv, 0);
return;
}
if (!CryptHashData(hHash, (BYTE*)secretKey, strlen(secretKey), 0)) {
fprintf(stderr, "CryptHashData (key) failed\n");
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
return;
}
if (!CryptHashData(hHash, (BYTE*)payload, strlen(payload), 0)) {
fprintf(stderr, "CryptHashData (payload) failed\n");
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
return;
}
BYTE digest[HASH_SIZE];
DWORD digestSize = sizeof(digest);
if (!CryptGetHashParam(hHash, HP_HASHVAL, digest, &digestSize, 0)) {
fprintf(stderr, "CryptGetHashParam failed\n");
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
return;
}
// Convert the digest to a hexadecimal string
for (int i = 0; i < HASH_SIZE; i++) {
snprintf(signature + (i * 2), 3, "%02x", digest[i]);
}
signature[HEX_SIZE - 1] = '\0'; // Null-terminate the string
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
}
// Function to send a Coinbase API request
CURLcode sendCoinbaseRequest(const char* method, const char* endpoint, const char* body, const char* timestamp, const char* signature) {
CURL* curl = curl_easy_init();
if (!curl) {
fprintf(stderr, "Failed to initialize cURL\n");
return CURLE_FAILED_INIT;
}
// Construct the request URL
char url[256];
snprintf(url, sizeof(url), "https://api.coinbase.com%s", endpoint);
// Set up headers
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
char keyHeader[64];
snprintf(keyHeader, sizeof(keyHeader), "CB-ACCESS-KEY: %s", API_KEY);
headers = curl_slist_append(headers, keyHeader);
char timestampHeader[64];
snprintf(timestampHeader, sizeof(timestampHeader), "CB-ACCESS-TIMESTAMP: %s", timestamp);
headers = curl_slist_append(headers, timestampHeader);
char signatureHeader[128];
snprintf(signatureHeader, sizeof(signatureHeader), "CB-ACCESS-SIGN: %s", signature);
headers = curl_slist_append(headers, signatureHeader);
// Set cURL options
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
if (body != NULL) {
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
}
CURLcode res = curl_easy_perform(curl);
// Clean up
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return res;
}
int main() {
char timestamp[TIMESTAMP_SIZE];
generateTimestamp(timestamp);
char signature[HEX_SIZE];
// Create the API request signature
const char* method = "GET";
const char* endpoint = "/api/v3/brokerage/accounts";
const char* body = "";
createSignature(method, endpoint, body, SECRET_KEY, signature);
printf("Signature: %s\n", signature);
// Send the Coinbase API request
CURLcode res = sendCoinbaseRequest(method, endpoint, body, timestamp, signature);
if (res != CURLE_OK) {
fprintf(stderr, "Request failed: %s\n", curl_easy_strerror(res));
}
return 0;
}
My api key has all permissions turned on btw, so that shouldn’t be the cause.
EDIT: I posted the entire code and cleaned it up a bit, still having the “unauthorized” problem.