[erlang-questions] Erlang SSL and Invalid Certificate Chains

Ben Murphy benmmurphy@REDACTED
Mon Aug 11 16:16:47 CEST 2014

(I originally sent this to erlang-bugs but it doesn't look like it
made it there)

We have a problem where an SSL server sends back a certificate chain
that is invalid according to the TLS 1.2 specification and erlang
rejects this chain with an unknown ca error. However, openssl and
browsers will accept this chain because they are less strict about

The chain looks something like:

0. Server Cert issued by Intermediate Cert
1. Intermediate Cert issued by Root Cert
2. Root Cert issued by Root Cert
3. Unrelated certificate
4. Unrelated certificate

Which is invalid according to: http://www.ietf.org/rfc/rfc5246.txt

      This is a sequence (chain) of certificates.  The sender's
      certificate MUST come first in the list.  Each following
      certificate MUST directly certify the one preceding it.  Because
      certificate validation requires that root keys be distributed
      independently, the self-signed certificate that specifies the root
      certificate authority MAY be omitted from the chain, under the
      assumption that the remote end must already possess it in order to
      validate it in any case

Looking at the openssl code they start at the beginning of the chain
then recursively find the issuer in order to build up a chain. While
the erlang ssl code assumes the last certificate in the chain is the
root CA (ssl_certificate:trusted_cert_and_path).

Maybe this is more of a feature request than a bug. But I was
wondering if it would be possible for erlang to either accept these
dodgy chains, provide an option when connecting to accept these dodgy
chains or allow users to supply a function to modify the certificate
chain before validation takes place.

I have some code that I've been mucking around with to fix up chains
but I haven't done thorough testing of it.

fix_path(CertChain) ->
  DecodedCerts = [ {public_key:pkix_decode_cert(Cert, otp), Cert} ||
Cert <- CertChain ],
  [Peer | RestOfChain] = DecodedCerts,
  lists:reverse(make_chain(Peer, RestOfChain, [])).

make_chain({OtpCert, Cert}, CertChain, ResultChain) ->
  case public_key:pkix_is_self_signed(OtpCert) of
    true ->
      [Cert | ResultChain ];
    false ->
        case find_issuer(OtpCert, CertChain) of
          {ok, NewCert, NewCertChain} ->
            % we remove the cert that was found from the chain
            % to prevent infinite loops where a chain becomes
            % a loop
            make_chain(NewCert, NewCertChain, [Cert | ResultChain]);
          {error, issuer_not_found} ->
            % assume it is the 'trusted' certificate
            [Cert | ResultChain]

find_issuer(OtpCert, CertChain) ->
  {Not, Maybe} = lists:splitwith(fun({Candidate, _}) ->
    not public_key:pkix_is_issuer(OtpCert, Candidate)
  end, CertChain),

  case Maybe of
    [Issuer | Rest] ->
      {ok, Issuer, Not ++ Rest};
    [] ->
      {error, issuer_not_found}

More information about the erlang-questions mailing list