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

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

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

签名必须是格式

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

HMAC,SHA1和B64工具可在openssl中使用 。

SOAP请求的格式由wsdl给出。

REST界面不同。

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

要求: OpenSSL , GSOAP 。

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

 #include "AmazonS3SoapBinding.nsmap" //generated from soapcpp2 #include "soapAmazonS3SoapBindingProxy.h" //generated from soapcpp2 #include  #include  #include  #include  #include  #include  /* 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 <::iterator itr; for(itr=buk_resp.ListAllMyBucketsResponse->Buckets->Bucket.begin(); itr!=buk_resp.ListAllMyBucketsResponse->Buckets->Bucket.end(); itr++ ) { std::cout<<(*itr)->Name< 

如何使用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散列签名。 签名是一个字符串,其中"AmazonS3" + OPERATION_NAME + Timestamp -SHA1散列字符串"AmazonS3" + OPERATION_NAME + Timestamp的base64编码版本:

 /*  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'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 << "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标头具有附件大小,因此攻击者可以将该任意大小设置为耗尽内存资源。 其他post没有提到这一点。

运行createbucket ,将创建一个新存储桶。

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

 ./createbucket BucketName path_to_credentials_file 

有关所有这些的更多详细信息,我建议阅读优秀的CodeProject文章,该文章是关于如何在C ++中使用AWS S3与 Chris Moutsos的gSOAP,其中部分原因来自于此。