2011-08-10 64 views
11

我到處搜索了這個,我找不到一個體面的代碼。如何使用GSOAP訪問Amazon AWS S3服務?如何使用適用於C和C++的GSOAP訪問Amazon AWS S3?

+0

我知道這不是一個真正的問題,但我也想知道gsoap + aws是如何製作c/C++ amazon應用程序的。感謝你,即使它被鎖定/ downvoted/tos'd:\ –

+2

你能否請格式化成適當的問題/答案對?請參閱http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/ –

+0

有關於此主題的文章發佈在[CodeProject關於gSOAP和AWS S3 ](https://www.codeproject.com/Articles/1108296/How-to-Use-Amazon-Simple-Storage-Service-S-in-Cplu)更詳細。該文章的副本也出現在gSOAP開發人員網站上。不知道爲什麼主持人用這些指針去掉了我的答案。 –

回答

3

以下代碼來自OP。最初,這篇文章包含了問題和答案,我將它轉換爲Q格式。

簽名必須是格式

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

的HMAC,SHA1and B64 utils的是 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; 
    } 

} 
0

我怎樣才能訪問Amazon AWS S3使用gSOAP的C和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源代碼,而不是默認的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的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文件中,請注意我們在設置credentialsFile和bucketName時檢查命令行參數(argv)。這使得程序與參數調用:

./createbucket BucketName path_to_credentials_file 

有關這一切的詳細信息,我建議閱讀由Chris Moutsos上How to use AWS S3 in C++ with gSOAP優秀CodeProject上的文章從這種解釋部分起源。