Access Amazon S3 services with C++ GSOAP client - c++

I am starting to develop an app to access the Amazon S3 storage using the SOAP API.
I have read the documents that says the the method PutObject must be used if the file size is greater than 1 MB.
Now PutObject uses DIME attachment.
Is there a sample code or example or a fragment of code that someone can show me on how to do DIME attachement with GSOAP for the PutObject method of Amazon S3.
I want to use GSOAP because of portability and to make it generic. I do not want to use the .NET API provided by Amazon for the same reason. I want in GSOAP particularly as I have worked in GSOAP earlier.
Thanks,
david

I put together something that uploads files larger than 1MB using PutObject, it should also work for smaller files.
I share it for others who might find it useful.
Also see my previous post on using GSOAP to access S3 AMAZON AWS S3 using GSOAP C C++
The link also contains the method to generate the signature.
Here is the code for PutObject.
It uses the latest GSOAP from sourceforge.
After wsdl2h to generate the header and soapcpp2 to generate the gsoap client code the following will be the code to access the service PutObject......
Requirements : OpenSSL GSOAP Build with the compiler preprocessor directive WITH_OPENSSL. Include the library files libeay32 and ssleay32. Take the methods to generate signature from the link above.
void PutObject(char *filename)
{
AmazonS3SoapBindingProxy amazonS3Interface;
struct soap* soapPtr;
soapPtr = dynamic_cast<struct soap*>(&amazonS3Interface);
soap_init2(soapPtr, SOAP_IO_DEFAULT|SOAP_IO_KEEPALIVE, SOAP_IO_DEFAULT|SOAP_IO_KEEPALIVE);
soap_ssl_client_context(&amazonS3Interface,
SOAP_SSL_NO_AUTHENTICATION, /* for encryption w/o 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 to connect to internet
amazonS3Interface.proxy_host="proxyservername"; //proxyservername
amazonS3Interface.proxy_port=4050; //proxy port
amazonS3Interface.proxy_userid="username"; //proxy authentication
amazonS3Interface.proxy_passwd="password";
amazonS3Interface.proxy_http_version="1.1"; //http ver
amazonS3Interface.dime_id_format ="uuid:09233523-345b-4351-b623-5dsf35sgs5d6-%x";
// Set callback functions
soapPtr->fdimereadopen = dime_read_open;
soapPtr->fdimereadclose = dime_read_close;
soapPtr->fdimeread =dime_read;
_ns1__PutObject preq;
_ns1__PutObjectResponse presp;
ns1__PutObjectResult res;
FILE *fp=fopen(filename,"rb");
fseek(fp, 0L, SEEK_END);
size_t sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
preq.Bucket=std::string("FGTSDrive");//bucket name to put file in
preq.AWSAccessKeyId=new std::string("ACCESSKEY");//access key here
char *sig=aws_signature("SECRETKEY","AmazonS3","PutObject",xml_datetime(),NULL);//correct secretkey here
preq.Signature=new std::string(sig);
preq.Timestamp=new time_t(time(NULL));
preq.Key=std::string(filename);//name of the key ie the filename
int result(0);
preq.ContentLength=sz; //length of the file
ns1__MetadataEntry med;
med.Name=std::string("Content-Type");
med.Value=std::string("application/zip");//change the type depending on the file extenstion
med.soap=&amazonS3Interface;
preq.Metadata.push_back(&med);
soap_set_dime(soapPtr);
result =soap_set_dime_attachment(soapPtr, (char*)fp, sz,"application/zip", NULL, 0,filename);//change the content type depending on the file extenstion
if (result != SOAP_OK) { }
result = amazonS3Interface.PutObject(&preq, &presp);
if (result != SOAP_OK) { }
amazonS3Interface.soap_stream_fault(std::cout);
}
static void *dime_read_open(struct soap *soap, void *handle, const char *id, const char *type, const char *options)
{ // we should return NULL without setting soap->error if we don't want to use the streaming callback for this DIME attachment. The handle contains the non-NULL __ptr field value which should have been set in the application.
// return value of this function will be passed on to the fdimeread and fdimereadclose callbacks. The return value will not affect the __ptr field.
std::cout <<"dime_read_open"<<std::endl;
return handle;
}
static void dime_read_close(struct soap *soap, void *handle)
{
std::cout <<"dime_read_close"<<std::endl;
fclose((FILE*)handle);
}
static size_t dime_read(struct soap *soap, void *handle, char *buf, size_t len)
{
std::cout <<"dime_read_read"<<std::endl;
return fread(buf, 1, len, (FILE*)handle);
}
Hope it helps.
Thanks,
david

Related

Simple HTTP Request not functioning on HTTPS Websites?

I have a website that I need to pull information from, currently I use this code:
std::stringstream Output;
IStream* Stream;
HRESULT StreamResult = URLOpenBlockingStream(0, WebsiteLink, &Stream, 0, 0);
if (SUCCEEDED(StreamResult))
{
char sBuffer[100];
unsigned long CurrentBRead;
Stream->Read(sBuffer, 100, &CurrentBRead);
while (CurrentBRead > 0U)
{
Output.write(sBuffer, (long long)CurrentBRead);
Stream->Read(sBuffer, 100, &CurrentBRead);
}
Stream->Release();
*Return = Output.str();
return true;
}
When I use this on HTTP websites, it functions properly. But when I need to access anything using HTTPS, it just hangs. After some time, I realized that there is a handshake that I must do to authenticate myself with the website, however I don't know how to do this, and all of the solutions online are just "use libcurl".
Note: I cannot use ANY 3rd party libraries

Libarchive - Creating a single .zip archive with both encrypted(PKzip) and un-encrypted files together

I am trying out libarchive to try to find out whether it is useful for my purpose.
I have a .zip file, with few files encrypted (PKzip) and few other files un-encrypted.
I am able to successfully extract my .zip file using libarchive without a problem.
But when I try to create such an archive, I am not able to do it. Any guidance is appreciated.
[Working Fine] Extraction
void Extract()
{
// In
archive *ina = archive_read_new();
archive_entry *entry;
archive_read_support_format_zip(ina);
archive_read_set_passphrase_callback(ina, &count, ReadPasswordCallback); // ReadPasswordCallback - Supplies nullptr/emptry string for extracting files with no password, and give appropriate password for extracting files with password
archive_read_open_filename(ina, "test.zip", 10240); // ARCHIVE_OK received here
// Out
archive *ext = archive_write_disk_new();
archive_write_disk_set_options(ext, flags);
// Extraction
for(;;)
{
archive_read_next_header(ina, &entry); // ARCHIEVE_OK received here
archive_write_header(ext, entry); // ARCHIEVE_OK received here
copy_data(ina, ext); // copy_data ** defined at the bottom **
// Completion of write entry
archive_write_finish_entry(ext);
}
// Close and Cleanup
archive_read_close(ina);
archive_read_free(ina);
archive_write_close(ext);
archive_write_free(ext);
}
**[NOT WORKING] Cloning - Read the same zip file and create a copy (with few files as encrypted, few others as un-encrypted as per the original zip file) and **
void Clone()
{
// In
archive *ina = archive_read_new();
archive_entry *entry;
archive_read_support_format_zip(ina);
archive_read_set_passphrase_callback(ina, &count, ReadPasswordCallback); // ReadPasswordCallback - Supplies nullptr/emptry string for extracting files with no password, and give appropriate password for extracting files with password
archive_read_open_filename(ina, "test.zip", 10240); // ARCHIVE_OK received here
// Out
archive *oua = archive_write_new();
archive_write_set_format_zip(oua);
archive_write_set_options(oua, "zip:encryption=traditional");
archive_write_set_passphrase_callback(oua, nullptr, WritePasswordCallback); // WritePasswordCallback - Supplies nullptr/empty string for writing files with no password, and give appropriate password for writings files with password
archive_write_open_filename(oua, "testNEW.zip");
// Cloning
for(;;)
{
archive_read_next_header(ina, &entry); // ARCHIEVE_OK received here
archive_write_header(oua, entry); // ARCHIEVE_OK received here
copy_data(ina, oua); // copy_data ** defined at the bottom **
// Completion of write entry
archive_write_finish_entry(oua);
}
// Close and Cleanup
archive_read_close(ina);
archive_read_free(ina);
archive_write_close(oua);
archive_write_free(oua);
}
ReadPasswordCallback is called once per entry, hence I am able to decrypt files independently by supplying the file's corresponding password. For non-encrypted files, supplying nullptr works fine.
Whereas, with the write, the behaviour is different with the WritePasswordCallback invocation. The WritePasswordCallback is getting called "only once", not once per entry as ReadPasswordCallback. Hence I am not able to supply independent encryption key for the files. Also supplying a nullptr throws an error saying encryption key is necessary for encryption, hence I am not able to have few files as unencrypted
Is this a limitation of Libarchive library?
Please help, and thanks in advance!
static int
copy_data(struct archive *ar, struct archive *aw)
{
int r;
char buff[1024];
do
{
r = archive_read_data(ar, buff, sizeof(buff)); // Receives 0/EOF at the last read
if (r == 0 || r == ARCHIVE_EOF)
return (ARCHIVE_OK);
// Copy
r = archive_write_data(aw, buff, r); // Receive ARCHIVE_OK
} while (r > 0);
return ARCHIVE_FATAL;
}

Find service`s uuid of a bluetooth device

I'm trying to get information about services provided by bluetooth devices. Using WSALookupServiceNext I can get list of all services. However, regardless of what bluetooth device I try to discover, the resulting WSAQUERYSET structure (pqs) never contains lpServiceClassId field (while the lpszServiceInstanceName is always present). I want to know UUID of a service to determine to which class of the Table 2: Service Class Profile Identifiers it belongs.
#include<winsock2.h>
int main()
{
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
WSAQUERYSET qs{};
qs.dwSize = sizeof(WSAQUERYSET);
qs.dwNameSpace = NS_BTH;
qs.dwNumberOfCsAddrs = 0;
qs.lpszContext = (LPWSTR)L"12:34:56:78:99:11";
qs.lpServiceClassId = const_cast<LPGUID>(&PublicBrowseGroupServiceClass_UUID);
const DWORD flags = LUP_FLUSHCACHE | LUP_RETURN_ALL;
HANDLE hlookup = nullptr;
WSALookupServiceBegin(&qs, flags, &hlookup);
while (true) {
char buff[4096];
WSAQUERYSET* pqs = (WSAQUERYSET*)buff;
DWORD size = sizeof(buff);
memset(buff, 0, size);
const INT res = WSALookupServiceNext(hlookup, flags, &size, pqs);
if (res != 0 && GetLastError() == WSA_E_NO_MORE) {
break;
}
// it prints "service name=Advanced Audio, service uuid=0x0"
wprintf(L"service name=%s, service uuid=0x%X\n", pqs->lpszServiceInstanceName, pqs->lpServiceClassId->Data1);
}
}
Answering my own question:
In order to get service ID we have to parse SPD structures which are returned from WSALookupServiceNext function call and accessible via WSAQUERYSET.lpBlob member.
For more details see Mike Petrichenko's comment.
Also, there is a very good example how to implement it from Qt framework source code.
As for myself, I decided not to follow this path, because my goal was to write a console utility for pairing Bluetooth devices. I have found that there is a better way to do that using Windows.Devices.Enumeration API. Finally, using this API, I managed to create the BluetoothDevicePairing utility I was working on.

How to use X509_verify()

How can we use X509_verify(). I have two certificates. The first certificate is the Root Certificate which signed the next certificate (which is my Certificate). So I want to check if my certificate is signed by the root certificate using x509_verify() in C++. My goal is to keep the code simple and Understandable so I can also put it online.
Signature of X509_verify is
int X509_verify(X509 * x509, EVP_PKEY * pkey);
Suppose of you have root certificate in root and your certificate in mycert;
X509 * root;
X509 * mycert;
//Get root certificate into root
//Get mycert into mycert.
//Get the public key.
EVP_PKEY * pubkey = X509_get_pubkey(root);
//verify. result less than or 0 means not verified or some error.
int result = X509_verify(mycert, pubkey);
//free the public key.
EVP_PKEY_free(pubkey);
I think this would help you.
I think dbasic and Balamurugan answered how to use it. Here's how to interpret the errors you get from it. I find error handling is much more important than business logic because nearly anyone can copy/paste code that works in a benign environment. How you respond to failures, broken/bad inputs and a hostile environments matter more.
The source code for the function is in <openssl dir>/crypto/x509/x_all.c:
int X509_verify(X509 *a, EVP_PKEY *r)
{
return(ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF),a->sig_alg,
a->signature,a->cert_info,r));
}
ASN1_item_verify id defined in <openssl dir>/crypto/asn1/a_verify.c:
int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *alg,
ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey)
{
...
}
The function returns -1 on failure with one of the following error codes:
ERR_R_PASSED_NULL_PARAMETER if pkey is NULL
ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM if alg is unknown using an OID lookup
ASN1_R_WRONG_PUBLIC_KEY_TYPE if the pkey type does not match the pkey->method
ERR_R_MALLOC_FAILURE if a buffer allocation fails
The function returns 0 on failure with one of the following error codes:
ERR_R_EVP_LIB if EVP_DigestVerifyInit fails
ERR_R_EVP_LIB if EVP_DigestVerifyUpdate fails
ERR_R_EVP_LIB if EVP_DigestVerifyFinal fails
On success, the function returns 1 (from around line 220):
...
if (EVP_DigestVerifyFinal(&ctx,signature->data,
(size_t)signature->length) <= 0)
{
ASN1err(ASN1_F_ASN1_ITEM_VERIFY,ERR_R_EVP_LIB);
ret=0;
goto err;
}
ret=1;
err:
EVP_MD_CTX_cleanup(&ctx);
return(ret);
} /* End of function */
From <openssl dir>/crypto/err/err.h, you use ERR_get_error() to retrieve the error code:
err.h:#define ASN1err(f,r) ERR_PUT_error(ERR_LIB_ASN1,(f),(r),__FILE__,__LINE__)
Step1 : Read the certificate and convert the Certificate into X509 structure
// the below will show how to read the certificate from the file (DER or PEM Encoded)
X509* oCertificate=NULL;
FILE *lFp=NULL;
lFp=fopen(iFilePath.c_str(),"rb"); // iFilepath is the string
if(lFp==NULL)
{
oCertificate=NULL;
}
else
{
oCertificate = PEM_read_X509(lFp, NULL, NULL, NULL);
if (oCertificate == NULL )
{
//Certificate may be DER encode
oCertificate = d2i_X509_fp(lFp, NULL);
}
fclose(lFp);
}
// OCertificate contains
Step 2: now read the Root certificate key
(Note check the X509 is NULL or not before use)
Step 3 : use the X509_verify() function.

Simple AES encryption using WinAPI

I need to do simple single-block AES encryption / decryption in my Qt / C++ application. This is a "keep the honest people honest" implementation, so just a basic encrypt(key, data) is necessary--I'm not worried about initialization vectors, etc. My input and key will always be exactly 16 bytes.
I'd really like to avoid another dependency to compile / link / ship with my application, so I'm trying to use what's available on each platform. On the Mac, this was a one-liner to CCCrypt. On Windows, I'm getting lost in the API from WinCrypt.h. Their example of encrypting a file is almost 600 lines long. Seriously?
I'm looking at CryptEncrypt, but I'm falling down the rabbit hole of dependencies you have to create before you can call that.
Can anyone provide a simple example of doing AES encryption using the Windows API? Surely there's a way to do this in a line or two. Assume you already have a 128-bit key and 128-bits of data to encrypt.
Here's the best I've been able to come up with. Suggestions for improvement are welcome!
static void encrypt(const QByteArray &data,
const QByteArray &key,
QByteArray *encrypted) {
// Create the crypto provider context.
HCRYPTPROV hProvider = NULL;
if (!CryptAcquireContext(&hProvider,
NULL, // pszContainer = no named container
NULL, // pszProvider = default provider
PROV_RSA_AES,
CRYPT_VERIFYCONTEXT)) {
throw std::runtime_error("Unable to create crypto provider context.");
}
// Construct the blob necessary for the key generation.
AesBlob128 aes_blob;
aes_blob.header.bType = PLAINTEXTKEYBLOB;
aes_blob.header.bVersion = CUR_BLOB_VERSION;
aes_blob.header.reserved = 0;
aes_blob.header.aiKeyAlg = CALG_AES_128;
aes_blob.key_length = kAesBytes128;
memcpy(aes_blob.key_bytes, key.constData(), kAesBytes128);
// Create the crypto key struct that Windows needs.
HCRYPTKEY hKey = NULL;
if (!CryptImportKey(hProvider,
reinterpret_cast<BYTE*>(&aes_blob),
sizeof(AesBlob128),
NULL, // hPubKey = not encrypted
0, // dwFlags
&hKey)) {
throw std::runtime_error("Unable to create crypto key.");
}
// The CryptEncrypt method uses the *same* buffer for both the input and
// output (!), so we copy the data to be encrypted into the output array.
// Also, for some reason, the AES-128 block cipher on Windows requires twice
// the block size in the output buffer. So we resize it to that length and
// then chop off the excess after we are done.
encrypted->clear();
encrypted->append(data);
encrypted->resize(kAesBytes128 * 2);
// This acts as both the length of bytes to be encoded (on input) and the
// number of bytes used in the resulting encrypted data (on output).
DWORD length = kAesBytes128;
if (!CryptEncrypt(hKey,
NULL, // hHash = no hash
true, // Final
0, // dwFlags
reinterpret_cast<BYTE*>(encrypted->data()),
&length,
encrypted->length())) {
throw std::runtime_error("Encryption failed");
}
// See comment above.
encrypted->chop(length - kAesBytes128);
CryptDestroyKey(hKey);
CryptReleaseContext(hProvider, 0);
}