ZF-5524: Zend_Http_Client_Adapter_Proxy doesn't support SSL Client Certificate

Issue Type: Bug Created: 2009-01-12T01:32:56.000+0000 Last Updated: 2010-06-19T13:20:48.000+0000 Status: Resolved Fix version(s): - 1.10.1 (10/Feb/10)

Reporter: Olivier Lépine (crapougnax) Assignee: Pádraic Brady (padraic) Tags: - Zend_Http_Client

Related issues: - ZF-2946



Although Zend_Http_Client_Adapter_Socket fully support SSL Client certificate authentication, Zend_Http_Client_Adapter_Proxy doesn't.

in the write() method of Zend_Http_Client_Adapter_Proxy, there is a call to method connectHandshake() if protocol is HTTPS.

This method tries to send a CONNECT query to the proxy server and then, if ok, switch to crypto with PHP function stream_socket_enable_crypto().

This fails if the remote server requires a SSL client certificate to be presented.

The stream_socket_enable_crypto() function is not well documented but it seems that an additional parameter allows to pass the context of another socket (including a local cert) to the function.

In the connecHandshake() method, I changed the code here:

    $success = false; 
    foreach($modes as $mode) {
        $success = stream_socket_enable_crypto($this->socket, true, $mode);
        if ($success) break;


    $context = stream_context_create();
    if (! stream_context_set_option($context, 'ssl', 'local_cert', 'path/to/my/local_cert')) {
        die ('unable to set SSL cert');

    $socket = stream_socket_client($host . ':' . $port, $errno,$errstr, (int) $this->config['timeout'], STREAM_CLIENT_CONNECT, $context);

    if ($socket === false) {
        die('Unable to set socket');

    $success = false; 
    foreach($modes as $mode) {
        $success = stream_socket_enable_crypto($this->socket, true, $mode, $socket);
        if ($success) break;

Currently, the apache child dies when processing this patched adapter (Apache 2.2.8 + PHP 5.2.8 on Ubuntu 8.04)

Is it a valid method to achieve what I aim to? If positive, what is going wrong?


Posted by Shahar Evron (shahar) on 2009-07-23T15:44:01.000+0000

Sorry for taking so long with this :)

Recent improvements to HTTP client allowed me to implement this by reducing most of the code in the connect() method, and simply relying on preset stream context for this task.

This should now not be any different than using the Socket adapter with an SSL certificate - you can also look at the setStreamContext and getStreamContext methods for this, if you need "advanced" stuff like peer certificate validation forcing.

Fixed in CS-17013

Posted by Mikko Koppanen (mkoppanen) on 2009-08-26T04:48:33.000+0000

This change breaks backwards compatibility.…

146 - if ($this->connected_to[0] != $host || $this->connected_to[1] != $port) { 126 + if ($this->connected_to[0] != "tcp://$host" || $this->connected_to[1] != $port) {

At least in our environment there is no tcp:// prefix there as it is not required as far as I understand.

Posted by Mikko Koppanen (mkoppanen) on 2009-08-26T04:56:34.000+0000

Changing the line to:

if (($this->connected_to[0] != "tcp://$host" && $this->connected_to[0] != $host) || $this->connected_to[1] != $port) {

Seems to fix this in our environment.

Posted by Shahar Evron (shahar) on 2009-08-26T05:37:34.000+0000

Reopening following previous comment

Posted by Mikko Koppanen (mkoppanen) on 2009-08-26T06:15:15.000+0000

To actually fix the functionality:

Posted by Mikko Koppanen (mkoppanen) on 2009-11-19T02:05:12.000+0000

I commented that this bug breaks backwards compatibility and it's still present in 1.9.5

Posted by Pádraic Brady (padraic) on 2009-11-19T12:37:00.000+0000


Can you repost the fix you added on pastebin? The paste ID is no longer valid. I'll commit tomorrow once I have a fix I can run tests against.

Posted by Mikko Koppanen (mkoppanen) on 2009-11-19T13:09:59.000+0000

The following code can be used to test the issue:

<pre class="highlight">
$client = new Zend_Http_Client('<a href=""></a>', array('sslcert' => '/etc/pki/test.pem',
                                                                 'proxy_host' => '',
                                         'proxy_port' => '8888',
                                     'adapter' => 'Zend_Http_Client_Adapter_Proxy'));


The whole issue explained briefly:

Zend_Http_Client_Adapter_Proxy calls the parent::connect method setting $secure = false. This means that ssl context is not created for the connection. This is problematic in a scenario where the connection to proxy is over plain but SSL stream using client certs is negotiated inside that connection.

The proposed fix:

<pre class="highlight">
### Eclipse Workspace Patch 1.0
#P zf-trunk
Index: src/Zend/Http/Client/Adapter/Socket.php
--- src/Zend/Http/Client/Adapter/Socket.php (revision 18996)
+++ src/Zend/Http/Client/Adapter/Socket.php (working copy)
@@ -62,10 +62,11 @@
      * @var array
     protected $config = array(
-        'persistent'    => false,
-        'ssltransport'  => 'ssl',
-        'sslcert'       => null,
-        'sslpassphrase' => null
+        'persistent'      => false,
+        'ssltransport'    => 'ssl',
+        'sslcert'         => null,
+        'sslpassphrase'   => null,
+        'sslusecontext' => false
@@ -176,11 +177,11 @@
         if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) {
             if (is_resource($this->socket)) $this->close();
         // Now, if we are not connected, connect
         if (! is_resource($this->socket) || ! $this->config['keepalive']) {
             $context = $this->getStreamContext();
-            if ($secure) {
+            if ($secure || $this->config['sslusecontext']) {
                 if ($this->config['sslcert'] !== null) {
                     if (! stream_context_set_option($context, 'ssl', 'local_cert',
                                                     $this->config['sslcert'])) {
Index: src/Zend/Http/Client/Adapter/Proxy.php
--- src/Zend/Http/Client/Adapter/Proxy.php  (revision 18996)
+++ src/Zend/Http/Client/Adapter/Proxy.php  (working copy)
@@ -91,6 +91,11 @@
         if (! $this->config['proxy_host']) {
             return parent::connect($host, $port, $secure);
+        /* Url might require stream context even if proxy connection doesn't */
+        if ($secure) {
+           $this->config['sslusecontext'] = true;
+        }
         // Connect (a non-secure connection) to the proxy server
         return parent::connect(

The issue can be tested using CA setup mentioned here ( and setting up for example apache to require the client certificate to be present. Note that this seems to only affect proxy adapter.

Posted by Pádraic Brady (padraic) on 2010-02-06T09:06:58.000+0000

Fixed in r20946

Posted by Lorenzo Alberton (quipo) on 2010-06-14T08:33:00.000+0000

This issue is not completely fixed in 1.10.5: the

'sslusecontext' => false

entry is missing from the

protected $config = array()

causing a NOTICE (Undefined index sslusecontext) on line 92:

if ($secure || $this->config['sslusecontext']) {

Posted by Marco Kaiser (bate) on 2010-06-16T02:06:14.000+0000

next release should address this issue. There was one line missing in the merge

Posted by Pádraic Brady (padraic) on 2010-06-19T13:12:42.000+0000

Reopen due to misapplied patch

Posted by Pádraic Brady (padraic) on 2010-06-19T13:20:48.000+0000

Missing lines previously committed to release branch by bate.

Note: If an issue is technically not fixed, it is fine to reopen it until properly resolved. Thanks for reporting the missing line!

Have you found an issue?

See the Overview section for more details.


© 2006-2016 by Zend, a Rogue Wave Company. Made with by awesome contributors.

This website is built using zend-expressive and it runs on PHP 7.