[Openvpn-devel] Support fingerprint authentication

Message ID 20180417165000.3872-1-Jason@zx2c4.com
State Superseded
Headers show
Series [Openvpn-devel] Support fingerprint authentication | expand

Commit Message

Jason A. Donenfeld April 17, 2018, 6:50 a.m. UTC
OpenVPN traditionally works around CAs. However many TLS-based protocols also
allow an alternative simpler mode in which rather than verify certificates
against CAs, the certificate itself is hashed and compared against a
pre-known set of acceptable hashes. This is usually referred to as
"fingerprint verification". It's popular across SMTP servers, IRC servers,
XMPP servers, and even in the context of HTTP with pinning.

So, I'd like to propose an extremely simple and non-invasive way of
supporting this in OpenVPN, by re-using several features that already
basically support it. Namely, what I propose is:

   * Allow specifying 'none' to the --ca parameter, to specify that
     certificates should not be checked against a CA. Note that 'none'
     is already used in other similar options as a special placeholder.

   * When '--ca none' is in use, --verify-hash checks all depths instead
     of just level 1.

With these very simple changes, fingerprint authentication is easily achieved
via the --tls-verify script on the server and via --verify-hash on the client.

I've included some instructions on how to use all of this.

Server side:

Comments

Steffan Karger May 17, 2018, 8:31 a.m. UTC | #1
Hi Jason,

[ Dumping my thoughts so this doesn't remain completely unanswered for
even longer. ]

On 17-04-18 18:50, Jason A. Donenfeld wrote:
> OpenVPN traditionally works around CAs. However many TLS-based protocols also
> allow an alternative simpler mode in which rather than verify certificates
> against CAs, the certificate itself is hashed and compared against a
> pre-known set of acceptable hashes. This is usually referred to as
> "fingerprint verification". It's popular across SMTP servers, IRC servers,
> XMPP servers, and even in the context of HTTP with pinning.
> 
> So, I'd like to propose an extremely simple and non-invasive way of
> supporting this in OpenVPN, by re-using several features that already
> basically support it. Namely, what I propose is:
> 
>    * Allow specifying 'none' to the --ca parameter, to specify that
>      certificates should not be checked against a CA. Note that 'none'
>      is already used in other similar options as a special placeholder.
> 
>    * When '--ca none' is in use, --verify-hash checks all depths instead
>      of just level 1.
> 
> With these very simple changes, fingerprint authentication is easily achieved
> via the --tls-verify script on the server and via --verify-hash on the client.

Adding support for fingerprint authentication sounds like a good idea to
me.  Even if it was just to simplify config for peer-to-peer and small
(home) setups by getting rid of the need to create and maintain a CA.
So: feature Ack.

However, if we were to add this, I think I'd rather make it a
first-class citizen.  For example by adding a peer fingerprint
verification option that works in both client and server configs, and is
mutually exclusive with --ca.  I'd expect such an option to accept an
(inline-able) file that contains a list of fingerprints, so one can make
a config like:

<allowed-peers>
AA:BB:CC:DD:..
AA:BB:CC:DE:..
</allowed-peers>

Or maybe base64 fingerprints, because a lot of people have been trained
by SSH to recognize short base64 strings as fingerprints.

Anyone else from the community that has some thoughts on this?

As for implementation:  I'm already having problems getting to my review
queue, so am not promising to implement this.  I would definitely put a
patch for this on my review queue though :)

-Steffan

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Jan Just Keijser May 23, 2018, 4:30 a.m. UTC | #2
Hi Steffan,

On 17/05/18 20:31, Steffan Karger wrote:
> Hi Jason,
>
> [ Dumping my thoughts so this doesn't remain completely unanswered for
> even longer. ]
>
> On 17-04-18 18:50, Jason A. Donenfeld wrote:
>> OpenVPN traditionally works around CAs. However many TLS-based protocols also
>> allow an alternative simpler mode in which rather than verify certificates
>> against CAs, the certificate itself is hashed and compared against a
>> pre-known set of acceptable hashes. This is usually referred to as
>> "fingerprint verification". It's popular across SMTP servers, IRC servers,
>> XMPP servers, and even in the context of HTTP with pinning.
>>
>> So, I'd like to propose an extremely simple and non-invasive way of
>> supporting this in OpenVPN, by re-using several features that already
>> basically support it. Namely, what I propose is:
>>
>>     * Allow specifying 'none' to the --ca parameter, to specify that
>>       certificates should not be checked against a CA. Note that 'none'
>>       is already used in other similar options as a special placeholder.
>>
>>     * When '--ca none' is in use, --verify-hash checks all depths instead
>>       of just level 1.
>>
>> With these very simple changes, fingerprint authentication is easily achieved
>> via the --tls-verify script on the server and via --verify-hash on the client.
> Adding support for fingerprint authentication sounds like a good idea to
> me.  Even if it was just to simplify config for peer-to-peer and small
> (home) setups by getting rid of the need to create and maintain a CA.
> So: feature Ack.
>
> However, if we were to add this, I think I'd rather make it a
> first-class citizen.  For example by adding a peer fingerprint
> verification option that works in both client and server configs, and is
> mutually exclusive with --ca.  I'd expect such an option to accept an
> (inline-able) file that contains a list of fingerprints, so one can make
> a config like:
>
> <allowed-peers>
> AA:BB:CC:DD:..
> AA:BB:CC:DE:..
> </allowed-peers>
>
> Or maybe base64 fingerprints, because a lot of people have been trained
> by SSH to recognize short base64 strings as fingerprints.
>
> Anyone else from the community that has some thoughts on this?
>
> As for implementation:  I'm already having problems getting to my review
> queue, so am not promising to implement this.  I would definitely put a
> patch for this on my review queue though :)
>
>
I have some strong thoughts on this, mostly related to:  can someone 
explain to me why this is safe?

