Supporting TLS client certificate in PHP?

CΓ΄me Chilliet <come (a) chilliet.eu>

Hello,

My server is implemented in PHP and I would like add client certificate 
support, but I cannot seem to find the right option for this.

I tried setting capture_peer_cert to true in the context options, but it 
does not seem to work, and the documentation is, well, sporadic.

You can read my code on 
gemini://code.lanterne.chilliet.eu/vendor/mcmic/gemini-server/src/Server.php

Are there any Gemini server in PHP with client cert support?

C?me

Link to individual message.

Jansen Price <jansen.price (a) gmail.com>

I ran into the same problem when trying to add client certificate support
on my PHP server (https://tildegit.org/sumpygump/orbit)

I think that `capture_peer_cert` is intended to only work
with stream_socket_client and not with socket servers. I started looking at
the source code of PHP to see if I could find the part that handles the TLS
handshake to find out if there is any way to get access to the cert that
came along with the connection, but I have not been successful in my search
yet.

On Sun, Dec 13, 2020 at 11:12 AM C?me Chilliet <come at chilliet.eu> wrote:

> Hello,
>
> My server is implemented in PHP and I would like add client certificate
> support, but I cannot seem to find the right option for this.
>
> I tried setting capture_peer_cert to true in the context options, but it
> does not seem to work, and the documentation is, well, sporadic.
>
> You can read my code on gemini://
> code.lanterne.chilliet.eu/vendor/mcmic/gemini-server/src/Server.php
>
> Are there any Gemini server in PHP with client cert support?
>
> C?me
>
>
>

-- 
Jansen Price
612-886-5065
jansen.price at gmail.com

Link to individual message.

CΓ΄me Chilliet <come (a) chilliet.eu>

Le dimanche 13 d?cembre 2020, 18:28:38 CET Jansen Price a ?crit :
> I ran into the same problem when trying to add client certificate support
> on my PHP server (https://tildegit.org/sumpygump/orbit)
> 
> I think that `capture_peer_cert` is intended to only work
> with stream_socket_client and not with socket servers. I started looking at
> the source code of PHP to see if I could find the part that handles the TLS
> handshake to find out if there is any way to get access to the cert that
> came along with the connection, but I have not been successful in my search
> yet.

I tried posting on php-internals ML, but no answer so far:
https://news-web.php.net/php.internals/112495

We can try the bug tracker but I?ve seen bugs related to streams and/or 
encryption with no answer since a long time :-/

I think the best solution is to try to come with a PR.

C?me

Link to individual message.

Trevor Slocum <trevor (a) rocketnine.space>

twins provides client certificate hashes via $_SERVER.

https://gitlab.com/tslocum/twins/blob/master/CONFIGURATION.md#fastcgi

Link to individual message.

CΓ΄me Chilliet <come (a) chilliet.eu>

Le dimanche 13 d?cembre 2020, 18:28:38 CET Jansen Price a ?crit :
> I ran into the same problem when trying to add client certificate support
> on my PHP server (https://tildegit.org/sumpygump/orbit)
> 
> I think that `capture_peer_cert` is intended to only work
> with stream_socket_client and not with socket servers. I started looking at
> the source code of PHP to see if I could find the part that handles the TLS
> handshake to find out if there is any way to get access to the cert that
> came along with the connection, but I have not been successful in my search
> yet.

So, I found something, it does work if you set verify_peer to true in the 
ssl context options.

The problem is that if you do so, a request without a client certificate 
will fail, even before you can know what is the request.

I?m not sure if you can change the context once a connection is accepted, 
if yes maybe you can attempt stream_socket_enable_crypto once with 
verify_peer and if it fails run it again without it, but it feels non-optimized.

This is related to the openssl behavior of 
https://www.openssl.org/docs/man1.1.1/man3/SSL_get_peer_certificate.html
It says: "Due to the protocol definition, a TLS/SSL server will always 
send a certificate, if present. A client will only send a certificate when 
explicitly requested to do so by the server (see SSL_CTX_set_verify(3))."

According to 
https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_verify.html , a 
client certificate request is sent by the server only if SSL_VERIFY_PEER 
is set, which php will only set if verify_peer is true.

However, even if I was implementing this in C it is not clear to me how to 
request a client certificate without making it mandatory, is that what 
SSL_VERIFY_CLIENT_ONCE is for?
Are there Gemini servers in C with client certificate support of which I 
could read the code out there?

C?me

Link to individual message.

Sean Conner <sean (a) conman.org>

It was thus said that the Great C?me Chilliet once stated:
> 
> However, even if I was implementing this in C it is not clear to me how to
> request a client certificate without making it mandatory, is that what
> SSL_VERIFY_CLIENT_ONCE is for? Are there Gemini servers in C with client
> certificate support of which I could read the code out there?

  You might want to check out the source code to LibreSSL (a fork of
OpenSSL).  It comes with a TLS wrapper in the form of libtls (in the tls/
directory), which has the function tlsconf_verify_client_optional().  You
might be able to follow the logic of that function (vs.
tlsconf_verify_client()) to see how it works.

  -spc

Link to individual message.

Omar Polo <op (a) omarpolo.com>


C?me Chilliet <come at chilliet.eu> writes:
> [snip]
> However, even if I was implementing this in C it is not clear to me how 
to request a client certificate without making it mandatory, is that what 
SSL_VERIFY_CLIENT_ONCE is for?
> Are there Gemini servers in C with client certificate support of which I 
could read the code out there?
>
> C?me

I've written my server[0] using LibreSSL (a fork of OpenSSL) and it
supports client certs.  The code is quite short (a bit more than 1k
lines) but the key points for your question are these two lines[1]:

	/* optionally accept client certs, but don't try to verify them */
	tls_config_verify_client_optional(conf);
	tls_config_insecure_noverifycert(conf);

you need to enable optionally the client certs and the tell the library
to not verify the certs.

I don't know how to help you in PHP, but I hope this can help a bit :)

[0]: https://github.com/omar-polo/gmid
[1]: https://github.com/omar-polo/gmid/blob/master/gmid.c#L1076-L1078

Link to individual message.

Omar Polo <op (a) omarpolo.com>


Omar Polo <op at omarpolo.com> writes:

> C?me Chilliet <come at chilliet.eu> writes:
>> [snip]
>> However, even if I was implementing this in C it is not clear to me how 
to request a client certificate without making it mandatory, is that what 
SSL_VERIFY_CLIENT_ONCE is for?
>> Are there Gemini servers in C with client certificate support of which 
I could read the code out there?
>>
>> C?me
>
> I've written my server[0] using LibreSSL (a fork of OpenSSL) and it
> supports client certs.  The code is quite short (a bit more than 1k
> lines) but the key points for your question are these two lines[1]:
>
> 	/* optionally accept client certs, but don't try to verify them */
> 	tls_config_verify_client_optional(conf);
> 	tls_config_insecure_noverifycert(conf);
>
> you need to enable optionally the client certs and the tell the library
> to not verify the certs.
>
> I don't know how to help you in PHP, but I hope this can help a bit :)
>
> [0]: https://github.com/omar-polo/gmid
> [1]: https://github.com/omar-polo/gmid/blob/master/gmid.c#L1076-L1078

I forgot to address a point

> I?m not sure if you can change the context once a connection is 
accepted, if yes maybe you can attempt stream_socket_enable_crypto once 
with verify_peer and if it fails run it again without it, but it feels non-optimized.

I don't think you can do this.  Once a connection is established, the
certificates have already been checked, so you need to act before.

and I also forgot to mention that I'm not using directly the libressl
api, but I'm using instead libtls (a wrapper with nicer APIs)

Link to individual message.

CΓ΄me Chilliet <come (a) chilliet.eu>



Link to individual message.

Remco <me (a) rwv.io>

2020/12/21 10:42, C?me Chilliet:

> Current PHP cannot do that. Not sure what would be the best API for
> this feature if it was added. Most likely a new context option for ssl
> which says the client cert is optional. I guess to be complete it
> would allow disabling client cert validation all together, or only
> when client cert is absent.
>
> An other solution is to allow PHP code to set its own callback, but it
> seems to be dangerous and hard to use.

I feel your pain, client certs seem to be not very widely used so a lot
for platforms don't support them out of the box.  For d??m?ni, I
have/had a similar problem with racket, its standard ssl implementation
does not handle client certificates at all, so I ended up breaking it
open and allowing SSL_CTX_set_verify to be set with SSL_VERIFY_PEER.

Access to the actual client certificate from the connection context
turned out a lot harder in openssl racket setup, so, yes, I went with
dangerous and hard (passing certificate info through a thread local).  I
already had fiddle with thread locals to do SNI (another thing
apparently not very popular in racket) so I kinda got used to that..

For details see:

  https://git.sr.ht/~rwv/dezhemini/tree/client-cert/item/openssl-extra.rkt

and search for verify-callback in:

  https://git.sr.ht/~rwv/dezhemini/tree/client-cert/item/dezhmnsrv.rkt

Cheers,
R.

Link to individual message.

---

Previous Thread: How do You publish to your gemlog?

Next Thread: [ANN] SmolNetSharp - C# library for building Gemini/Gopher clients