Admittedly, the topic of this post is very specific but I hope it will still be of some value for some people.The task for today is to setup SSL server and client with POCO framework classes. I will leave out the whole certificate managing issues and just assume that the right files are at hand.
The SSL related part of the POCO libraries essentially wraps the OpenSSL library into a nice object-oriented interface. When you know OpenSSL, you can instantly relate to classes like Poco::Net::Context, or the …Handler classes (if you replace “handler” with “callback”).
“SSL” stands for Secure Socket Layer, so the first thing to discover is class Poco::Net::SecureServerSocket. As you would expect, this class is derived from Poco::Net::ServerSocket, extending it only with SSL related stuff. And sure enough, some constructors of Poco::Net::ServerSocket take a Poco::Net::ContextPtr as argument.
But why only some constructors? Since there is no setContext method, there must be some other mechanism in place by which SecureServerSockets get their SSL context.
Introducing Poco::Net::SSLManager. From the API docs:
SSLManager is a singleton for holding the default server/client Context and handling callbacks for certificate verification errors and private key passphrases.
Proper initialization of SSLManager is critical.
Aha! So all the constructors of SecureServerSocket that do not take Context pointers simply get it from the SSLManager singleton.
But how to initialize SSLManager?
1. The POCO Way:
If you developed your application with POCO from the ground up there probably exists a sub-class of Poco::Application, and all the configuration is handled by the built-in configuration classes.
With this in place, all you have to do is to add the proper ssl configuration elements:
openSSL.server.privateKeyFile = /path/to/key/file openSSL.server.certificateFile = /path/to/certificate/file openSSL.server.verificationMode = none openSSL.server.verificationDepth = 9 openSSL.server.loadDefaultCAFile = false openSSL.server.cypherList = ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH openSSL.server.privateKeyPassphraseHandler.name = KeyFileHandler openSSL.server.privateKeyPassphraseHandler.options.password = securePassword openSSL.server.invalidCertificateHandler = AcceptCertificateHandler
2. Manually:
Depending on which side you are – client or server – you have to call SSLManager::initializeClient or SSLManager::initializeServer. Both methods take three arguments:
- PrivateKeyPassphraseHandler pointer
- InvalidCertificateHandler pointer
- Context pointer
This is where it becomes a little bit tricky: If you try to instantiate a Context with a privateKey file in order to provide it as argument to the initialize… method, a PrivateKeyPassphraseHandler might be needed. This handler is fetched from the SSLManager singleton – which you are just about to initialize!.
This circular dependency between Context and SSLManager can be overcome e.g. if you call SSLManager::initializeServer first only with a PrivateKeyPassphraseHandler, a InvalidCertificateHandler and null Context pointer. Then instantiate the Context and call SSLManager::initializeServer again.
Now that SSL Manager is initialized we can use Secure… prefixed classes as we would used their non-SSL counterparts. As with SecureServerSocket, other Secure… classes are derieved from corresponding non-secure base classes.
Conclusion: Once you got around the initialization of SSLManager singelton, using SSL POCO classes is very easy and straight forward. Check it out!
Interesting post! I have tried to implement a HTTPS server following your description but I have an issue when I try to connect to the server using Firefox 14.
My application responds correctly to the requests.
But as soon as the keep alive timeout elapses the application stops responding and the browser gives an error: “The connection to the server was reset while the page was loading”.
Are you experiencing anything similar?
This is part of my source code:
class SSLInit {
public:
static void init() {
Poco::Path mycert( Poco::Util::ServerApplication::instance().config().getString(“application.dir”) );
mycert.append(“mycert.pem”);
Poco::SharedPtr pConsoleHandler = new KeyConsoleHandler(true);
Poco::SharedPtr pCertHandler = new AcceptCertificateHandler(true);
SSLManager::instance().initializeServer(pConsoleHandler, pCertHandler, NULL);
Context::Ptr pContext = new Context(Context::SERVER_USE, mycert.toString(), mycert.toString(), “”, Context::VERIFY_RELAXED, 9, false, “ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH”);
SSLManager::instance().initializeServer(pConsoleHandler, pCertHandler, pContext);
}
};
class HTTPSRequestHandler: public HTTPRequestHandler {
(…)
void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response)
{
Poco::Timestamp now;
std::string dt(Poco::DateTimeFormatter::format(now, “%W, %e %b %y %H:%M:%S %Z”));
response.setChunkedTransferEncoding(true);
response.setContentType(“text/html”);
std::ostream& ostr = response.send();
ostr << "HTTPSRequestHandler powered by POCO C++ Libraries”;
ostr << "”;
ostr << "”;
ostr << "Hello HTTPS” << dt;
ostr << "”;
}
};
class HTTPSServer : public Poco::Util::ServerApplication
{
(…)
int main(const std::vector& args)
{
SSLInit::init();
SecureServerSocket scs(10444);
int maxQueued = 100;
int maxThreads = 16;
HTTPServerParams* pParams = new HTTPServerParams;
pParams->setMaxQueued(maxQueued);
pParams->setMaxThreads(maxThreads);
pParams->setKeepAlive(true);
pParams->setKeepAliveTimeout(Poco::Timespan(5,0));
HTTPServer scv(new HTTPSRequestHandlerFactory(), scs, pParams);
scv.start();
// wait for CTRL-C or kill
waitForTerminationRequest();
// Stop the HTTPServer
scv.stop();
return Application::EXIT_OK;
}
};
A HTTPS-Server is included in the samples!
Does the manual way of initialization with the circular dependency issue still seen in current version of Poco. I am using Poco 1.4.6.
We have not been using Poco lately in our projects, so I am afraid I cannot answer your question.