I've seen that OpenSSH 7.7 now implements something similar (xmss 
hash-based signatures, 
https://tools.ietf.org/html/draft-irtf-cfrg-xmss-hash-based-signatures-12, 
disabled by default) but that works entirely differently from what is 
proposed here; amongst other things, it uses *one time private keys* to 
maximize security - do we want to add something like that to our TLS 
stuff (my gut answer: no).

How can a hash replace a public key, cryptographically speaking? if you 
are not replacing public keys, then who generates, stores, maintains 
these public keys?  What about accounting in such a setup? with 
certificates, you normally hand out a certificate to a person, with the 
name of the person/entity in it. If that person misbehaves, you revoke 
the certificate and presto. Is it sufficient to remove the hash from the 
config? if so, wouldn't it be better to have a directory with 
hash-files, so that you can add and remove hashes without having to 
update the server config and/or restart the server to pick up new hashes?

JM2CW,

JJK


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Arne Schwabe May 23, 2018, 4:46 a.m. UTC | #3
>>
> I have some strong thoughts on this, mostly related to:  can someone
> explain to me why this is safe?
> 
> I've seen that OpenSSH 7.7 now implements something similar (xmss
> hash-based signatures,
> https://tools.ietf.org/html/draft-irtf-cfrg-xmss-hash-based-signatures-12,
> disabled by default) but that works entirely differently from what is
> proposed here; amongst other things, it uses *one time private keys* to
> maximize security - do we want to add something like that to our TLS
> stuff (my gut answer: no).
> 
> How can a hash replace a public key, cryptographically speaking? if you
> are not replacing public keys, then who generates, stores, maintains
> these public keys?  What about accounting in such a setup? with
> certificates, you normally hand out a certificate to a person, with the
> name of the person/entity in it. If that person misbehaves, you revoke
> the certificate and presto. Is it sufficient to remove the hash from the
> config? if so, wouldn't it be better to have a directory with
> hash-files, so that you can add and remove hashes without having to
> update the server config and/or restart the server to pick up new hashes?
> 
When you sign a certificate you are actually singing the hash of the
certificate. So you essentially are saying: "This certificate with the
hash xxxyyy is trusted by my CA". Traditionally we used the MD5 of the
certificate, then SHA1 and now SHA256 which we signed. (See the weak md5
discussion).

The reason that the hash is signed instead of the public is that this
way you are also signing the other properties of the certificate (e.g.
CN, extensions, etc.). If you can the public key (or any other property
of the certificate) also the hash of the certificate changes.

If you just have a list of hashes that you trust you just cut out the
middle man (the CA) that establishes the trust relationship for you.


Arne

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Jan Just Keijser May 23, 2018, 5:14 a.m. UTC | #4
Hi Arne,

On 23/05/18 16:46, Arne Schwabe wrote:
>> I have some strong thoughts on this, mostly related to:  can someone
>> explain to me why this is safe?
>>
>> I've seen that OpenSSH 7.7 now implements something similar (xmss
>> hash-based signatures,
>> https://tools.ietf.org/html/draft-irtf-cfrg-xmss-hash-based-signatures-12,
>> disabled by default) but that works entirely differently from what is
>> proposed here; amongst other things, it uses *one time private keys* to
>> maximize security - do we want to add something like that to our TLS
>> stuff (my gut answer: no).
>>
>> How can a hash replace a public key, cryptographically speaking? if you
>> are not replacing public keys, then who generates, stores, maintains
>> these public keys?  What about accounting in such a setup? with
>> certificates, you normally hand out a certificate to a person, with the
>> name of the person/entity in it. If that person misbehaves, you revoke
>> the certificate and presto. Is it sufficient to remove the hash from the
>> config? if so, wouldn't it be better to have a directory with
>> hash-files, so that you can add and remove hashes without having to
>> update the server config and/or restart the server to pick up new hashes?
>>
> When you sign a certificate you are actually singing the hash of the
> certificate. So you essentially are saying: "This certificate with the
> hash xxxyyy is trusted by my CA". Traditionally we used the MD5 of the
> certificate, then SHA1 and now SHA256 which we signed. (See the weak md5
> discussion).
>
> The reason that the hash is signed instead of the public is that this
> way you are also signing the other properties of the certificate (e.g.
> CN, extensions, etc.). If you can the public key (or any other property
> of the certificate) also the hash of the certificate changes.
>
> If you just have a list of hashes that you trust you just cut out the
> middle man (the CA) that establishes the trust relationship for you.
>
>
I understand that part - it's how certificate pinning etc work. However, 
for a "regular" TLS connection (or any assymmetric encryprion scheme) 
you normally need a public key and a private key in order to establish a 
connection. However, with certificate pinning all you do is *ADD* an 
extra check, not replace a check. You will need and use both the public 
and the private key to establish security. How is this done in the 
proposed patch?

JJK


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Arne Schwabe May 24, 2018, 12:17 a.m. UTC | #5
>> When you sign a certificate you are actually singing the hash of the
>> certificate. So you essentially are saying: "This certificate with the
>> hash xxxyyy is trusted by my CA". Traditionally we used the MD5 of the
>> certificate, then SHA1 and now SHA256 which we signed. (See the weak md5
>> discussion).
>>
>> The reason that the hash is signed instead of the public is that this
>> way you are also signing the other properties of the certificate (e.g.
>> CN, extensions, etc.). If you can the public key (or any other property
>> of the certificate) also the hash of the certificate changes.
>>
>> If you just have a list of hashes that you trust you just cut out the
>> middle man (the CA) that establishes the trust relationship for you.
>>
>>
> I understand that part - it's how certificate pinning etc work. However,
> for a "regular" TLS connection (or any assymmetric encryprion scheme)
> you normally need a public key and a private key in order to establish a
> connection. However, with certificate pinning all you do is *ADD* an
> extra check, not replace a check. You will need and use both the public
> and the private key to establish security. How is this done in the
> proposed patch?

Private and public key are still used. The patch stil uses certificates
and TLS, it only replaces the check certificate of the peer's
certificate against the CA with a hash check (certificate pinning if you
want).

