[C++] Implementing SSL

Started by
5 comments, last by fitfool 13 years, 12 months ago
Hello, im developing an application that needs to be able to send emails. Unfortunately, almost all large free email providers(Gmail, Hotmail etc.) require encryption over SSL/TLS. I'm looking to possibly implement this myself. I've looked over a few librarys such as OpenSSL and yaSSL. The documentation isn't too great for either of these, and i've looked over some sample code in yaSSL, and it seems to require a certificate file, which I do not want to have to distribute. Anyways, I was basically asking if you guys know of a library of which has the following propertys: 1. Statically linkable. 2. Not dependant on certificate files, or any other external dependencies. 3. Win32/64 compatible. 4. Hopefully, easy to implement. :) Or, if I were to implement SSL myself on top of my socket wrapper, I was looking for a helpful resource on the knitty-gritty implementation details. Of course, I've used google, but maybe there's the holy grail of resources out there I don't know about. Reading over RFCs is never fun and always seems to be a cryptic process. Thanks.
Advertisement
http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/ssl__stream.html
You should be able to use cURL for your task. It comes with optional support for SSL, so make sure to download the SSL version for the 32bit and 64bit packages. It also comes with static libraries as well as source that you can use if needed. However, OpenSSL is provided as a DLL so you would need to spend some time putting it all together if you wanted absolutely no DLLs.

I hacked together a simple (32bit) e-mail retrieving program based on this PHP cURL code to see how it was using cURL and it was pretty easy.

