How to Use SSL Sockets with PHP

SSL sockets are perfect for sending secure data. With certificates, you can verify the identify of the host, the client, or both. Signed certificates cost money but you can create and self-sign a certificate. Check out the code samples below to see how to generate SSL certificates and create SSL clients and servers. Examples include raw socket communication as well as the common HTTPS protocol.

Generating an OpenSSL PEM

$certificateData = array(
    "countryName" => "US",
    "stateOrProvinceName" => "Texas",
    "localityName" => "Houston",
    "organizationName" => "DevDungeon.com",
    "organizationalUnitName" => "Development",
    "commonName" => "DevDungeon",
    "emailAddress" => "nanodano@devdungeon.com"
);

// Generate certificate
$privateKey = openssl_pkey_new();
$certificate = openssl_csr_new($certificateData, $privateKey);
$certificate = openssl_csr_sign($certificate, null, $privateKey, 365);

// Generate PEM file
# Optionally change the passphrase from 'comet' to whatever you want, or leave it empty for no passphrase
$pem_passphrase = 'abracadabra';
$pem = array();
openssl_x509_export($certificate, $pem[0]);
openssl_pkey_export($privkey, $pem[1], $pem_passphrase);
$pem = implode($pem);

// Save PEM file
$pemfile = './server.pem';
file_put_contents($pemfile, $pem);

SSL Client Socket

Simplest version

$socket = stream_socket_client("ssl://192.168.1.2:5522", $errno, $errstr);
if ($socket) {
echo fread($socket, 2000);
}

More options

$host = '192.168.1.2';
$port = 5522;
$timeout = 30;
$cert = 'e:\www\workspace\php\sockets\server.pem'; // Path to certificate
$context = stream_context_create(
    array('ssl'=>array('local_cert'=> $cert))
);
if ($socket = stream_socket_client(
        'ssl://'.$host.':'.$port,
        $errno,
        $errstr,
        30,
        STREAM_CLIENT_CONNECT,
        $context)
) {
    fwrite($socket, "\n");
    echo fread($socket,8192);
    fclose($socket);
} else {
   echo "ERROR: $errno - $errstr\n";
}

SSL Server Socket

$context = stream_context_create();

// local_cert must be in PEM format
stream_context_set_option($context, 'ssl', 'local_cert', $pemfile);

// Pass Phrase (password) of private key
stream_context_set_option($context, 'ssl', 'passphrase', $pem_passphrase);
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);

// Create the server socket
$socket = stream_socket_server(
    'ssl://0.0.0.0:9001',
    $errno,
    $errstr,
    STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,
    $context
);

// fwrite/fread to $socket

HTTPS Client

// Get port and IP address
$service_port = getservbyname('www', 'tcp');
$address = gethostbyname('www.google.com');

// Bind socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}

// Connect
$result = socket_connect($socket, $address, $service_port);
if ($result === false) {
    echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
}

// Create HTTP request
$in = "HEAD / HTTP/1.1\r\n";
$in .= "Host: www.google.com\r\n";
$in .= "Connection: Close\r\n\r\n";
$out = '';

// Send HTTP request
socket_write($socket, $in, strlen($in));

// Read and output response
while ($out = socket_read($socket, 2048)) {
    echo $out;
}

// Close socket
socket_close($socket);

HTTPS Server

This is just a slight extension of the SSL Server code above to accept and respond as an HTTP server

$context = stream_context_create();

// local_cert must be in PEM format
stream_context_set_option($context, 'ssl', 'local_cert', '/path/to/pem/file');
stream_context_set_option($context, 'ssl', 'passphrase', $pem_passphrase);
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);

// Create the server socket
$server = stream_socket_server('ssl://192.168.1.96:9001', $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);

while(true)
{
    $buffer = '';
    $client = stream_socket_accept($server);
    if($client) {
        // Read until double CRLF
        while( !preg_match('/\r?\n\r?\n/', $buffer) )
            $buffer .= fread($client, 2046);
        // Respond to client
        fwrite($client,  "200 OK HTTP/1.1\r\n"
                         . "Connection: close\r\n"
                         . "Content-Type: text/html\r\n"
                         . "\r\n"
                         . "Hello World! " . microtime(true)
                         . "\n<pre>{$buffer}</pre>");
        fclose($client);
    }
}