OpenSSL s_client vs PHP stream_socket_client

Mar 20, 2016

We, as developers, craft bespoke apps that connect to remote servers, over encrypted connections. Those connections are authenticated using API keys, username and passwords or public certificate and private keys. Sometimes, those connections work out of the box, sometimes we’ll spend 6 hours figuring out what’s wrong.

The easiest way to establish a plain text connection from the terminal to a server, is to use telnet

telnet 80

then issue a HTTP command like GET / and you’ll get Google’s homepage, which will be a redirect to a HTTPS version of the same page.

To establish a SSL connection, you can use the openssl Swiss knife.

openssl s_client -connect

and then issue the same HTTP command GET / and you’ll get the HTTPS version of the Google homepage.

OpenSSL can be used to open a connection that requires certificate authentication too, just supply those as CLI options. This way you can test that the Apple Push Notification connectivity, by using your developer certificate and private key. Just make sure that they are converted to the PEM file format first.

openssl s_client -connect -cert -key -CAfile -showcerts -state

After you’ve figured out which developer certificate and which private key are still active and match your remote server, you can establish the same connection from PHP.

$opts = array(
  'ssl' => array(
      'local_cert' => '',
      'local_pk' => '',
      'cafile' => 'example.cacert',
      'verify_peer' => true,

$timeout = 160;
$host = "ssl://";

echo "Connecting\n";

$context = stream_context_create($opts);
$socket = stream_socket_client (
  $host, $errno, $errstr, $timeout,

if (!$socket) {
    echo "Failure $errno errstr $errstr.\n";
} else {
    echo "Success\n";

In most of the cases, after running this script, the output will be Success.

However, if you’re unlucky, you’ll get the following output

PHP Warning:  stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in /path/to/example.php on line 20
PHP Warning:  stream_socket_client(): Failed to enable crypto in /path/to/example.php on line 20
PHP Warning:  stream_socket_client(): unable to connect to ssl:// (Unknown error) in /path/to/example.php on line 20
errno 0 errstr .

After you double check several times, you reach the conclusion that you’re using the same certificate, key and CA certificate as above. You’ll start wondering if there is a difference when the connection is made using openssl s_client and PHP’s stream_context_create and stream_socket_client (or curl).

One of the ways to get a glimpse into what each program is doing, is to use DTrace if you’re lucky enough to use one of the OSes where’s available (like MacOS X, FreeBSD or SmartOS) or to use its poor friend strace.

strace php example.php 2> example.php.log
strace openssl s_client -connect -cert -key -CAfile -showcerts -state 2> example.openssl.log

After a careful check of those file’s output, you’ll notice that OpenSSL and PHP are using two different CA stores. Open SSL’s /usr/lib/ssl/certs/, versus PHP’s /usr/share/ca-certificates/mozilla/. You can spot this by checking what happens before getting the stream_socket_client(): SSL operation failed with code 1. error message:

PHP’s trace:

stat("/usr/share/ca-certificates/mozilla//6b99d060.0", 0x7ffded049c80) = -1 ENOENT (No such file or directory)

OpenSSL’s trace:

stat("/usr/lib/ssl/certs/6b99d060.0", {st_mode=S_IFREG|0644, st_size=1643, ...}) = 0
open("/usr/lib/ssl/certs/6b99d060.0", O_RDONLY) = 4

After figuring this little difference, it’s pretty easy to fix the PHP code. Change the stream_context_create to include the new CA path:

$opts = array(
  'ssl' => array(
      'local_cert' => '',
      'local_pk' => '',
      'cafile' => 'example.cacert',
      'capath' => '/usr/lib/ssl/certs/',
      'verify_peer' => true,

Tip o’ the hat to Ben Hearsum’s Python and SSL Certificate Verification article for suggesting to use strace to figure out why do we get different results, while using the same certificates.


Skagen 597LSLB

May 29, 2015

A couple of years ago, I bought a nice Danish watch, Skagen 597LSLB. It wasn’t a very expensive watch, but it was a pretty one. At that time, I paid about €120 for it.

Fast forward a couple of years later, some parts of the watch do not seem to be that solid. The leather band started to peel off, like an onion. It’s quite unfortunate that this happened, having in mind that I did not wear this watch every day, since I got it, but only about half of those days. We all know that things are not built as they used to be and that we’re living in a consumerist world, so nothing new so far. One would assume that getting a leather band replaced, shouldn’t be a big deal though. However, it seems that the folks running Skagen could not be bothered to sell any watch accessories anymore. Since they do not offer a replacement on their site, your only option is eBay or a third party company.

A couple of days later, the battery ran out (for the third time since I got the watch). That’s fine, they are not designed to last forever. Let’s check Skagen’s site again, to find out what battery should I buy. Again, I was out of luck, as I could not find that info online.

To save others the trouble of researching this bit, I’ve written this blog post. The Skagen 597LSLB watch model is using a SR616SW/Renata 321/Energizer 321 cell.

Here is a photo of the watch’s internals, in case you’re curious about the built quality.

Remote Logging using CocoaLumberjack, Antenna and DDAntennaLogger

May 16, 2015

The classic iOS way of logging debug messages is to use the built in NSLog method.

NSLog(@"Application Started");

The dissadvantage of this method is that when your application is ready for release, you have to manually remove them. Alternatively, you can include them in if or #ifdef/#endif blocks:

    NSLog(@"Application Started");

An improvement to the above method of debugging is using the following DLog/ALog macros:

#ifdef DEBUG
#   define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#   define DLog(...)

// ALog always displays output regardless of the DEBUG setting
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)

Then sprinkle them in your code:

DLog(@"Application Started");

If you need granular debug levels, one of the best options is the logging framework CocoaLumberjack. Using Cocoapods, it’s only one line of code (TM):

pod 'CocoaLumberjack'

Then in your code:

DDLogDebug(@"Will Start Application");
// ...
DDLogInfo(@"Application Started");

Then in your Application.pch or AppDelegate.m you can set the desired log level:

static const int ddLogLevel = DDLogFlagDebug;

One powerfull feature of CocoaLumberjack is that you can define your custom log levels, so you can separate logging of different parts of your project:

[DDLog addLogger:[DDTTYLogger sharedInstance]];

DDLogSync(@"Starting Sync Operation");
DDLogRts(@"Broadcasting Real Time event");

Which then can be enabled or disabled by by setting the corresponding ddLogLevel:

static const int ddLogLevel = DDLogFlagDebug|LOG_FLAG_RTS;

So far, so good. When your friends are testing your app, your logs are saved on the device. If you’d like to access them, you can connect the device to your Mac and explore them in the Window > Devices menu (shift + cmd + 2).

One improuvement to the current setup would be send your logs to the net. For that, we’ll be using two more frameworks Antenna and DDAntennaLogger. Antenna is responsible for shipping the logs to your server, DDAntennaLogger is a custom logger for CocoaLumberjack. Once you plug them together, you’ll be able to have your logs automatically sent to your server.

pod 'CocoaLumberjack'
pod 'Antenna'
pod 'DDAntennaLogger'

Then in your code:

NSURL *logUrl = [NSURL URLWithString:@""];
[[Antenna sharedLogger] addChannelWithURL:logUrl method:@"POST"];
[[Antenna sharedLogger] startLoggingApplicationLifecycleNotifications];

DDAntennaLogger *logger = [[DDAntennaLogger alloc] initWithAntenna:[Antenna sharedLogger]];
[DDLog addLogger:logger];
[DDLog addLogger:[DDTTYLogger sharedInstance]]; // To see them in the Xcode debugger

The server should have this end-point setup. You can save them to file, or db, or forward them to the logger of your choice.

For a node.js based example check out this npm package express-antenna-cocoalumberjack, or check this Ruby Rack middleware rack-http-logger.

npm install express-antenna-cocoalumberjack
node node_modules/express-antenna-cocoalumberjack/app.js
tail -f /tmp/antenna-cocoalumberjack.log

And change the link to the remote logger to:

NSURL *logUrl = [NSURL URLWithString:@"http://yourserver:3205/log/"];
[[Antenna sharedLogger] addChannelWithURL:logUrl method:@"POST"];

There is one more bit to change, switch from http to https.

Now, there are two possible scenarios. The trivial one is when you setup the logger on the same server where you already have a SSL certificate setup (e.g. or you own a wildcard SSL certificate. Everything up to this point should work great together.

Now let’s try to use a self signed certificate. Generate a SSL self signed certificate for 10 years:

openssl req -x509 -newkey rsa:2048 -keyout \
  -out -days 3560 -nodes

Configure Apache to use that then see what happens.

The other one is when you try to use self-signed certificates, at which point you’ll get the following error message in the logs:

CFNetwork SSLHandshake failed (-9847)
NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9847)

Which pretty much means that your setup is not happy with the self-signed certificate.

Because we’re controlling both the server and the client, we’re able to use SSL pinning to overcome this issue. The basic of SSL pinning is we distribute our public key certificate with the application and configure our logging setup to authenticate using this certificate. TODO: try to add some analogies.

Convert the public key certificate to binary DER:

openssl x509 -in -out -outform der

Configure your request operation manager to use the public key certificate, which you already dragged in your project:

- (AFSecurityPolicy*)remoteLoggingSecurityPolicy
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@""
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
    [securityPolicy setAllowInvalidCertificates:YES]; // Unfortunate name
    [securityPolicy setPinnedCertificates:@[certData]];
    return securityPolicy;

Then you setup your Antenna logger to use a different channel:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager setSecurityPolicy:[self remoteLoggingSecurityPolicy]];

NSURL *logUrl = [NSURL URLWithString:@""];
AntennaHTTPSChannel *httpsChannel =
  [[AntennaHTTPSChannel alloc] initWithURL:logUrl
[[Antenna sharedLogger] addChannel:httpsChannel];
[[Antenna sharedLogger] startLoggingApplicationLifecycleNotifications];

If your end-point is using authentication, you can set it up in the request operation manager, after setting up the security policy:

NSURLCredential *credential =
    [[NSURLCredential alloc] initWithUser:@"user"
[manager setCredential:credential];

Now, the earlier error message should dissapear and you should see the logs comming to your server.

If you’re asking, why would you like to send the debug logs to a server, there are several reason. First of them is to get debug info from your beta builds, when your testers are not in the same city as you. Second of them is to corelate data coming to your server, with data sent to your iPad with debug messages from your iPad.