|  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80770 It is not possible to get client peer certificate with stream_socket_server
Submitted: 2021-02-18 16:11 UTC Modified: 2021-02-18 17:13 UTC
Avg. Score:3.7 ± 0.5
Reproduced:2 of 2 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: Assigned:
Status: Verified Package: OpenSSL related
PHP Version: 7.3.27 OS:
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If this is not your bug, you can add a comment by following this link.
If this is your bug, but you forgot your password, you can retrieve your password here.
Bug Type:
New email:
PHP Version: OS:


 [2021-02-18 16:11 UTC]
It is not possible to get the client certificate when accepting TLS connections, without forcing the client to provide a (valid) certificate.

This forbids writing a fully compliant Gemini server in PHP, because it is not possible to accept client certificates without forcing them on all pages.


Test script:
$context = stream_context_create(
        'ssl' => [
            'allow_self_signed' => true,
            'SNI_enabled'       => true,
            'SNI_server_certs'  => ['' => '/path/to/cert.pem'],
            'capture_peer_cert' => true,
$socket = stream_socket_server(
    'tcp://[::]:' . $port,

if ($socket === false) {
    throw new \Exception($errstr, $errno);
} else {
    while ($conn = stream_socket_accept($socket, -1, $peername)) {
        $tlsSuccess = stream_socket_enable_crypto(
        if ($tlsSuccess !== true) {

Expected result:
Have the client certificate in 'peer_certificate' key of $conn, if the client sends one.

Actual result:
No 'peer_certificate' option.

If I set 'verify_peer' to true, it works, but then it is not possible for a client to connect without a certificate.


Add a Patch

Pull Requests

Add a Pull Request


AllCommentsChangesGit/SVN commitsRelated reports
 [2021-02-18 16:26 UTC]
Note that there is a test for this behaviour which is not marked as XFAIL, so I believe this functionality should work - although I cannot immediately see an issue with your sample code
 [2021-02-18 16:26 UTC]
Ah no I think I see the problem, you need to pass $context instead of $conn to stream_context_get_options()
 [2021-02-18 16:32 UTC]
The test you linked to is about getting the server certificate from the client, I want the other way around, get the client certificate from the server.
And without making the client certificate mandatory, I still need to accept certificate-less requests.
 [2021-02-18 16:34 UTC]
See section 4.3 of for more information about client certificate use in Gemini protocol.
 [2021-02-18 17:13 UTC]
My apologies, you are correct and I have actually now remembered encountering the same limitation a few months ago. I do also have a (theoretical) work-around, which is to obtain the ClientHello message (and potentially other traffic) via stream_socket_recvfrom() and STREAM_PEEK before calling stream_socket_enable_crypto().

I think I even wrote a PoC partial implementation of it, I cannot find it at the moment but if I do I will make a gist and link it here.

As for a proper fix for the issue, I have touched this code in the (distant) past and I from what I remember it should be reasonably easy to optionally store a client certificate into the stream's context during the peer verification callback, though I think this would not work if peer verification was completely disabled. I'm not sure if this would be considered an acceptable limitation.
 [2021-02-18 17:13 UTC]
-Status: Open +Status: Verified
 [2022-12-31 05:09 UTC] marlynrasavong at gmail dot com
Hey it is not possible to get certificate without TLS. 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Apr 24 03:01:29 2024 UTC