So basically instead of saying that you trust all certificates signed by
a CA, you only trust only those certifcates of which have hashes. A
certificate pinning of an unknown CA is exactly the same. Since you
cannot verify that certificate you add a one off certificate in your
list of trusted certificates.

Arne

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
David Sommerseth May 24, 2018, 9:30 a.m. UTC | #6
On 24/05/18 12:17, Arne Schwabe wrote:
> 
>>> When you sign a certificate you are actually singing the hash of the
>>> certificate. So you essentially are saying: "This certificate with the
>>> hash xxxyyy is trusted by my CA". Traditionally we used the MD5 of the
>>> certificate, then SHA1 and now SHA256 which we signed. (See the weak md5
>>> discussion).
>>>
>>> The reason that the hash is signed instead of the public is that this
>>> way you are also signing the other properties of the certificate (e.g.
>>> CN, extensions, etc.). If you can the public key (or any other property
>>> of the certificate) also the hash of the certificate changes.
>>>
>>> If you just have a list of hashes that you trust you just cut out the
>>> middle man (the CA) that establishes the trust relationship for you.
>>>
>>>
>> I understand that part - it's how certificate pinning etc work. However,
>> for a "regular" TLS connection (or any assymmetric encryprion scheme)
>> you normally need a public key and a private key in order to establish a
>> connection. However, with certificate pinning all you do is *ADD* an
>> extra check, not replace a check. You will need and use both the public
>> and the private key to establish security. How is this done in the
>> proposed patch?
> 
> Private and public key are still used. The patch stil uses certificates
> and TLS, it only replaces the check certificate of the peer's
> certificate against the CA with a hash check (certificate pinning if you
> want).
> 
> So basically instead of saying that you trust all certificates signed by
> a CA, you only trust only those certifcates of which have hashes. A
> certificate pinning of an unknown CA is exactly the same. Since you
> cannot verify that certificate you add a one off certificate in your
> list of trusted certificates.

Correct me if I'm wrong, but this approach allows for self-signed certificates
too, right?
Simon Rozman May 24, 2018, 3:41 p.m. UTC | #7
Hi,

> > Private and public key are still used. The patch stil uses
> > certificates and TLS, it only replaces the check certificate of the
> > peer's certificate against the CA with a hash check (certificate
> > pinning if you want).
> >
> > So basically instead of saying that you trust all certificates signed
> > by a CA, you only trust only those certifcates of which have hashes. A
> > certificate pinning of an unknown CA is exactly the same. Since you
> > cannot verify that certificate you add a one off certificate in your
> > list of trusted certificates.
> 
> Correct me if I'm wrong, but this approach allows for self-signed
certificates
> too, right?

Exactly! Client and server can use whatever certificate they like or make a
self-signed one. All they need to do is to exchange their fingerprints over
some trustworthy channel.

Simple. Like SSH.

Best regards,
Simon
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Antonio Quartulli May 24, 2018, 9:57 p.m. UTC | #8
On 25/05/18 09:41, Simon Rozman wrote:
> Hi,
> 
>>> Private and public key are still used. The patch stil uses
>>> certificates and TLS, it only replaces the check certificate of the
>>> peer's certificate against the CA with a hash check (certificate
>>> pinning if you want).
>>>
>>> So basically instead of saying that you trust all certificates signed
>>> by a CA, you only trust only those certifcates of which have hashes. A
>>> certificate pinning of an unknown CA is exactly the same. Since you
>>> cannot verify that certificate you add a one off certificate in your
>>> list of trusted certificates.
>>
>> Correct me if I'm wrong, but this approach allows for self-signed
> certificates
>> too, right?
> 
> Exactly! Client and server can use whatever certificate they like or make a
> self-signed one. All they need to do is to exchange their fingerprints over
> some trustworthy channel.
> 
> Simple. Like SSH.

As a side note, this approach might not need to disable the CA: a
preliminary check against the trusted fingerprints can be performed and
then fallback to the normal CA check in case of failure (but I'd make
openvpn *clearly* log what is happening to avoid debug nightmares).

Regards,
Jan Just Keijser May 25, 2018, 3:51 a.m. UTC | #9
Hi,

On 25/05/18 03:41, Simon Rozman wrote:
>>> Private and public key are still used. The patch stil uses
>>> certificates and TLS, it only replaces the check certificate of the
>>> peer's certificate against the CA with a hash check (certificate
>>> pinning if you want).
>>>
>>> So basically instead of saying that you trust all certificates signed
>>> by a CA, you only trust only those certifcates of which have hashes. A
>>> certificate pinning of an unknown CA is exactly the same. Since you
>>> cannot verify that certificate you add a one off certificate in your
>>> list of trusted certificates.
>> Correct me if I'm wrong, but this approach allows for self-signed
> certificates
>> too, right?
> Exactly! Client and server can use whatever certificate they like or make a
> self-signed one. All they need to do is to exchange their fingerprints over
> some trustworthy channel.
>
> Simple. Like SSH.
>
>
for the record: this is not entirely the same as SSH. What happens when 
establishing an SSH connection to a new server is that you send the 
*public key* to the server, not a hash; similarly the server sends its 
public key to the client. The client needs to accept this new key, 
otherwise the connection is aborted. For public/priv authentication to 
work, the server must know the public key of the client (listed in the 
authorized_keys file) , otherwise the user is prompted for a password. 
Thus, for SSH a 'trustworthy channel' consists of the user typing 'y' 
when connecting to a new server and the server accepting a 
username+password upon first connection from a client.

To implement a similar feature in OpenVPN would require a way to send 
only the public key upon TLS initialisation, and these public keys would 
then need to be stored/listed on the server somewhere. Probably 
possible, but a different approach then to what is proposed above. I'd 
be (slightly) in favour of sending public keys instead of hashes, BTW, 
as it them more closely mimicks what SSH does.

JM2CW,

JJK


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Selva Nair May 25, 2018, 4:07 a.m. UTC | #10
Hi,

