Я искал везде для этого, и я не мог найти ни одного достойного кода. Как я могу получить доступ к услуге Amazon AWS S3 с помощью GSOAP
Как я могу получить доступ к Amazon AWS S3, используя GSOAP для C и С++?
Ответ 1
Ниже приведен код из OP. Первоначально сообщение содержало как вопрос, так и ответ, и я превращаю его в формат Q & A.
Подпись должна иметь формат
base64encode((HMAC-SHA1(ActionName+"AmazonS3"+XMLTimestamp)))
Утилиты HMAC, SHA1 и B64 доступны в openssl.
Формат запросов SOAP задается wsdl.
Интерфейс REST отличается.
После wsdl2h
для создания заголовка и soapcpp2 для генерации GSOAP
Клиентский код следующим кодом будет доступ к службе:
Построить с помощью директивы препроцессора компилятора WITH_OPENSSL
. Связать с
библиотеки libeay32
и ssleay32
.
#include "AmazonS3SoapBinding.nsmap" //generated from soapcpp2
#include "soapAmazonS3SoapBindingProxy.h" //generated from soapcpp2
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
/* convert to base64 */
std::string base64_encodestring(char* text, int len) {
EVP_ENCODE_CTX ectx;
int size = len*2;
size = size > 64 ? size : 64;
unsigned char* out = (unsigned char*)malloc( size );
int outlen = 0;
int tlen = 0;
EVP_EncodeInit(&ectx);
EVP_EncodeUpdate(&ectx,
out,
&outlen,
(const unsigned char*)text,
len
);
tlen += outlen;
EVP_EncodeFinal( &ectx, out+tlen, &outlen );
tlen += outlen;
std::string str((char*)out, tlen );
free( out );
return str;
}
/* return the utc date+time in xml format */
const char* xml_datetime() {
/*"YYYY-mm-ddTHH:MM:SS.000Z\"*/
const int MAX=25;
static char output[MAX+1];
time_t now = time(NULL);
strftime( output, MAX+1, "%Y-%m-%dT%H:%M:%S.000Z", gmtime( &now ) );
std::cout <<output<<std::endl;
return output;
}
/* first argument is the signing key */
/* all subsequent argumets are concatenated */
/* must end list with NULL */
char* aws_signature(char* key, ...) {
unsigned int i, len;
char *data, **list = &key;
static char hmac[EVP_MAX_MD_SIZE];
for (i = 1, len = 0; *(list+i) != NULL; ++i) {
len += strlen( *(list+i) );
}
data = (char*)malloc(sizeof(char) * (len+1));
if (data) {
for ( i = 1, len = 0 ; *(list+i) != NULL ; ++i ) {
strncpy( data+len, *(list+i), strlen(*(list+i)) );
len += strlen(*(list+i));
}
data[len]='\0';
std::cout<<data<<std::endl;
HMAC( EVP_sha1(),
key, strlen(key),
(unsigned char*)data, strlen(data),
(unsigned char*) hmac, &len
);
free(data);
}
std::string b64data=base64_encodestring(hmac, len);
strcpy(hmac,b64data.c_str());
return hmac;
};
int main(void) {
AmazonS3SoapBindingProxy client;
soap_ssl_client_context(&client,
/* for encryption w/o authentication */
SOAP_SSL_NO_AUTHENTICATION,
/* SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK, */
/* if we don't want the host name checks since
* these will change from machine to machine */
/*SOAP_SSL_DEFAULT,*/
/* use SOAP_SSL_DEFAULT in production code */
NULL, /* keyfile (cert+key): required only when
client must authenticate to server
(see SSL docs to create this file) */
NULL, /* password to read the keyfile */
NULL, /* optional cacert file to store trusted
certificates, use cacerts.pem for all
public certificates issued by common CAs */
NULL, /* optional capath to directory with trusted
certificates */
NULL /* if randfile!=NULL: use a file with random
data to seed randomness */
);
/* use this if you are behind a proxy server.....
client.proxy_host="proxyserver"; // proxy hostname
client.proxy_port=4250;
client.proxy_userid="username"; // user pass if proxy
client.proxy_passwd="password"; // requires authentication
client.proxy_http_version="1.1"; // http version
*/
_ns1__ListAllMyBuckets buk_req;
_ns1__ListAllMyBucketsResponse buk_resp;
ns1__ListAllMyBucketsResult buk_res;
buk_res.soap=&client;
buk_req.AWSAccessKeyId=new std::string("ACCESSKEY");
buk_req.soap=&client;
/* ListAllMyBuckets is the method I want to call here.
* change it for other S3 services that you wish to call.*/
char *sig=aws_signature(
"SECRETKEY",
"AmazonS3",
"ListAllMyBuckets",
xml_datetime(),
NULL
);
buk_req.Signature=new std::string(sig);
buk_req.Timestamp=new time_t(time(NULL));
buk_resp.soap=&client;
buk_resp.ListAllMyBucketsResponse=&buk_res;
client.ListAllMyBuckets(&buk_req,&buk_resp);
client.soap_stream_fault(std::cout);
std::vector<ns1__ListAllMyBucketsEntry * >::iterator itr;
for(itr=buk_resp.ListAllMyBucketsResponse->Buckets->Bucket.begin();
itr!=buk_resp.ListAllMyBucketsResponse->Buckets->Bucket.end();
itr++
) {
std::cout<<(*itr)->Name<<std::endl;
}
}
Ответ 2
Как я могу получить доступ к Amazon AWS S3 с помощью GSOAP для C и С++?
Шаг 1
Используйте инструмент gSOAP wsd2lh для преобразования Amazon S3 WSDL в файл заголовка интерфейса aws-s3.h:
wsdl2h -t typemap.dat -o aws-s3.h http://doc.s3.amazonaws.com/2006-03-01/AmazonS3.wsdl
Используйте опцию -c
, чтобы сгенерировать исходный код C вместо исходного кода на С++. Файл typemap.dat
находится в каталоге gsoap дистрибутива gSOAP.
Шаг 2
Используйте инструмент soapcpp2 в файле заголовка, созданном из инструмента wsdl2h.
soapcpp2 -C -j aws-s3.h
Это генерирует клиентский код (-c
) с прокси-серверами и объектами службы С++ (-j
) из заголовка aws-s3.h. Опустите -j
для кода C.
Шаг 3
Используйте автогенерированные методы прокси-сервера AmazonS3SoapBindingProxy
для доступа к AWS S3 и создайте сигнатуру хеширования HMAC-SHA1 с base64 для AWS S3. Подпись представляет собой строку с кодировкой base64 версии хешированной строки HMAC-SHA1 "AmazonS3" + OPERATION_NAME + Timestamp
:
/*
createbucket.cpp
Example AWS S3 CreateBucket service invocation
*/
#include "soapAmazonS3SoapBindingProxy.h"
#include "AmazonS3SoapBinding.nsmap"
#include <fstream>
// Make allocation of primitive values quick and easy:
template<class T>
T * soap_make(struct soap *soap, T val) {
T *p = (T*)soap_malloc(soap, sizeof(T));
*p = val;
return p;
}
// Make base64-encoded, HMAC-SHA1 hashed signature for AWS S3
std::string soap_make_s3__signature(struct soap *soap, char const *operation, char const *key) {
std::string signature = "AmazonS3";
signature += operation;
char UTCstamp[40]; //to hold ISO 8601 time format
time_t now;
time(&now);
strftime(UTCstamp, sizeof UTCstamp, "%Y-%m-%dT%H:%M:%S.000Z", gmtime(&now));
signature += UTCstamp;
// Get the HMAC-SHA1 digest of the signature string
unsigned char * digest;
digest = HMAC(EVP_sha1(), key, strlen(key),
(unsigned char*)(signature.c_str()),
signature.length(), NULL, NULL);
char signatureBase64[20];
// Convert the digest to base64
soap_s2base64(soap, digest, signatureBase64, sizeof signatureBase64);
return std::string(signatureBase64);
}
// Read access keys from file generated by AWS CLI
bool getAWSKeys(std::string path, std::string user, std::string &accessKey, std::string &secretKey) {
std::ifstream credentialsFile(path.c_str());
if (!credentialsFile.is_open())
return false;
std::string line;
while (std::getline(credentialsFile, line)) {
// Keep going until we get to the desired user
if (line.find(user) == std::string::npos)
continue;
while (std::getline(credentialsFile, line)) {
// Keep going until we get to the access key lines
if (line.find("aws_access_key_id") == std::string::npos)
continue;
// Grab keys and trim whitespace
size_t first, last;
accessKey = line.substr(line.find_first_of('=')+1);
first = accessKey.find_first_not_of(' ');
if (first == std::string::npos)
return false;
last = accessKey.find_last_not_of(' ');
accessKey.substr(first, last-first+1).swap(accessKey);
std::getline(credentialsFile, line);
secretKey = line.substr(line.find_first_of('=')+1);
first = secretKey.find_first_not_of(' ');
if (first == std::string::npos)
return false;
last = secretKey.find_last_not_of(' ');
secretKey.substr(first, last-first+1).swap(secretKey);
return true;
}
}
return false;
}
int main(int argc, char **argv) {
// Load AWS keys from file
std::string accessKey, secretKey;
// Use the path to your AWS credentials file
std::string credentialsFile = (argc > 2 ? argv[2] : "path_to_aws_credentials_file");
std::string user = "default";
if (!getAWSKeys(credentialsFile, user, accessKey, secretKey)) {
std::cout << "Couldn't read AWS keys for user " << user
<< " from file " << credentialsFile << '\n';
return 0;
}
// Create a proxy to invoke AWS S3 services
AmazonS3SoapBindingProxy aws(SOAP_XML_INDENT);
// Create bucket
// Set the arguments of the CreateBucket service operation
_s3__CreateBucket createBucketReq;
std::string bucketName = (argc > 1 ? argv[1] : "BucketName");
createBucketReq.Bucket = bucketName;
createBucketReq.AWSAccessKeyId = soap_new_std__string(aws.soap);
*createBucketReq.AWSAccessKeyId = accessKey;
createBucketReq.Timestamp = soap_make(aws.soap, time(0));
createBucketReq.Signature = soap_new_std__string(aws.soap);
*createBucketReq.Signature = soap_make_s3__signature(aws.soap,
"CreateBucket",
secretKey.c_str());
// Store the result of the service
_s3__CreateBucketResponse createBucketRes;
// Create a bucket
if (aws.CreateBucket(&createBucketReq, createBucketRes)) {
aws.soap_stream_fault(std::cerr);
}
/*
NOTE: you must add the line:
_s3__CreateBucketResponse = $ s3__CreateBucketResult* CreateBucketResponse;
to the typemap.dat file because Amazon response doesn't match
their promised schema. This adds the variable CreateBucketResponse
to the _s3__CreateBucketResponse class so we can access the response.
*/
else if (createBucketRes.CreateBucketResponse) {
s3__CreateBucketResult &result = *createBucketRes.CreateBucketResponse;
std::cout << "You are the owner of bucket '" << result.BucketName << "'." << std::endl;
}
// Delete all managed data
aws.destroy();
return 0;
}
C-код выглядит аналогичным, причем основным отличием является использование вызовов функций вместо вызовов методов, т.е. soap_call___s3__CreateBucket(&createBucketReq, &createBucketRes)
. Все это объясняется в сгенерированном файле aws-s4.h.
Скомпилируйте сгенерированные файлы с исходным кодом:
c++ -DSOAP_MAXDIMESIZE=104857600 -DWITH_OPENSSL -o createbucket createbucket.cpp soapAmazonS3SoapBindingProxy.cpp soapC.cpp stdsoap2.cpp -lssl -lcrypto
SOAP_MAXDIMESIZE=104857600
гарантирует, что размеры вложений DIME могут быть достаточно большими, предотвращая атаки на отказ в обслуживании с использованием DIME. Заголовка DIME имеет размер вложения, поэтому злоумышленник может установить, что любые большие ресурсы памяти исчерпаны. Другие сообщения не упоминают об этом.
Запустите createbucket
, и будет создано новое ведро.
В финальном файле .cpp обратите внимание, что мы проверяем аргументы командной строки (argv) при установке credentialsFile и bucketName. Это позволяет вызывать программу с помощью аргументов:
./createbucket BucketName path_to_credentials_file
Для получения более подробной информации обо всем этом, я предлагаю прочитать отличную статью CodeProject на Как использовать AWS S3 в С++ с gSOAP Крис Мутос, из которого вытекают части этого объяснения.