[erlang-questions] Fwd: Figuring out proper ssl certificate settings with 17.3

Vincent de Phily vincent.dephily@REDACTED
Thu Feb 19 18:43:26 CET 2015

On Thursday 22 January 2015 21:45:08 Ingela Andin wrote:
> Hi!

Hi, thanks for the detailed explanations. Sorry for my late reply : urgent 
tasks, holidays and the need to digest the info all contributed to the delay.

> There was a bug in R16 that if the client sends only its peer-cert and the
> server happens to own the intermediate CA cert that signed the client cert
> it concludes that the intermediate CA cert is the trusted cert and
> everything works.

That behavior made sense to me, I wouldn't have labeled it as a bug :p If I 
know/trust a CA that signed one of the certs that the peer sends me in its 
certificate_list, then I can trust the whole list that the peer sent.

> But according to the TLS protocol the client must send
> the whole chain only the self signed ROOT may be excluded from the chain.
> However  pkix-X509 certificate path validation says it should be possible
> to put the trust in an intermediate cert,

I'm not going to pretend that I know better than the RFC writers, but taken 
together (particularly the RFC quotes from your changeset[1]) it seems that 
rfc5246's "only the top/root cert may be ommited" rule should be understood as 
"only the certs that are expected to be known by the peer may be ommited".

> and that is why we have the
> partial_chain to allow path validation on only part of the chain, so that
> means the server that accepts a partial chain could know of the
> intermediate cert but not own the ROOT, as it has decided to trust the
> intermediate CA.

I don't understand why certs specified in {cacerts,[der_encoded()]} are not 
trusted. What is the usecase of providing untrusted certs in cacerts ?

I've also been unable to make use of the partial_chain option the way I 
expected it to work (to ensure that what I put in cacert is trusted) :

  {certfile,      Account1Device1Cert},
  {cacerts,       [ServerCA]}
  {partial_chain, fun(_) -> {trusted_ca,ServerCA} end}
  {certfile,      Account1ServerCert},
  {cacerts,       [ClientCA]}
  {partial_chain, fun(_) -> {trusted_ca,ClientCA} end}

It seems to be because the fun only accepts to accept a CA as the base of a 
partial chain if that CA has been sent by the peer. But in my case the CA has 
not been sent by the peer : it's expected to be in the local list of trusted 

> So a TLS client that can not build its whole chain is incorrectly
> configured.

Depends on what you call "whole". The question is wether one can follow its 
chain up to a trusted certificate, not wether one can follow its chain up to a 
self-signed certificate.

On the wider internet, the recommendation that a peer sends a cert chain with 
all CAs except the last is a good one, because you never know which CA is in 
the other peer's store. When in doubt, send as much info as possible.

But sending a CA that is already known/trusted by the peer is a waste of 
bandwidth, and there are plenty of usecases where you can expect the peer to 
know the necessary CA. That's why "send the whole chain excluding the root" is 
only recomended, not mandatory (unless there's a security issue I missed ?).

> E.i. the partial chain function lets you  shorten the chain by accepting an
> intermediate cert sent to you by the peer as trusted.

Ok, that's what I eventually understood. But why is it limited to intermediate 
certs *sent by the peer* ? I've got the intermediate cert right there in my 
local cacerts list. It has signed the leaf cert sent by the client.

> If you want to handle
> incorrect clients  by building the chain to the client certificate on the
> server side , if possible, you need to do that in the verify_fun when it
> fails and then call public_key:pkix_path_validation again with the chain
> that you built.

I didn't try this yet, but I verified that
> {partial_chain, fun(CAs) -> {trusted_ca,hd(CAs)} end}
"solves" my issue but is basically a verify_none. It seems there's some 
functionality overlap between partial_chain and verify_fun, but I'm not 100% 
sure when to use which.

I'd like OTP to do the "peer-sent cert chain is signed by one of the localy-
trusted CAs" step for me. I realize that I could do it myself using the 
public_key module, but I find it very surprising that my usecase would be so 
unusual that it isn't supported out of the box. A peer that sends the smallest 
chain that can be verified by the other peer isn't "incorrect".

> As the self-signed root cert may be left out of the chain client software
> will guess that if the signer of a cert is not found it must be the
> self-signed root, however if
> the client is not correctly configured this could be a wrong guess.

I'm not sure I understand this. Why guess ? Either you have the CA that signed 
a cert (wether that's a root CA or an intermediate one) or you don't.

> > I guess some confusion comes from the fact that the cacerts option is used
> > for
> > two different things (authenticating the cert received by the peer, and
> > sending intermediate CAs to the peer to help it authenticate me). Maybe
> > the
> > option could be split in two ?
> This is documented and you will find the same behaviour in openSSL.

It may be, but that doesn't make it a clear API :p Openssl isn't renowned for 
having a good API.

It's nice that OTP can use 'cacerts' to build the chain that signs 'cert', but 
sometimes the resulting chain is overkill. And maybe I don't want to trust a 
peer whose cert is signed by the same CA as my own cert ?

What about allowing 'cert' to be [der_encoded()] instead of der_encoded() ? If 
a list is provided, use that as the chain. If a single cert is provided, try 
to build a chain using cacerts.

> Client side:
> *{cacertfile, path()}*The path to a file containing PEM encoded CA
> certificates. The CA certificates are used during server authentication and
> when building the client certificate chain.
> Sever side:
> *{cacertfile, path()}*
> The path to a file containing PEM encoded CA certificates. The CA
> certificates are used to build the server certificate chain, and for client
> authentication. Also the CAs are used in the list of acceptable client CAs
> passed to the client when a certificate is requested. May be omitted if
> there is no need to verify the client and if there are not any intermediate
> CAs for the server certificate.

The wordings are different, but they seem compatible. One is just wordyer than 
the other. Also, if cacertfile really is different between client and server, 
why isn't cacerts ?

I know that, thanks to the web, most authentications are only one-way so that 
it's tempting to provide more information in one of the cases. But really, 
authentication should be symetrical (wether it's sending your certs to the 
peer or listing the CAs you trust).

> Hope that helps.

I does, thanks. Things are clearer in my head, but I'm still not convinced by 
the new OTP behavior ;)


Vincent de Phily

More information about the erlang-questions mailing list