On Fri, May 25, 2018 at 9:51 AM, Jan Just Keijser <janjust@nikhef.nl> wrote:
> Hi,
>
> On 25/05/18 03:41, Simon Rozman wrote:
>>>>
>>>> Private and public key are still used. The patch stil uses
>>>> certificates and TLS, it only replaces the check certificate of the
>>>> peer's certificate against the CA with a hash check (certificate
>>>> pinning if you want).
>>>>
>>>> So basically instead of saying that you trust all certificates signed
>>>> by a CA, you only trust only those certifcates of which have hashes. A
>>>> certificate pinning of an unknown CA is exactly the same. Since you
>>>> cannot verify that certificate you add a one off certificate in your
>>>> list of trusted certificates.
>>>
>>> Correct me if I'm wrong, but this approach allows for self-signed
>>
>> certificates
>>>
>>> too, right?
>>
>> Exactly! Client and server can use whatever certificate they like or make
>> a
>> self-signed one. All they need to do is to exchange their fingerprints
>> over
>> some trustworthy channel.
>>
>> Simple. Like SSH.
>>
>>
> for the record: this is not entirely the same as SSH. What happens when
> establishing an SSH connection to a new server is that you send the *public
> key* to the server, not a hash; similarly the server sends its public key to
> the client. The client needs to accept this new key, otherwise the
> connection is aborted. For public/priv authentication to work, the server
> must know the public key of the client (listed in the authorized_keys file)
> , otherwise the user is prompted for a password. Thus, for SSH a
> 'trustworthy channel' consists of the user typing 'y' when connecting to a
> new server and the server accepting a username+password upon first
> connection from a client.
>
> To implement a similar feature in OpenVPN would require a way to send only
> the public key upon TLS initialisation, and these public keys would then
> need to be stored/listed on the server somewhere. Probably possible, but a
> different approach then to what is proposed above. I'd be (slightly) in
> favour of sending public keys instead of hashes, BTW, as it them more
> closely mimicks what SSH does.

Me too chiming in :)

JJK, I think you are misreading this proposal. No hash is being sent
as a part of the handshake  -- its still client and server
certificates that are exchanged and checked during handshake. The hash
is exchanged by a separate channel (say snail mail:) in advance, and
serves the purpose of establishing trust (ie., the prior knowledge of
hash replaces the prior knowledge of a trusted CA). How the hash is
exchanged is beyond the scope of openvpn or TLS handshake in this
case.

Selva

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Jan Just Keijser May 25, 2018, 4:43 a.m. UTC | #11
Hi Selva,

On 25/05/18 16:07, Selva Nair wrote:
> On Fri, May 25, 2018 at 9:51 AM, Jan Just Keijser <janjust@nikhef.nl> wrote:
>> On 25/05/18 03:41, Simon Rozman wrote:
>>>>> Private and public key are still used. The patch stil uses
>>>>> certificates and TLS, it only replaces the check certificate of the
>>>>> peer's certificate against the CA with a hash check (certificate
>>>>> pinning if you want).
>>>>>
>>>>> So basically instead of saying that you trust all certificates signed
>>>>> by a CA, you only trust only those certifcates of which have hashes. A
>>>>> certificate pinning of an unknown CA is exactly the same. Since you
>>>>> cannot verify that certificate you add a one off certificate in your
>>>>> list of trusted certificates.
>>>> Correct me if I'm wrong, but this approach allows for self-signed
>>> certificates
>>>> too, right?
>>> Exactly! Client and server can use whatever certificate they like or make
>>> a
>>> self-signed one. All they need to do is to exchange their fingerprints
>>> over
>>> some trustworthy channel.
>>>
>>> Simple. Like SSH.
>>>
>>>
>> for the record: this is not entirely the same as SSH. What happens when
>> establishing an SSH connection to a new server is that you send the *public
>> key* to the server, not a hash; similarly the server sends its public key to
>> the client. The client needs to accept this new key, otherwise the
>> connection is aborted. For public/priv authentication to work, the server
>> must know the public key of the client (listed in the authorized_keys file)
>> , otherwise the user is prompted for a password. Thus, for SSH a
>> 'trustworthy channel' consists of the user typing 'y' when connecting to a
>> new server and the server accepting a username+password upon first
>> connection from a client.
>>
>> To implement a similar feature in OpenVPN would require a way to send only
>> the public key upon TLS initialisation, and these public keys would then
>> need to be stored/listed on the server somewhere. Probably possible, but a
>> different approach then to what is proposed above. I'd be (slightly) in
>> favour of sending public keys instead of hashes, BTW, as it them more
>> closely mimicks what SSH does.
> Me too chiming in :)
>
> JJK, I think you are misreading this proposal. No hash is being sent
> as a part of the handshake  -- its still client and server
> certificates that are exchanged and checked during handshake. The hash
> is exchanged by a separate channel (say snail mail:) in advance, and
> serves the purpose of establishing trust (ie., the prior knowledge of
> hash replaces the prior knowledge of a trusted CA). How the hash is
> exchanged is beyond the scope of openvpn or TLS handshake in this
> case.
>
>
no, I've heard a lot and talked a lot about this proposal before it 
ended up on the list. I do know what the purpose is, it's just that I 
have serious doubts about replacing
   ( pub/priv key plus  'trust anchors' such as CA certificates )
