如何使用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
构建。 链接库libeay32
和ssleay32
。
#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 <
如何使用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,其中部分原因来自于此。