#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <stdint.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <curl/curl.h>
// getenv() specifies that the AccessKey ID and AccessKey secret are obtained from environment variables.
#define ACCESS_KEY_ID getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
#define ACCESS_KEY_SECRET getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
#define ALGORITHM "ACS3-HMAC-SHA256"
#define BUFFER_SIZE 4096
// Perform calculation by using the HMAC-SHA256 algorithm.
void hmac256(const char *key, const char *message, char *output) {
unsigned char hmac[SHA256_DIGEST_LENGTH];
unsigned int result_len;
// Calculate the HMAC value.
HMAC(EVP_sha256(), key, strlen(key), (unsigned char *)message, strlen(message), hmac, &result_len);
// Convert the HMAC value to a hexadecimal string.
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
sprintf(output + (i * 2), "%02x", hmac[i]);
}
output[SHA256_DIGEST_LENGTH * 2] = '\0';
}
// Calculate the hash value by using the SHA-256 algorithm.
void sha256_hex(const char *input, char *output) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256((unsigned char *)input, strlen(input), hash);
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
sprintf(output + (i * 2), "%02x", hash[i]);
}
output[SHA256_DIGEST_LENGTH * 2] = '\0';
}
// Encode the value in the URL format.
char* percentEncode(const char* str) {
if (str == NULL) {
fprintf(stderr, "The input string cannot be null\n");
return NULL;
}
size_t len = strlen(str);
// In worst case scenarios, each character must be encoded into three characters in the format of %XX. This is why the allocated space is three times the size of the original character.
char* encoded = (char*)malloc(len * 3 + 1);
if (encoded == NULL) {
fprintf(stderr, "Failed to allocate memory resources\n");
return NULL;
}
char* ptr = encoded;
for (size_t i = 0; i < len; i++) {
unsigned char c = (unsigned char)str[i];
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
// You can add the character if it is risk-free.
*ptr++ = c;
} else {
// Otherwise, encode the character in the URL format.
ptr += sprintf(ptr, "%%%02X", c);
}
}
*ptr = '\0'; // End with null.
// Replace the plus signs (+).
char* finalEncoded = malloc(strlen(encoded) + 1);
if (finalEncoded) {
char* fptr = finalEncoded;
for (size_t j = 0; j < strlen(encoded); j++) {
if (encoded[j] == '+') {
strcpy(fptr, "%20");
fptr += 3; // Move the cursor.
} else if (encoded[j] == '*') {
strcpy(fptr, "%2A");
fptr += 3;
} else if (encoded[j] == '~') {
*fptr++ = '~';
} else {
*fptr++ = encoded[j];
}
}
*fptr = '\0'; // End with null.
}
free(encoded); // Release the space of temporary code.
return finalEncoded;
}
// A random nonce.
void generate_uuid(char *uuid, size_t size) {
if (size < 37) { // The UUID format requires 36 characters and a terminator.
fprintf(stderr, "Buffer size too small for UUID\n");
return;
}
// Use a random number generator to generate 16 random bytes.
unsigned char random_bytes[16];
RAND_bytes(random_bytes, sizeof(random_bytes));
// The valid version is 4, which is used to generate random UUIDs.
random_bytes[6] &= 0x0f; // Retain the highest four values.
random_bytes[6] |= 0x40; // Set the version to 4.
random_bytes[8] &= 0x3f; // Retain the highest two values.
random_bytes[8] |= 0x80; // Set the variant to 10xx.
// Format the value to a UUID string.
snprintf(uuid, size,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
random_bytes[0], random_bytes[1], random_bytes[2], random_bytes[3],
random_bytes[4], random_bytes[5], random_bytes[6], random_bytes[7],
random_bytes[8], random_bytes[9], random_bytes[10], random_bytes[11],
random_bytes[12], random_bytes[13], random_bytes[14], random_bytes[15]);
}
// Read the binary file.
size_t read_file(const char *file_path, char **buffer) {
FILE *file = fopen(file_path, "rb");
if (!file) {
fprintf(stderr, "Cannot open file %s\n", file_path);
return 0; // Failed to read the file.
}
fseek(file, 0, SEEK_END);
size_t file_size = ftell(file);
fseek(file, 0, SEEK_SET);
*buffer = (char *)malloc(file_size);
if (!*buffer) {
fprintf(stderr, "Failed to allocate memory for file buffer\n");
fclose(file);
return 0; // Failed to read the file.
}
fread(*buffer, 1, file_size, file);
fclose(file);
return file_size; // Return the number of bytes that are read.
}
// Obtain authorization.
void get_authorization(const char *http_method, const char *canonical_uri, const char *host,
const char *x_acs_action, const char *x_acs_version, const char *query_params,
const char *body, char *authorization_header,
char *hashed_payload, char *x_acs_date, char *uuid) {
// Generate a UUID.
generate_uuid(uuid, 37);
// Generate an x-acs-date string.
time_t now = time(NULL);
struct tm *utc_time = gmtime(&now);
strftime(x_acs_date, 64, "%Y-%m-%dT%H:%M:%SZ", utc_time);
// Print the timestamp.
printf("Generated x-acs-date: %s\n", x_acs_date);
// Calculate the hash value of the request body by using the SHA-256 algorithm (x-acs-content-sha256).
sha256_hex(body ? body : "", hashed_payload);
// Print the hash value.
printf("Generated x-acs-content-sha256: %s\n", hashed_payload);
char canonical_headers[BUFFER_SIZE];
snprintf(canonical_headers, sizeof(canonical_headers),
"host:%s\nx-acs-action:%s\nx-acs-content-sha256:%s\nx-acs-date:%s\nx-acs-signature-nonce:%s\nx-acs-version:%s",
host, x_acs_action, hashed_payload, x_acs_date, uuid, x_acs_version);
// Print the canonicalized request headers.
printf("Canonical Headers:===============>\n%s\n", canonical_headers);
// The headers used for signature calculation.
char signed_headers[] = "host;x-acs-action;x-acs-content-sha256;x-acs-date;x-acs-signature-nonce;x-acs-version";
char canonical_request[BUFFER_SIZE];
snprintf(canonical_request, sizeof(canonical_request), "%s\n%s\n%s\n%s\n\n%s\n%s",
http_method, canonical_uri, query_params ? strdup(query_params) : "", // The following headers are used for signature calculation: percentCode,
canonical_headers, signed_headers, and hashed_payload);
// Print the canonicalized request.
printf("Canonical Request:\n%s\n", canonical_request);
// Calculate the hash value of the canonicalized request by using the SHA-256 algorithm.
char hashed_canonical_request[SHA256_DIGEST_LENGTH * 2 + 1];
sha256_hex(canonical_request, hashed_canonical_request);
// Print the canonicalized request after hashing.
printf("hashedCanonicalRequest: %s\n", hashed_canonical_request);
// Create a string-to-sign.
char string_to_sign[BUFFER_SIZE];
snprintf(string_to_sign, sizeof(string_to_sign), "%s\n%s", ALGORITHM, hashed_canonical_request);
// Print the string-to-sign.
printf("stringToSign:\n%s\n", string_to_sign);
// Generate a signature.
char signature[SHA256_DIGEST_LENGTH * 2 + 1];
hmac256(ACCESS_KEY_SECRET, string_to_sign, signature);
// Print the signature.
printf("Signature: %s\n", signature);
// Create the final Authorization header that includes the headers used for signature calculation (SignedHeaders).
snprintf(authorization_header, BUFFER_SIZE,
"%s Credential=%s,SignedHeaders=%s,Signature=%s",
ALGORITHM, ACCESS_KEY_ID, signed_headers, signature);
// Print the Authorization header.
printf("Authorization: %s\n", authorization_header);
}
// Send the request.
void call_api(const char *http_method, const char *canonical_uri, const char *host,
const char *x_acs_action, const char *x_acs_version, const char *query_params,
const char *body,const char *content_type, size_t body_length) {
// Obtain the parameter values required by the signature.
char authorization_header[BUFFER_SIZE];
char hashed_payload[SHA256_DIGEST_LENGTH * 2 + 1];
char x_acs_date[64];
char uuid[37];
// Obtain the Authorization header.
get_authorization(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, authorization_header, hashed_payload, x_acs_date, uuid);
// Concatenate the request URL.
char url[BUFFER_SIZE];
if (query_params && strlen(query_params) > 0) {
snprintf(url, sizeof(url), "https://%s%s?%s", host, canonical_uri, query_params);
} else {
snprintf(url, sizeof(url), "https://%s%s", host, canonical_uri);
}
// Print the request URL.
printf("Request URL: %s\n", url);
// The cURL command for initialization.
CURL *curl = curl_easy_init();
if(!curl) {
fprintf(stderr, "curl_easy_init() failed\n");
return;
}
// Specify the request headers that you want to add in the array.
struct curl_slist *headers = NULL;
// Create a character array to store and add the request headers.
char header_value[BUFFER_SIZE];
// Specify the header information.
snprintf(header_value, sizeof(header_value), "Content-Type: %s", content_type);
headers = curl_slist_append(headers, header_value);
snprintf(header_value, sizeof(header_value), "Authorization: %s", authorization_header);
headers = curl_slist_append(headers, header_value);
snprintf(header_value, sizeof(header_value), "host: %s", host);
headers = curl_slist_append(headers, header_value);
snprintf(header_value, sizeof(header_value), "x-acs-action: %s", x_acs_action);
headers = curl_slist_append(headers, header_value);
snprintf(header_value, sizeof(header_value), "x-acs-content-sha256: %s", hashed_payload);
headers = curl_slist_append(headers, header_value);
snprintf(header_value, sizeof(header_value), "x-acs-date: %s", x_acs_date);
headers = curl_slist_append(headers, header_value);
snprintf(header_value, sizeof(header_value), "x-acs-signature-nonce: %s", uuid);
headers = curl_slist_append(headers, header_value);
snprintf(header_value, sizeof(header_value), "x-acs-version: %s", x_acs_version);
headers = curl_slist_append(headers, header_value);
// Set cURL options.
// Specify a cURL request method.
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method); // Specify an HTTP method.
// Specify a URL.
curl_easy_setopt(curl, CURLOPT_URL, url);
// Disable SSL validation during debugging.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
// Add debugging information.
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// Add request headers.
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// Add a request body.
if (body) {
// Specify the size of the request body.
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body_length);
if (strcmp(content_type, "application/octet-stream") == 0) {
// Add a request body.
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
} else if (strcmp(content_type, "application/x-www-form-urlencoded") == 0) {
// Add a request body.
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
} else if (strcmp(content_type, "application/json; charset=utf-8") == 0) {
// Add a request body.
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
}
}
// Print the headers.
struct curl_slist *header_ptr = headers;
while (header_ptr) {
printf("Header: %s\n", header_ptr->data);
header_ptr = header_ptr->next;
}
// Send the request and check the response.
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
return;
}
// Clear data.
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
/**
*
* A sample signature. You need to adjust the parameters in the main() method.
* <p>
* Obtain the request method (methods), request parameter names (name), request parameter types (type), and request parameter positions (in) and encapsulate the information in the signature request (SignatureRequest).
*1. If the request parameters in the API metadata contain the "in":"query" position information, specify the parameters in the query string (queryParam).
*2. If the request parameters in the API metadata contain the "in": "body" position information, specify the parameters in the body.
*2. If the request parameters in the API metadata contain the "in": "body" position information, specify the parameters in the body.
*/
int main() {
// Set the response format to UTF-8.
SetConsoleOutputCP(CP_UTF8);
srand((unsigned int)time(NULL));
/**
* Example: Call an API operation in the RPC style (Parameter position: "in":"query")
*/
// Specify the request parameters for the API operation.
const char *http_method = "POST";
const char *canonical_uri = "/";
const char *host = "ecs.cn-hangzhou.aliyuncs.com";
const char *x_acs_action = "DescribeInstanceStatus";
const char *x_acs_version = "2014-05-26";
// Configure the InstanceId parameter, which is optional. The value is an array.
const char *instance_ids[] = {
"i-bp11ht4h2kd1ic5fXXXX",
"i-bp16maz3h3xg83raXXXX"
};
// Concatenate the InstanceId array.
char InstanceId[BUFFER_SIZE];
snprintf(InstanceId, sizeof(InstanceId),
"InstanceId.1=%s&InstanceId.2=%s",
percentEncode(instance_ids[0]),
percentEncode(instance_ids[1]));
// Specify the query parameters. Required parameters: RegionId=cn-hangzhou and const char *query_params = "RegionId=cn-hangzhou".
char query_params[BUFFER_SIZE];
snprintf(query_params, sizeof(query_params),
"%s&RegionId=cn-hangzhou", InstanceId);
// Print the parameters.
printf("Query Params: %s\n", query_params);
// The body is empty.
const char *body = "";
const char *content_type = "application/json; charset=utf-8";
call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
/**
* Example: Call an API operation in the RPC style (Parameter position: "in":"body")
*/
// Declare the pointer for storing the file content read by the system.
// char *body = NULL;
// // The length of the file read by the system.
// size_t body_length = read_file("C:\\Users\\issuser\\Desktop\\img\\001.png", &body);
//
// if (body_length > 0) {
// const char *http_method = "POST";
// const char *canonical_uri = "/";
// const char *host = "ocr-api.cn-hangzhou.aliyuncs.com";
// const char *x_acs_action = "RecognizeGeneral";
// const char *x_acs_version = "2021-07-07";
// // Specify the query parameters.
// const char *query_params = "";
// const char *content_type = "application/octet-stream";
//
// // Call the API operation.
// call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, body_length);
//
// // Release the allocated memory resources.
// free(body);
// } else {
// fprintf(stderr, "File read error\n");
// }
/**
* Example: Call an API operation in the RPC style (Parameter position: "in": "formData")
*/
// const char *http_method = "POST";
// const char *canonical_uri = "/";
// const char *host = "mt.aliyuncs.com";
// const char *x_acs_action = "TranslateGeneral";
// const char *x_acs_version = "2018-10-12";
// // For parameters whose position is formData, query the parameter values by using the encoded characters.
// // Configure the Context query parameter: Context="Morning".
// char query_params[BUFFER_SIZE];
// snprintf(query_params, sizeof(query_params), "Context=%s", percentEncode("Morning"));
//
// // Specify the value of formdate parameters.
// const char *format_type = "text";
// const char *source_language = "zh";
// const char *target_language = "en";
// const char *source_text = "Hello";
// const char *scene = "general";
// // If the body is of the formdate type, create a form data string and encode it.
// char body[BUFFER_SIZE];
// snprintf(body, sizeof(body),
// "FormatType=%s&SourceLanguage=%s&TargetLanguage=%s&SourceText=%s&Scene=%s",
// percentEncode(format_type), percentEncode(source_language), percentEncode(target_language),
// percentEncode(source_text), percentEncode(scene));
// const char *content_type = "application/x-www-form-urlencoded";
// printf("formdate_body: %s\n", body);
// call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
/**
* Construct a POST request for an API operation in the ROA style.
*/
// const char *http_method = "POST";
// const char *canonical_uri = "/clusters";
// const char *host = "cs.cn-beijing.aliyuncs.com";
// const char *x_acs_action = "CreateCluster";
// const char *x_acs_version = "2015-12-15";
// // Specify the query parameters.
// const char *query_params = "";
// // The body is of the JSON type.
// // Create a request body in the JSON format.
// char body[BUFFER_SIZE];
// snprintf(body, sizeof(body),
// "{\"name\":\"%s\",\"region_id\":\"%s\",\"cluster_type\":\"%s\","
// "\"vpcid\":\"%s\",\"container_cidr\":\"%s\","
// "\"service_cidr\":\"%s\",\"security_group_id\":\"%s\","
// "\"vswitch_ids\":[\"%s\"]}",
// "Test cluster", "cn-beijing", "ExternalKubernetes",
// "vpc-2zeou1uod4ylaf35tXXXX", "10.0.0.0/8",
// "10.2.0.0/24", "sg-2ze1a0rlgeo7dj37XXXX",
// "vsw-2zei30dhfldu8ytmtXXXX");
// // Print the request body.
// printf("Request Body: %s\n", body);
// const char *content_type = "application/json; charset=utf-8";
// // Send the request.
// call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
/**
* Construct a GET request for an API operation in the ROA style.
*/
// const char *http_method = "GET";
// // Form a URL by concatenating the request parameters, such as canonical_uri:/clusters/cluster_id/resources.
// char canonical_uri[BUFFER_SIZE];
// snprintf(canonical_uri, sizeof(canonical_uri), "/clusters/%s/resources", percentEncode("cd1f5ba0dbfa144c69e48b75df47bXXXX"));
// // Print the resource path.
// printf("canonical_uri: %s\n", canonical_uri);
//
// const char *host = "cs.cn-beijing.aliyuncs.com";
// const char *x_acs_action = "DescribeClusterResources";
// const char *x_acs_version = "2015-12-15";
// // Specify the query parameters.
// const char *query_params = "with_addon_resources=true";
// // Specify the body.
// const char *body = "";
// const char *content_type = "";
// call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
/**
* Construct a DELETE request for an API operation in the ROA style.
*/
// const char *http_method = "DELETE";
// // Form a URL by concatenating the request parameters, such as canonical_uri:/clusters/cluster_id.
// char canonical_uri[BUFFER_SIZE];
// snprintf(canonical_uri, sizeof(canonical_uri), "/clusters/%s", percentEncode("cd1f5ba0dbfa144c69e48b75df47bXXXX"));
// // Print the resource path.
// printf("canonical_uri: %s\n", canonical_uri);
// const char *host = "cs.cn-beijing.aliyuncs.com";
// const char *x_acs_action = "DeleteCluster";
// const char *x_acs_version = "2015-12-15";
// // Specify the query parameters.
// const char *query_params = "";
// // Specify the body.
// const char *body = "";
// const char *content_type = "";
// call_api(http_method, canonical_uri, host, x_acs_action, x_acs_version, query_params, body, content_type, strlen(body));
// Store the generated values in variables.
char authorization_header[BUFFER_SIZE];
char hashed_payload[SHA256_DIGEST_LENGTH * 2 + 1];
char x_acs_date[64];
char uuid[37];
return 0;
}