by
   ( we all trust each other because we know each other's SHA2 hashes )
There are downsides to a PKI with certificates but I think we're 
throwing out too much of the good stuff by allowing "just a hash" as the 
basis for trust.  And one of my main concerns is that people keep 
comparing it to "that's just like how SSH does it" - *THAT* is simply 
not true.

JM2CW,

JJK

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Simon Rozman May 25, 2018, 10:56 a.m. UTC | #12
Hi,

> > JJK, I think you are misreading this proposal. No hash is being sent
> > as a part of the handshake  -- its still client and server
> > certificates that are exchanged and checked during handshake. The hash
> > is exchanged by a separate channel (say snail mail:) in advance, and
> > serves the purpose of establishing trust (ie., the prior knowledge of
> > hash replaces the prior knowledge of a trusted CA). How the hash is
> > exchanged is beyond the scope of openvpn or TLS handshake in this
> > case.

Right to the point, Selva. This is the best description of this proposal.

> no, I've heard a lot and talked a lot about this proposal before it ended up 
> on
> the list. I do know what the purpose is, it's just that I have serious 
> doubts
> about replacing
>    ( pub/priv key plus  'trust anchors' such as CA certificates ) by
>    ( we all trust each other because we know each other's SHA2 hashes )
> There are downsides to a PKI with certificates but I think we're throwing 
> out
> too much of the good stuff by allowing "just a hash" as the basis for
> trust.  And one of my main concerns is that people keep comparing it to
> "that's just like how SSH does it" - *THAT* is simply not true.

JJK, I am sorry I brought SSH as an example. I didn't mean "exactly" like SSH. 
Just, "kind of like" SSH.

In this proposal, we leave the TLS handshake to handle public key exchange as 
usual. No need to modify client<->server communication.
The only difference is how server and client verify peer's certificate 
validity. Normally, they check peer's certificate fields like "Not valid 
before", "Not valid after", "Issued By" etc. In this proposal, they'd only 
check peer certificate by its SHA thumbprint - and skip all other standard 
certificate checks.

This would allow you to have a CA-less OpenVPN setup:
- Make self-signed certificate on server and each client (with like 100 years 
validity),
- Deploy server certificate hash in client.ovpn,
- List acceptable client certificate hashes in server.ovpn (Or use an external 
script to do the hash lookup)

Best regards,
Simon
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Eric Crist May 25, 2018, 11:05 a.m. UTC | #13
What does this accomplish you can’t just basically do with —client-cert-not-required?

Eric Crist

> On May 25, 2018, at 3:56 PM, Simon Rozman <simon@rozman.si> wrote:
> 
> Hi,
> 
>>> JJK, I think you are misreading this proposal. No hash is being sent
>>> as a part of the handshake  -- its still client and server
>>> certificates that are exchanged and checked during handshake. The hash
>>> is exchanged by a separate channel (say snail mail:) in advance, and
>>> serves the purpose of establishing trust (ie., the prior knowledge of
>>> hash replaces the prior knowledge of a trusted CA). How the hash is
>>> exchanged is beyond the scope of openvpn or TLS handshake in this
>>> case.
> 
> Right to the point, Selva. This is the best description of this proposal.
> 
>> no, I've heard a lot and talked a lot about this proposal before it ended up 
>> on
>> the list. I do know what the purpose is, it's just that I have serious 
>> doubts
>> about replacing
>>   ( pub/priv key plus  'trust anchors' such as CA certificates ) by
>>   ( we all trust each other because we know each other's SHA2 hashes )
>> There are downsides to a PKI with certificates but I think we're throwing 
>> out
>> too much of the good stuff by allowing "just a hash" as the basis for
>> trust.  And one of my main concerns is that people keep comparing it to
>> "that's just like how SSH does it" - *THAT* is simply not true.
> 
> JJK, I am sorry I brought SSH as an example. I didn't mean "exactly" like SSH. 
> Just, "kind of like" SSH.
> 
> In this proposal, we leave the TLS handshake to handle public key exchange as 
> usual. No need to modify client<->server communication.
> The only difference is how server and client verify peer's certificate 
> validity. Normally, they check peer's certificate fields like "Not valid 
> before", "Not valid after", "Issued By" etc. In this proposal, they'd only 
> check peer certificate by its SHA thumbprint - and skip all other standard 
> certificate checks.
> 
> This would allow you to have a CA-less OpenVPN setup:
> - Make self-signed certificate on server and each client (with like 100 years 
> validity),
> - Deploy server certificate hash in client.ovpn,
> - List acceptable client certificate hashes in server.ovpn (Or use an external 
> script to do the hash lookup)
> 
> Best regards,
> Simon
> 
> ------------------------------------------------------------------------------
> Check out the vibrant tech community on one of the world's most
> engaging tech sites, Slashdot.org! http://sdm.link/slashdot
> _______________________________________________
> Openvpn-devel mailing list
> Openvpn-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/openvpn-devel
<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto">What does this accomplish you can’t just basically do with —client-cert-not-required?<br><br><div id="AppleMailSignature"><div style="direction: inherit;">Eric Crist</div></div><div><br>On May 25, 2018, at 3:56 PM, Simon Rozman &lt;<a href="mailto:simon@rozman.si">simon@rozman.si</a>&gt; wrote:<br><br></div><blockquote type="cite"><div><span>Hi,</span><br><span></span><br><blockquote type="cite"><blockquote type="cite"><span>JJK, I think you are misreading this proposal. No hash is being sent</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>as a part of the handshake &nbsp;-- its still client and server</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>certificates that are exchanged and checked during handshake. The hash</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>is exchanged by a separate channel (say snail mail:) in advance, and</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>serves the purpose of establishing trust (ie., the prior knowledge of</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>hash replaces the prior knowledge of a trusted CA). How the hash is</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>exchanged is beyond the scope of openvpn or TLS handshake in this</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>case.</span><br></blockquote></blockquote><span></span><br><span>Right to the point, Selva. This is the best description of this proposal.</span><br><span></span><br><blockquote type="cite"><span>no, I've heard a lot and talked a lot about this proposal before it ended up </span><br></blockquote><blockquote type="cite"><span>on</span><br></blockquote><blockquote type="cite"><span>the list. I do know what the purpose is, it's just that I have serious </span><br></blockquote><blockquote type="cite"><span>doubts</span><br></blockquote><blockquote type="cite"><span>about replacing</span><br></blockquote><blockquote type="cite"><span> &nbsp;&nbsp;( pub/priv key plus &nbsp;'trust anchors' such as CA certificates ) by</span><br></blockquote><blockquote type="cite"><span> &nbsp;&nbsp;( we all trust each other because we know each other's SHA2 hashes )</span><br></blockquote><blockquote type="cite"><span>There are downsides to a PKI with certificates but I think we're throwing </span><br></blockquote><blockquote type="cite"><span>out</span><br></blockquote><blockquote type="cite"><span>too much of the good stuff by allowing "just a hash" as the basis for</span><br></blockquote><blockquote type="cite"><span>trust. &nbsp;And one of my main concerns is that people keep comparing it to</span><br></blockquote><blockquote type="cite"><span>"that's just like how SSH does it" - *THAT* is simply not true.</span><br></blockquote><span></span><br><span>JJK, I am sorry I brought SSH as an example. I didn't mean "exactly" like SSH. </span><br><span>Just, "kind of like" SSH.</span><br><span></span><br><span>In this proposal, we leave the TLS handshake to handle public key exchange as </span><br><span>usual. No need to modify client&lt;-&gt;server communication.</span><br><span>The only difference is how server and client verify peer's certificate </span><br><span>validity. Normally, they check peer's certificate fields like "Not valid </span><br><span>before", "Not valid after", "Issued By" etc. In this proposal, they'd only </span><br><span>check peer certificate by its SHA thumbprint - and skip all other standard </span><br><span>certificate checks.</span><br><span></span><br><span>This would allow you to have a CA-less OpenVPN setup:</span><br><span>- Make self-signed certificate on server and each client (with like 100 years </span><br><span>validity),</span><br><span>- Deploy server certificate hash in client.ovpn,</span><br><span>- List acceptable client certificate hashes in server.ovpn (Or use an external </span><br><span>script to do the hash lookup)</span><br><span></span><br><span>Best regards,</span><br><span>Simon</span><br><span></span><br></div></blockquote><blockquote type="cite"><div><span>------------------------------------------------------------------------------</span><br><span>Check out the vibrant tech community on one of the world's most</span><br><span>engaging tech sites, <a href="http://Slashdot.org">Slashdot.org</a>! <a href="http://sdm.link/slashdot">http://sdm.link/slashdot</a></span></div></blockquote><blockquote type="cite"><div><span>_______________________________________________</span><br><span>Openvpn-devel mailing list</span><br><span><a href="mailto:Openvpn-devel@lists.sourceforge.net">Openvpn-devel@lists.sourceforge.net</a></span><br><span><a href="https://lists.sourceforge.net/lists/listinfo/openvpn-devel">https://lists.sourceforge.net/lists/listinfo/openvpn-devel</a></span><br></div></blockquote></body></html>
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Simon Rozman May 25, 2018, 11:23 a.m. UTC | #14
Hi,

> What does this accomplish you can’t just basically do with —client-cert-not-
> required?

I am using --client-cert-not-required already. :)

But that simplifies only the client half of the equation.

TLS server will always need a certificate. And client will always need to verify it to prevent MITM attacks. So, you still need CA.

The idea is to have a choice to drop CA completely. Of course, like JJK said: PKI is great and has its advantages. However, for small setups and some use-cases I'd be willing to live without it and have some simpler mean to validate mutual trust.

Best regards,
Simon
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Jan Just Keijser May 28, 2018, 5:52 a.m. UTC | #15
Hi all,

On 25/05/18 22:56, Simon Rozman wrote:
>>> JJK, I think you are misreading this proposal. No hash is being sent
>>> as a part of the handshake  -- its still client and server
>>> certificates that are exchanged and checked during handshake. The hash
>>> is exchanged by a separate channel (say snail mail:) in advance, and
>>> serves the purpose of establishing trust (ie., the prior knowledge of
>>> hash replaces the prior knowledge of a trusted CA). How the hash is
>>> exchanged is beyond the scope of openvpn or TLS handshake in this
>>> case.
> Right to the point, Selva. This is the best description of this proposal.
>
>> no, I've heard a lot and talked a lot about this proposal before it ended up
>> on
>> the list. I do know what the purpose is, it's just that I have serious
>> doubts
>> about replacing
>>     ( pub/priv key plus  'trust anchors' such as CA certificates ) by
>>     ( we all trust each other because we know each other's SHA2 hashes )
>> There are downsides to a PKI with certificates but I think we're throwing
>> out
>> too much of the good stuff by allowing "just a hash" as the basis for
>> trust.  And one of my main concerns is that people keep comparing it to
>> "that's just like how SSH does it" - *THAT* is simply not true.
> JJK, I am sorry I brought SSH as an example. I didn't mean "exactly" like SSH.
> Just, "kind of like" SSH.
>
> In this proposal, we leave the TLS handshake to handle public key exchange as
> usual. No need to modify client<->server communication.
> The only difference is how server and client verify peer's certificate
> validity. Normally, they check peer's certificate fields like "Not valid
> before", "Not valid after", "Issued By" etc. In this proposal, they'd only
> check peer certificate by its SHA thumbprint - and skip all other standard
> certificate checks.
>
> This would allow you to have a CA-less OpenVPN setup:
> - Make self-signed certificate on server and each client (with like 100 years
> validity),
> - Deploy server certificate hash in client.ovpn,
> - List acceptable client certificate hashes in server.ovpn (Or use an external
> script to do the hash lookup)
>
>

this discussion has muddled along both off and on this mailing list. I apologize in advance for being nitpicky, but when 
touching the "core" security layer of OpenVPN I think we need to be very careful and express very carefully and precisely what 
it is we want to add/change/remove.

As far as I can now oversee, what the original (off-list) proposal has boiled down (or watered down) to now, is

1. wouldn't it be great to allow users to specify self-signed certificates instead of having to use a full-blown PKI or CA 
certificate?
For this, an option like "ca none" would useful, esp since this is explicitly allowed since the TLV v1.1 spec (before that, it 
was not clear whether it was allowed or not).
Therefore, I tend to ACK the feature request to allow an 'empty' CA list, so that people can more easily use self-signed certs.

Note that it currently is possible to come close to using self-signed certs, by creating CA certificates and use those as 
host/client certs as well (I will need to test this a bit further on different platforms, and I have no clue how mbedTLS likes this)

2. instead of storing a certificate on each side, wouldn't it be nice to be able to store the public key only of the 
certificate, or perhaps even a hash of the public key of the certificate?
To me, storing either the certificate itself is not a problem (I recall Jason Donenfeld wanting to get rid of X509 certs 
altogether - something I strongly disagree with), but storing its public key should also be good - this mimicka SSH pub/priv 
key, so we could even re-use parts of the SSH code for this. One could even imagine getting the public key for a particular user 
out of his/her ~/.ssh/authorized_keys file.
Then, to make things even shorter, it was proposed to add *hashes* of public keys of certificates. To me, the added value of 
using a 256 bit hash instead of a 1024/2048 bit pubkey is limited, and I would like some assurance that this indeed foolproof. 
Other than that, I wouldn't NACK this feature, as long as it remains optional.

3. how a person gets the public key or hash of the client or server side certificate to the "other side" is pretty much out of 
scope: it simply requires "a secure channel". I am fine with that.

Note that browser certificate pinning is something else entirely: that is an *extra* check , on top of the cert chain check, to 
ensure that a server certificate (for, e.g. google.com) was signed by one of the "pinned" CA certificates. Browsers tend to 
trust a large number of CA certs, so this extra check makes sense there. As long as OpenVPN is not configured to trust 100+ CA's 
then this check does not make a lot fo sense in OpenVPN.



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Arne Schwabe May 28, 2018, 6:18 a.m. UTC | #16
> 
> 2. instead of storing a certificate on each side, wouldn't it be nice to
> be able to store the public key only of the certificate, or perhaps even
> a hash of the public key of the certificate?
> To me, storing either the certificate itself is not a problem (I recall
> Jason Donenfeld wanting to get rid of X509 certs altogether - something
> I strongly disagree with), but storing its public key should also be
> good - this mimicka SSH pub/priv key, so we could even re-use parts of
> the SSH code for this. One could even imagine getting the public key for
> a particular user out of his/her ~/.ssh/authorized_keys file.
> Then, to make things even shorter, it was proposed to add *hashes* of
> public keys of certificates. To me, the added value of using a 256 bit
> hash instead of a 1024/2048 bit pubkey is limited, and I would like some
> assurance that this indeed foolproof. Other than that, I wouldn't NACK
> this feature, as long as it remains optional.

There seem to be some confusion about what is secure here and what not.
I will give a bite more detailed example here to explain the pubkey vs
hash check.

Simplified a X509 looks like this:

X509Cert {
  CN=foo.example.net
  PUBKEY=00:aa:bb:cc
  usage="TLSServer"
  other attributes
}
Signature {
  SigningCAHash=00:bb:cc
  SHA1Signature=binarysting
}

On a normal CA Check, the client will do a hash over the whole of the
X509Cert (including CN and all other attributes). Then it will fetch the
CA that signed the certificate, gets its public key and checks if the
signature that is appended to the certifcate is valid and signs the hash
it calculated before.

The reason that we hash the whole certificate and sign the hash and also
check the hash against the signature instead of the pubkey is that we
want to have the other attributes to be validated as well. If we only
signed the pubkey, a malicious party could just edit the CN of the
certificate and the certificate would still be valid.

What is proposed here is that instead of taking the signature of the
certificate and checking if the signature sign that hash, we just look
if we can find the hash in our list of trusted hash.

I have not check the proposol in detail but if we implment something
like this (especially with server side support), I would strongly
suggest verify the hash/fingerprint of the certificate instead of using
only the pubkey. Then there are no surprises for people that are not
intimate with X509/SSL that client can fake the CN.


> 
> 3. how a person gets the public key or hash of the client or server side
> certificate to the "other side" is pretty much out of scope: it simply
> requires "a secure channel". I am fine with that.
> 
> Note that browser certificate pinning is something else entirely: that
> is an *extra* check , on top of the cert chain check, to ensure that a
> server certificate (for, e.g. google.com) was signed by one of the
> "pinned" CA certificates. Browsers tend to trust a large number of CA
> certs, so this extra check makes sense there. As long as OpenVPN is not
> configured to trust 100+ CA's then this check does not make a lot fo
> sense in OpenVPN.

For self signed certificates checking the hash against the well known
exception list (add this certificate as exception) also becomes the only
check.

Arne

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
François Kooman June 5, 2018, 10:59 a.m. UTC | #17
On 04/17/2018 06:50 PM, Jason A. Donenfeld wrote:
>    * Allow specifying 'none' to the --ca parameter, to specify that
>      certificates should not be checked against a CA. Note that 'none'
>      is already used in other similar options as a special placeholder.
> 
>    * When '--ca none' is in use, --verify-hash checks all depths instead
>      of just level 1.
> 
> With these very simple changes, fingerprint authentication is easily achieved
> via the --tls-verify script on the server and via --verify-hash on the client.

This is a great idea! I already played around with it, and it works as
advertised :)

I recently thought about how to integrate this properly. The thing that
is still missing is using fingerprints as CNs. The --status command, and
management interface (--management) use CNs to identify clients when
certificates are used. This would then also need to be modified to use
'fingerprints' as identifiers so you would see the fingerprints when you
use --status and can use fingerprints when you do "kill" through the
management interface.

An option similar to --username-as-common-name, e.g.
--fingerprint-as-common-name could be introduced to accomplish this in
an easy way, although it is a bit "hacky".

Cheers,
François

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

Patch

============

Make self-signed cert:
$ openssl req -x509 -newkey ec:<(openssl ecparam -name secp384r1) -keyout key.pem -out cert.pem -nodes -sha256 -days 3650 -subj '/CN=server'

Record our fingerprint in an environment variable for the client to use later:
$ server_fingerprint="$(openssl x509 -in cert.pem -noout -sha256 -fingerprint | sed 's/.*=//;s/\(.*\)/\L\1/')"

Start openvpn with tls verify script:
$ sudo openvpn --server 10.66.0.0 255.255.255.0 --dev tun --dh none --ca none --cert cert.pem --key key.pem --tls-verify $(readlink -f tls-verify.sh) --script-security 2

TLS Verify Script:
==================

#!/bin/sh
[ -n "$tls_digest_sha256_0" -a -e "/tmp/allowed-openvpn-fingerprints/$tls_digest_sha256_0" ]

Client side:
============

Make self-signed cert:
$ openssl req -x509 -newkey ec:<(openssl ecparam -name secp384r1) -keyout key.pem -out cert.pem -nodes -sha256 -days 3650 -subj '/CN=client'

"Tell" the server about our fingerprint:
$ mkdir -p /tmp/allowed-openvpn-fingerprints; touch "/tmp/allowed-openvpn-fingerprints/$(openssl x509 -in cert.pem -noout -sha256 -fingerprint | sed 's/.*=//;s/\(.*\)/\L\1/')"

Start openvpn with server fingerprint verification:
$ sudo openvpn --client --remote 127.0.0.1 --dev tun --ca none --cert cert.pem --key key.pem --verify-hash "$server_fingerprint" SHA256 --nobind

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
Sorry for the double post. Somebody suggested I submit this as an actual
git patch instead of as just part of the goofy email I sent earlier. So
here's that email turned into a proper patch that you can apply. Feel
free to hack the patch to pieces or discard it and start over.

 src/openvpn/init.c               | 1 +
 src/openvpn/options.c            | 9 ++++++++-
 src/openvpn/options.h            | 1 +
 src/openvpn/ssl.c                | 2 +-
 src/openvpn/ssl_common.h         | 1 +
 src/openvpn/ssl_verify.c         | 2 +-
 src/openvpn/ssl_verify_mbedtls.c | 2 +-
 src/openvpn/ssl_verify_openssl.c | 2 +-
 8 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 36c1a4c4..4da994d8 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2722,6 +2722,7 @@  do_init_crypto_tls(struct context *c, const unsigned int flags)
     to.remote_cert_eku = options->remote_cert_eku;
     to.verify_hash = options->verify_hash;
     to.verify_hash_algo = options->verify_hash_algo;
+    to.ca_file_none = options->ca_file_none;
 #ifdef ENABLE_X509ALTUSERNAME
     to.x509_username_field = (char *) options->x509_username_field;
 #else
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 426057ab..dad706d9 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3259,7 +3259,10 @@  options_postprocess_filechecks(struct options *options)
 
     /* ** SSL/TLS/crypto related files ** */
     errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->dh_file, R_OK, "--dh");
-    errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->ca_file, R_OK, "--ca");
+    if (!options->ca_file_none)
+    {
+        errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->ca_file, R_OK, "--ca");
+    }
     errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE, options->ca_path, R_OK, "--capath");
     errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->cert_file, R_OK, "--cert");
     errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->extra_certs_file, R_OK,
