I try to create an application for use the AWS API. For this, i programme in c with the library CURL. I test my application with "GET" request and it's work. After, i test with the "PUT" request but, i have an error :
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>NotImplemented</Code>
<Message>A header you provided implies functionality that is not implemented</Message>
<Header>Transfer-Encoding</Header>
<RequestId> *** </RequestId>
<HostId> ***</HostId>
</Error>
my code :
#include <stdio.h>
#include <curl/curl.h>
#include <iostream>
#include "sha256_calc.h"
#include <ctime>
#include <hmac_sha256/hmac_sha256.h>
#include <vector>
using std::string;
using std::cout;
using std::endl;
#define BUCKET_NAME "<my bucket>"
#define REGION "eu-west-3"
#define METHODE_PUT "PUT"
#define SERVICE_S3 "s3"
#define ALGORITHM "AWS4-HMAC-SHA256"
#define ACCESS_KEY "<My access Key>"
#define SECRET_KEY "<My secret access Key>"
#define SHA256_HASH_SIZE 32
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
string vector_to_string (std::vector<uint8_t> val)
{
string retour = "";
char res[2048];
char intermedaire[2];
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
//sprintf(intermedaire,"%x",val[i]);
retour = val[i];
}
return retour;
}
std::vector<uint8_t> sign(string key,string msg)
{
std::vector<uint8_t> out(SHA256_HASH_SIZE);
hmac_sha256(key.data(), key.size(), msg.data(), msg.size(),out.data(), out.size());
return out;
}
std::vector<uint8_t> getSignatureKey(string key,string dateStamp,string regionName,string
serviceName)
{
printf("key : %s \n" , key.c_str());
std::vector<uint8_t> kDate = sign("AWS4" key, dateStamp);
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
printf("%d\n",kDate[i]);
}
printf("\n");
string buff = vector_to_string (kDate);
std::vector<uint8_t> kRegion = sign(buff, regionName);
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
printf("%d\n",kRegion[i]);
}
printf("\n");
buff = vector_to_string (kRegion);
std::vector<uint8_t> kService = sign(buff, serviceName);
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
printf("%d\n",kService[i]);
}
printf("\n");
buff = vector_to_string (kService);
std::vector<uint8_t> kSigning = sign(buff, "aws4_request");
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
printf("%d\n",kSigning[i]);
}
printf("\n");
return kSigning;
}
int main(void)
{
CURL *curl;
CURLcode res;
curl_slist *chunk=NULL;
char *file = (char*)"fichier/CR1-12_11.pdf";
char end_point[2048];
char host[1024];
sprintf(host,"%s.s3.%s.amazonaws.com",BUCKET_NAME,REGION);
sprintf(end_point,"https://%s/fichier/CR1-12_11.pdf",host);
time_t now = time(0);
tm *gmtm = gmtime(&now);
char amzdate[32];
char datestamp[32];
sprintf(amzdate,"dddTdddZ",gmtm->tm_year 1900,gmtm->tm_mon 1,gmtm-
>tm_mday,gmtm->tm_hour,gmtm->tm_min,gmtm->tm_sec);
//sprintf(amzdate,"dddTdddZ",1900 gmtm->tm_year,gmtm->tm_mon 1,gmtm-
>tm_mday,8,45,25);
sprintf(datestamp,"ddd",1900 gmtm->tm_year,gmtm->tm_mon 1,gmtm->tm_mday);
cout << gmtm->tm_hour << endl;
string canonical_uri;
canonical_uri = (string)"/" file;
string test = "";
string payload_hash = sha256(test);
string canonical_headers;
canonical_headers = (string)"host:" host "\nx-amz-content-sha256:" payload_hash
"\nx-amz-date:" amzdate "\n";
string signed_header = "host;x-amz-content-sha256;x-amz-date";
string canonicale_request;
canonicale_request = (string) METHODE_PUT "\n" canonical_uri "\n\n"
canonical_headers "\n" signed_header "\n" payload_hash;
printf ("%s\n",canonicale_request.c_str());
string credential_scope;
credential_scope = (string) datestamp "/" REGION "/" SERVICE_S3 "/aws4_request";
string string_to_sign;
string_to_sign = (string) ALGORITHM "\n" amzdate "\n" credential_scope "\n"
sha256(canonicale_request);
char *pCACertFile = (char*)"<file>";
std::vector<uint8_t> signing_key ;
signing_key = getSignatureKey(SECRET_KEY,datestamp,REGION,SERVICE_S3);
string buff = vector_to_string(signing_key);
std::vector<uint8_t> signature(SHA256_HASH_SIZE);
string string_to_sign_buff = string_to_sign;
hmac_sha256(buff.data(), buff.size(), string_to_sign_buff.data(),
string_to_sign_buff.size(),signature.data(), signature.size());
printf("\n");
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
printf("%d\n",signing_key[i]);
}
printf("\n");
printf("\n %s \n",string_to_sign_buff.c_str());
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
printf("%d\n",signature[i]);
}
FILE *fileOpen = fopen("<file>","rb");
FILE *fileOpenWrite = fopen("<file>","wb");
if (fileOpen)
{
printf("fichier ok \n");
}
else
{
printf("erreur\n");
}
FILE *f = fopen(pCACertFile,"r");
if (f)
{
fclose(f);
}
else
{
printf("certificat abscent");
}
string buffUser="";
curl=curl_easy_init();
//curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_URL, end_point);
curl_easy_setopt(curl, CURLOPT_PUT, 1);
buff = credential_scope;
buff = ACCESS_KEY string("/") buff;
printf("%s\n",buff.c_str());
string test_val = "";
char tampon[8];
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
sprintf(tampon,"x",signature[i]);
test_val = tampon;
}
buffUser = (string) "Authorization:" ALGORITHM " Credential=" ACCESS_KEY "/"
credential_scope ",SignedHeaders=" signed_header ",Signature=" test_val;
chunk = curl_slist_append(chunk,buffUser.c_str());
buffUser = "x-amz-content-sha256:" payload_hash;
chunk = curl_slist_append(chunk,buffUser.c_str());
buff = amzdate;
buffUser = "x-amz-date:" buff;
chunk = curl_slist_append(chunk,buffUser.c_str());
buffUser = "x-amz-decoded-content-length: 1024";
chunk = curl_slist_append(chunk,buffUser.c_str());
curl_easy_setopt(curl,CURLOPT_HTTPHEADER,chunk);
curl_easy_setopt(curl,CURLOPT_READDATA,fileOpen);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
// }
fclose (fileOpen);
printf("Fin programme \n");
return 0;
}
The signature fonction, i can verify this with an other programme and, the GET methode use this.
CodePudding user response:
If you use CURLOPT_PUT, you should also use CURLOPT_INFILESIZE or similar to tell libcurl how big the body of the request is that you are going to send (in your case, the number of bytes in <file>
).
If the size is known in advance, there is no longer a need to use chunked encoding, therefore no longer a need to send a Transfer-Encoding
header, and this error should go away.
Unrelated to your question, consider the CURLOPT_AWS_SIGV4 libcurl option if you haven't already.
CodePudding user response:
I fixe the bug, i use CURLOPT_INFILESIZE and i add new line for calculate the flux.
[NEW CODE]
#include <stdio.h>
#include <curl/curl.h>
#include <iostream>
#include "sha256_calc.h"
#include <ctime>
#include <hmac_sha256/hmac_sha256.h>
#include <vector>
using std::string;
using std::cout;
using std::endl;
#define BUCKET_NAME "<bucket>"
#define REGION "eu-west-3"
#define METHODE_PUT "PUT"
#define SERVICE_S3 "s3"
#define ALGORITHM "AWS4-HMAC-SHA256"
#define ACCESS_KEY "<access_key>"
#define SECRET_KEY "<secret_key>"
#define SHA256_HASH_SIZE 32
string vector_to_string (std::vector<uint8_t> val)
{
string retour = "";
char res[2048];
char intermedaire[2];
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
//sprintf(intermedaire,"%x",val[i]);
retour = val[i];
}
return retour;
}
std::vector<uint8_t> sign(string key,string msg)
{
std::vector<uint8_t> out(SHA256_HASH_SIZE);
hmac_sha256(key.data(), key.size(), msg.data(), msg.size(),out.data(), out.size());
return out;
}
std::vector<uint8_t> getSignatureKey(string key,string dateStamp,string regionName,string serviceName)
{
printf("key : %s \n" , key.c_str());
std::vector<uint8_t> kDate = sign("AWS4" key, dateStamp);
string buff = vector_to_string (kDate);
std::vector<uint8_t> kRegion = sign(buff, regionName);
buff = vector_to_string (kRegion);
std::vector<uint8_t> kService = sign(buff, serviceName);
buff = vector_to_string (kService);
std::vector<uint8_t> kSigning = sign(buff, "aws4_request");
return kSigning;
}
int main(void)
{
CURL *curl;
CURLcode res;
curl_slist *chunk=NULL;
char *file = (char*)"<file>";
char end_point[2048];
char host[1024];
sprintf(host,"%s.s3.%s.amazonaws.com",BUCKET_NAME,REGION);
sprintf(end_point,"https://%s/<file>",host);
time_t now = time(0);
tm *gmtm = gmtime(&now);
char amzdate[32];
char datestamp[32];
sprintf(amzdate,"dddTdddZ",gmtm->tm_year 1900,gmtm->tm_mon 1,gmtm->tm_mday,gmtm->tm_hour,gmtm->tm_min,gmtm->tm_sec);
//sprintf(amzdate,"dddTdddZ",1900 gmtm->tm_year,gmtm->tm_mon 1,gmtm->tm_mday,8,45,25);
sprintf(datestamp,"ddd",1900 gmtm->tm_year,gmtm->tm_mon 1,gmtm->tm_mday);
cout << gmtm->tm_hour << endl;
string canonical_uri;
canonical_uri = (string)"/" file;
string test = "";
char val;
FILE *fileTest = fopen("<file_local>","rb");
if (fileTest)
{
val = fgetc(fileTest);
while (!feof(fileTest))
{
test = val;
val = fgetc(fileTest);
}
fclose(fileTest);
}
else
{
printf("fichier introuvable");
}
printf("taille test : %d",test.length());
string payload_hash = sha256(test);
string canonical_headers;
canonical_headers = (string)"host:" host "\nx-amz-content-sha256:" payload_hash "\nx-amz-date:" amzdate "\n";
string signed_header = "host;x-amz-content-sha256;x-amz-date";
string canonicale_request;
canonicale_request = (string) METHODE_PUT "\n" canonical_uri "\n\n" canonical_headers "\n" signed_header "\n" payload_hash;
printf ("%s\n",canonicale_request.c_str());
string credential_scope;
credential_scope = (string) datestamp "/" REGION "/" SERVICE_S3 "/aws4_request";
string string_to_sign;
string_to_sign = (string) ALGORITHM "\n" amzdate "\n" credential_scope "\n" sha256(canonicale_request);
char *pCACertFile = (char*)"C:/Users/merop/Documents/build-test_api-Desktop_Qt_5_15_2_MinGW_32_bit-Debug/debug/cacert.pem";
std::vector<uint8_t> signing_key ;
signing_key = getSignatureKey(SECRET_KEY,datestamp,REGION,SERVICE_S3);
string buff = vector_to_string(signing_key);
std::vector<uint8_t> signature(SHA256_HASH_SIZE);
string string_to_sign_buff = string_to_sign;
hmac_sha256(buff.data(), buff.size(), string_to_sign_buff.data(), string_to_sign_buff.size(),signature.data(), signature.size());
printf("\n");
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
printf("%d\n",signing_key[i]);
}
printf("\n");
printf("\n %s \n",string_to_sign_buff.c_str());
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
printf("%d\n",signature[i]);
}
FILE *fileOpen = fopen("<file_local>","rb");
if (fileOpen)
{
printf("fichier ok \n");
}
else
{
printf("erreur\n");
}
FILE *f = fopen(pCACertFile,"r");
if (f)
{
fclose(f);
}
else
{
printf("certificat abscent");
}
string buffUser="";
curl=curl_easy_init();
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_URL, end_point);
curl_easy_setopt(curl, CURLOPT_PUT, 1L);
buff = credential_scope;
buff = ACCESS_KEY string("/") buff;
printf("%s\n",buff.c_str());
string test_val = "";
char tampon[8];
for (int i = 0; i < SHA256_HASH_SIZE; i )
{
sprintf(tampon,"x",signature[i]);
test_val = tampon;
}
buffUser = (string) "Authorization:" ALGORITHM " Credential=" ACCESS_KEY "/" credential_scope ",SignedHeaders=" signed_header ",Signature=" test_val;
chunk = curl_slist_append(chunk,buffUser.c_str());
buffUser = "x-amz-content-sha256:" payload_hash;
chunk = curl_slist_append(chunk,buffUser.c_str());
buff = amzdate;
buffUser = "x-amz-date:" buff;
chunk = curl_slist_append(chunk,buffUser.c_str());
buffUser = "Content-Length: 80512 ";
chunk = curl_slist_append(chunk,buffUser.c_str());
buffUser="Connection: keep-alive";
chunk = curl_slist_append(chunk,buffUser.c_str());
buffUser="Expect: 100-continue";
chunk = curl_slist_append(chunk,buffUser.c_str());
buffUser="Content-Type: text/plain";
chunk = curl_slist_append(chunk,buffUser.c_str());
curl_easy_setopt (curl,CURLOPT_HTTP_TRANSFER_DECODING,1);
curl_easy_setopt(curl,CURLOPT_INFILESIZE,1);
curl_easy_setopt(curl,CURLOPT_HTTPHEADER,chunk);
curl_easy_setopt(curl, CURLOPT_READDATA, fileOpen);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
fclose (fileOpen);
printf("Fin programme \n");
return 0;
}
WARNING it's a dev code, it's possible it don't work, take care. (and we can clean the code)