如何使用适用于 C 和 C++ 的 GSOAP 访问 Amazon AWS S3?

问题描述 投票:0回答:2

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

c++ c amazon-s3 signature gsoap
2个回答
3
投票

下面的代码来自OP。最初,该帖子包含问题和答案,我将其变成问答格式。

签名必须采用以下格式

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

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

SOAP 请求的格式由 wsdl.

REST接口不同。

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

要求:OpenSSLGSOAP

使用编译器预处理器指令构建

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
投票

如何使用适用于 C 和 C++ 的 GSOAP 访问 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++ 服务代理和对象(

-C
选项)的客户端代码(
-j
选项)。对于 C 代码,省略
-j

第3步

使用自动生成的

AmazonS3SoapBindingProxy
代理方法访问 AWS S3 并为 AWS S3 创建 Base64 编码的 HMAC-SHA1 哈希签名。签名是一个带有 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)。这允许使用参数调用程序:

./createbucket BucketName path_to_credentials_file

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

© www.soinside.com 2019 - 2024. All rights reserved.