@@ -7638,6 +7641,10 @@  add_option(struct options *options,
         {
             options->ca_file_inline = p[2];
         }
+        else if (streq(p[1], "none"))
+        {
+            options->ca_file_none = true;
+        }
     }
 #ifndef ENABLE_CRYPTO_MBEDTLS
     else if (streq(p[0], "capath") && p[1] && !p[2])
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index f7d0145a..6ccdca49 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -489,6 +489,7 @@  struct options
     /* TLS (control channel) parms */
     bool tls_server;
     bool tls_client;
+    bool ca_file_none;
     const char *ca_file;
     const char *ca_path;
     const char *dh_file;
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 669f941b..0ef0f31f 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -693,7 +693,7 @@  init_ssl(const struct options *options, struct tls_root_ctx *new_ctx)
         }
     }
 
-    if (options->ca_file || options->ca_path)
+    if ((!options->ca_file_none && options->ca_file) || options->ca_path)
     {
         tls_ctx_load_ca(new_ctx, options->ca_file, options->ca_file_inline,
                         options->ca_path, options->tls_server);
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 08ef6ffa..e9ac5271 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -270,6 +270,7 @@  struct tls_options
     uint8_t *verify_hash;
     hash_algo_type verify_hash_algo;
     char *x509_username_field;
+    bool ca_file_none;
 
     /* allow openvpn config info to be
      * passed over control channel */
diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c
index 25395b27..61873e6f 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -720,7 +720,7 @@  verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep
     }
 
     /* verify level 1 cert, i.e. the CA that signed our leaf cert */