#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <string>#include <vector>#include <sstream>#include <time.h>#include <curl/curl.h>std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len);std::string base64_decode(std::string const& encoded_string);std::vector<std::string> TokenizeString(const std::string& str, const std::string& delim);int main(int argc, char * argv[]){	const std::string g_username = "";	const std::string g_password = "";	CURL * curl;	CURLcode result;	curl_slist * headers = 0;	curl_global_init(CURL_GLOBAL_ALL);	curl = curl_easy_init();	result = curl_easy_setopt(curl, CURLOPT_URL, "https://gmail.google.com/gmail/feed/atom");	if(result == CURLE_OK)	{		headers = curl_slist_append(headers, "Host: gmail.google.com");		std::stringstream ss0;		ss0 << g_username << ":" << g_password;		std::stringstream ss1;		ss1 << "Authorization: Basic " << base64_encode(reinterpret_cast<const unsigned char *>(ss0.str().c_str()), ss0.str().size());		headers = curl_slist_append(headers, ss1.str().c_str());		headers = curl_slist_append(headers, "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4");		headers = curl_slist_append(headers, "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");		headers = curl_slist_append(headers, "Accept-Language: en-gb,en;q=0.5");		headers = curl_slist_append(headers, "Accept-Encoding: text");		headers = curl_slist_append(headers, "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7");		time_t rawtime;		struct tm * timeinfo = 0;		tm * ptm = 0;		time(&rawtime);		timeinfo = localtime(&rawtime);		std::vector<std::string> tokens = TokenizeString(asctime(timeinfo), " \n");		char outb[32] = {0};		ptm = gmtime(&rawtime);		_snprintf_s(outb, 32, 31, "%2d:%02d:%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);		std::stringstream ss3;		ss3 << "Date: " << tokens[0] << ", " << tokens[2] << " " << tokens[1] << " " << tokens[4] << " " << outb << " " << "GMT";		headers = curl_slist_append(headers, ss3.str().c_str());		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);		curl_easy_setopt(curl, CURLOPT_COOKIESESSION, true);		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);		//curl_easy_setopt(curl, CURLOPT_RETURNTRANSFER, 1);		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);		curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1);		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);		result = curl_easy_perform(curl);		curl_slist_free_all(headers);		headers = NULL;	}	curl_global_cleanup();	return 0;}// Tokenizes a string into a vectorstd::vector<std::string> TokenizeString(const std::string& str, const std::string& delim){	// http://www.gamedev.net/community/forums/topic.asp?topic_id=381544#TokenizeString	using namespace std;	vector<string> tokens;	size_t p0 = 0, p1 = string::npos;	while(p0 != string::npos)	{		p1 = str.find_first_of(delim, p0);		if(p1 != p0)		{			string token = str.substr(p0, p1 - p0);			tokens.push_back(token);		}		p0 = str.find_first_not_of(delim, p1);	}	return tokens;}/*    base64.cpp and base64.h   Copyright (C) 2004-2008 René Nyffenegger   This source code is provided 'as-is', without any express or implied   warranty. In no event will the author be held liable for any damages   arising from the use of this software.   Permission is granted to anyone to use this software for any purpose,   including commercial applications, and to alter it and redistribute it   freely, subject to the following restrictions:   1. The origin of this source code must not be misrepresented; you must not      claim that you wrote the original source code. If you use this source code      in a product, an acknowledgment in the product documentation would be      appreciated but is not required.   2. Altered source versions must be plainly marked as such, and must not be      misrepresented as being the original source code.   3. This notice may not be removed or altered from any source distribution.   René Nyffenegger rene.nyffenegger@adp-gmbh.ch*/static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";static inline bool is_base64(unsigned char c){	return (isalnum(c) || (c == '+') || (c == '/'));}std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len){	std::string ret;	int i = 0;	int j = 0;	unsigned char char_array_3[3];	unsigned char char_array_4[4];	while (in_len--)	{		char_array_3[i++] = *(bytes_to_encode++);		if(i == 3)		{			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);			char_array_4[3] = char_array_3[2] & 0x3f;			for(i = 0; (i <4) ; i++)				ret += base64_chars[char_array_4];			i = 0;		}	}	if(i)	{		for(j = i; j < 3; j++)			char_array_3[j] = '\0';		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);		char_array_4[3] = char_array_3[2] & 0x3f;		for (j = 0; (j < i + 1); j++)			ret += base64_chars[char_array_4[j]];		while((i++ < 3))			ret += '=';	}	return ret;}std::string base64_decode(std::string const& encoded_string){	int in_len = (int)encoded_string.size();	int i = 0;	int j = 0;	int in_ = 0;	unsigned char char_array_4[4], char_array_3[3];	std::stringstream ret;	while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_]))	{		char_array_4[i++] = encoded_string[in_]; in_++;		if (i ==4)		{			for (i = 0; i <4; i++)			char_array_4 = (unsigned char)base64_chars.find(char_array_4);			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];			for (i = 0; (i < 3); i++)				ret << char_array_3;			i = 0;		}	}	if(i)	{		for (j = i; j <4; j++)			char_array_4[j] = 0;		for (j = 0; j <4; j++)			char_array_4[j] = (unsigned char)base64_chars.find(char_array_4[j]);		char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);		char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);		char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];		for (j = 0; (j < i - 1); j++) 			ret << char_array_3[j];	}	return ret.str();}


It also worked quite nicely on the first try!:


So, perhaps you could use it as a starting point. You can also look into an application packer that bundles DLLs as well if you only wanted one final file to distribute as well, such as MoleBox.

I've not tried the e-mail sending logic or other e-mail sites, but since this works very easily with Gmail over SSL for retrieving, I'd strongly suspect it'll work with others as well. That will be up to you to verify though. [wink] Goodluck!
Thanks for you replys.

I'm actually looking to implement SMTP with the SSL extension. So I don't think curl will be of too much help. Has anyone used Asio's SSL capabilities? I already have a winsock wrapper. How hard would it be to build Asios SLL-ness on top of it? Is there any way to have Asio use my socket descriptor?

Thanks.
Nothing about boost is really easy until you've struggled with it for a couple days.

It appears you can construct a boost::asio socket from a native socket using the following overload:

http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio/reference/basic_stream_socket/basic_stream_socket/overload4.html


However, I haven't used its SSL capabilities so I can't tell you really how to proceed from there. I'm pretty sure the whole thing is possible though, and once you get past the learning curve it's probably elegant and intuitive.
Thanks.
Just for anyone facing similar issues in the future, I ended up going with CyaSSL. It's lightweight and very easy to work with. Just check out their examples.

This topic is closed to new replies.

Advertisement