"Unauthorized" response in C

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.

Solved this by switching to OpenSSL, here’s the code that works:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <Windows.h>
#include "curl/curl.h"
#include "cJSON/cJSON.h"
#include "openssl/sha.h"
#include "openssl/hmac.h"

#define VERSION "v1.0"

// Coinbase API credentials
#define API_KEY "xxxxxxxxxxxxxxxx"
#define SECRET_KEY "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

#define TIMESTAMP_SIZE 20

// 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);

    // Calculate HMAC-SHA-256
    unsigned char hmac_result[SHA256_DIGEST_LENGTH];
    unsigned int hmac_len;

    HMAC(EVP_sha256(), secretKey, strlen(secretKey), (unsigned char*)payload, strlen(payload), hmac_result, &hmac_len);

    // Convert the binary HMAC result to a hex string

    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        sprintf(&signature[i * 2], "%02x", hmac_result[i]);
    }

    signature[2 * SHA256_DIGEST_LENGTH] = '\0'; // Null-terminate the string
}

// 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[2 * SHA256_DIGEST_LENGTH + 1]; // +1 for null terminator

    // 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;
}

Hey @TaskForceTorture, We are glad that the issue was resolved by yourselves.

Please let us know if you have any more queries.

Thank you!