Как я могу получить доступ к Amazon AWS S3, используя GSOAP для C и C ++? - PullRequest
11 голосов
/ 10 августа 2011

Я искал это везде и не смог найти ни одного достойного кода. Как получить доступ к сервису Amazon AWS S3 , используя GSOAP ?

Ответы [ 2 ]

3 голосов
/ 15 октября 2011

Код ниже взят из OP.Первоначально пост содержал как вопрос, так и ответ, и я превращаю его в формат вопросов и ответов.

Подпись должна иметь формат

base64encode((HMAC-SHA1(ActionName+"AmazonS3"+XMLTimestamp)))

Используется HMAC, SHA1 и B64.доступны в openssl .

Формат запросов SOAP задается wsdl .

Интерфейс REST отличается.

После wsdl2h для генерации заголовка и soapcpp2 для генерации кода клиента GSOAP следующий код будет для доступа к сервису:

Требования: OpenSSL , 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;
    }

}
0 голосов
/ 07 июля 2017

Как получить доступ к Amazon AWS S3 с помощью GSOAP для C и C ++?

Шаг 1

Использовать инструмент gSOAP wsd2lh для конвертации AmazonS3 WSDL в файл заголовка интерфейса aws-s3.h :

wsdl2h -t typemap.dat -o aws-s3.h http://doc.s3.amazonaws.com/2006-03-01/AmazonS3.wsdl

Используйте параметр -c для создания исходного кода C вместо исходного C ++ по умолчаниюкод.Файл typemap.dat находится в каталоге gsoap дистрибутива gSOAP.

Шаг 2

Используйте инструмент soapcpp2 для файла заголовка, созданного из инструмента wsdl2h.

soapcpp2 -C -j aws-s3.h

Thisгенерирует код на стороне клиента (опция -C) с прокси-серверами и объектами службы C ++ (опция -j) из заголовка aws-s3.h.Пропустите -j для кода C.

Шаг 3

Используйте автоматически сгенерированные прокси-методы AmazonS3SoapBindingProxy для доступа к AWS S3 и создания HMAC в кодировке base64-SHA1 хэшированная подпись для AWS S3.Сигнатура представляет собой строку с версией хешированной строки HMAC-SHA1 в кодировке base64 "AmazonS3" + OPERATION_NAME + Timestamp:

/*
    createbucket.cpp
    Example AWS S3 CreateBucket service invocation
*/

#include "soapAmazonS3SoapBindingProxy.h"
#include "AmazonS3SoapBinding.nsmap"
#include &lt;fstream>

// Make allocation of primitive values quick and easy:
template&lt;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 &lt;&lt; "Couldn't read AWS keys for user " &lt;&lt; user 
                  &lt;&lt; " from file " &lt;&lt; credentialsFile &lt;&lt; '\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's 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 &lt;&lt; "You are the owner of bucket '" &lt;&lt; result.BucketName &lt;&lt; "'." &lt;&lt; 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 в C ++ с gSOAP Крисом Мутсосом, из которого происходят части этого объяснения.

...