[erlang-questions] bad certificate if trying to verify StartSsl certificate

Ben Murphy <>
Thu Sep 10 12:44:30 CEST 2015


I have a verify function that hacks around this problem. It adds the
certs to a list during the 'verification' then it resorts and passes
it off to the path validation. However, this function only supports
validation from a single root cert because we are using it in
production to connect to a server that has a chain signed by a
non-public CA. You use it like: {verify_fun,
fixed_root_lenient_verifier:create_verify_function(DerCaCert, 10)}

Use this module at your own risk it may effectively disable your SSL
security. I really think this resorting should be done in OTP or OTP
should supply a cleaner hook for resorting. A hook that gives you the
chain and the cacerts and lets you send back a new chain would be
perfect :)

-module(fixed_root_lenient_verifier).

-record(state, {certs = [], root_cert, max_path_length}).

-export([verify/3]).
-export([create_verify_function/2]).

create_verify_function(DerCertificate, MaxPathLength) ->
  DecodedCert = public_key:pkix_decode_cert(DerCertificate, otp),
  {fun fixed_root_lenient_verifier:verify/3, #state{root_cert =
DecodedCert, max_path_length = MaxPathLength}}.

append_cert(State, Cert) ->
  State#state{certs = [Cert | State#state.certs]}.

default_verify(_Cert, Event, State) ->
  case Event of
    {bad_cert, Reason} ->  {fail, Reason};
    {extension, _} -> {unknown, State};
    valid -> {valid, State};
    valid_peer -> {valid, State}
  end.


verify(Cert, Event, State) ->
  case Event of
    {bad_cert, _Reason} ->
      {valid, append_cert(State, Cert)};
    {extension, _} -> {unknown, State};
    valid -> {valid, append_cert(State, Cert)};
    valid_peer ->
      case final_verification(State#state.certs, Cert,
State#state.max_path_length, State#state.root_cert) of
        {ok, _} -> {valid, State};
        {error, Reason} -> {fail, Reason}
      end
  end.

final_verification(Certs, PeerCert, MaxPathLength, RootCertificate) ->
  Chain = fix_path(PeerCert, Certs),
  public_key:pkix_path_validation(RootCertificate, Chain,
[{max_path_length, MaxPathLength}, {verify_fun, {fun default_verify/3,
none}}]).


fix_path(Peer, RestOfChain) ->
  make_chain(Peer, RestOfChain, []).

make_chain(OtpCert, CertChain, ResultChain) ->
  case public_key:pkix_is_self_signed(OtpCert) of
    true ->
      [OtpCert | 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, [OtpCert | ResultChain]);
          {error, issuer_not_found} ->
            % assume it is the 'trusted' certificate
            [OtpCert | ResultChain]
        end
  end.

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}
  end.

On Thu, Sep 10, 2015 at 10:02 AM, Benoit Chesneau <> wrote:
> I
> On Tue, Aug 11, 2015 at 9:54 AM Ingela Andin <> wrote:
>>
>> Hi!
>>
>> 2015-07-16 11:16 GMT+02:00 Alex Hudich <>:
>>>
>>> Hi!
>>>
>>>
>>>
>>> wget http://curl.haxx.se/ca/cacert.pem
>>>
>>> and then
>>>
>>> ssl:connect( "www.nicemine.ru", 443,
>>> [{verify,verify_peer},{server_name_indication,"www.nicemine.ru"},{depth,2},{cacertfile,"cacert.pem"}]
>>> ).
>>>
>>> gives me {error,{tls_alert,"bad certificate"}}
>>>
>>>
>>>
>>
>> This site is not sending a correct certificate chain,  I get all the
>> certificates that shall be in the chain but scrambled around and not in the
>> correct order, this is breaking the
>> SSL/TLS-protocol. OpenSSL will also get the error above when trying to
>> verify that chain, but later versions of OpenSSL and also other
>> implementations obviously tries to work around this by attempting to sort
>> them and run the validation again.
>>
>> You could do that too using the verify_fun if you really want to. We would
>> rather not make that a default feature as breaking security protocols is
>> usually a bad idea that could lead to vulnerabilities.
>>
>>
>> Regards Ingela Erlang/OTP Team - Ericsson AB
>>
>
>
> I have the same issue on another host: rest-api.pay.nl:
>
> 15> ssl:connect( "rest-api.pay.nl", 443,
> [{verify,verify_peer},{server_name_indication,"rest-api.pay.nl"},{depth,2},{cacertfile,
> "priv/ca-bundle.crt"}] ).
>
> =ERROR REPORT==== 10-Sep-2015::11:01:31 ===
> SSL: certify: ssl_handshake.erl:1476:Fatal error: bad certificate
> {error,{tls_alert,"bad certificate"}}
>
>
> the chain looks correct for me and curl handle it without issue. What do you
> mean by sorting certificates ? Any example?
>
> - benoit
>
>
>
>>
>>
>>>
>>> Why? Site can be opened ok in the browser.
>>>
>>> Erlang/OTP 17 [erts-6.3]
>>>
>>>
>>> _______________________________________________
>>> erlang-questions mailing list
>>> 
>>> http://erlang.org/mailman/listinfo/erlang-questions
>>
>> _______________________________________________
>> erlang-questions mailing list
>> 
>> http://erlang.org/mailman/listinfo/erlang-questions
>
>
> _______________________________________________
> erlang-questions mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-questions
>


More information about the erlang-questions mailing list