【问题标题】:How can I access Amazon AWS S3 using GSOAP for C and C++?如何使用适用于 C 和 C++ 的 GSOAP 访问 Amazon AWS S3?
【发布时间】:2011-08-10 12:23:29
【问题描述】:

我到处搜索了这个,我找不到一个像样的代码。如何使用GSOAP 访问Amazon AWS S3 服务?

【问题讨论】:

  • 我知道这不是一个真正的问题,但我也想知道使用 gsoap + aws 来制作一个 c/c++ 亚马逊应用程序。谢谢你,即使它确实被锁定/downvoted/tos'd:\
  • 您能否将其格式化为正确的问题/答案对?见blog.stackoverflow.com/2011/07/…
  • CodeProject about gSOAP and AWS S3 有一篇关于这个主题的文章更详细。该文章的副本也出现在 gSOAP 开发者网站上。不知道为什么版主用这些指针删除了我的答案。

标签: c++ c amazon-s3 signature gsoap


【解决方案1】:

以下代码来自 OP。最初,该帖子包含问题和答案,我将其转换为问答格式。

签名必须是格式

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

HMAC、SHA1 和 B64 实用程序可在 openssl.

SOAP 请求的格式由 wsdl.

REST 接口不同。

wsdl2h 之后生成标头,soapcpp2 生成 GSOAP 客户端代码以下将是访问服务的代码:

要求:OpenSSLGSOAP

使用编译器预处理指令WITH_OPENSSL 构建。链接到 库libeay32ssleay32

#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】:

    如何使用 GSOAP for C 和 C++ 访问 Amazon AWS S3?

    第 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 源代码而不是默认的C++ 源代码。 typemap.dat 文件位于 gSOAP 发行版的 gsoap 目录中。

    第 2 步

    对从 wsdl2h 工具创建的头文件使用 soapcpp2 工具。

    soapcpp2 -C -j aws-s3.h

    这会从 aws-s3.h 标头生成带有 C++ 服务代理和对象(-j 选项)的客户端代码(-C 选项)。 C 代码省略-j

    第 3 步

    使用自动生成的 AmazonS3SoapBindingProxy 代理方法访问 AWS S3 并为 AWS S3 创建 base64 编码的 HMAC-SHA1 哈希签名。签名是一个带有 HMAC-SHA1 哈希字符串 "AmazonS3" + OPERATION_NAME + Timestamp 的 base64 编码版本的字符串:

    /*
        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(&amp;createBucketReq, &amp;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,会创建一个新的bucket。

    在最终的 .cpp 文件中,请注意我们在设置 credentialsFile 和 bucketName 时会检查命令行参数 (argv)。这允许使用参数调用程序:

    ./createbucket BucketName path_to_credentials_file
    

    有关所有这些的更多详细信息,我建议阅读 Chris Moutsos 在How to use AWS S3 in C++ with gSOAP 上撰写的优秀 CodeProject 文章,该解释的部分内容来自该文章。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-25
      • 2021-01-20
      • 2017-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多