-    if (cert_depth == 1 && opt->verify_hash)
+    if ((opt->ca_file_none || cert_depth == 1) && opt->verify_hash)
     {
         struct buffer ca_hash = {0};
 
diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c
index fd31bbbd..147fb983 100644
--- a/src/openvpn/ssl_verify_mbedtls.c
+++ b/src/openvpn/ssl_verify_mbedtls.c
@@ -63,7 +63,7 @@  verify_callback(void *session_obj, mbedtls_x509_crt *cert, int cert_depth,
     cert_hash_remember(session, cert_depth, &cert_fingerprint);
 
     /* did peer present cert which was signed by our root cert? */
-    if (*flags != 0)
+    if (*flags != 0 && !session->opt->ca_file_none)
     {
         int ret = 0;
         char errstr[512] = { 0 };
diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c
index 9b984751..10d351f4 100644
--- a/src/openvpn/ssl_verify_openssl.c
+++ b/src/openvpn/ssl_verify_openssl.c
@@ -66,7 +66,7 @@  verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
     cert_hash_remember(session, X509_STORE_CTX_get_error_depth(ctx), &cert_hash);
 
     /* did peer present cert which was signed by our root cert? */
-    if (!preverify_ok)
+    if (!preverify_ok && !session->opt->ca_file_none)
     {
         /* get the X509 name */
         char *subject = x509_get_subject(current_cert, &gc);