Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get client certificate without validation using Erlang

I'm relatively new to Erlang and want to write a small server that uses the client certificate to identify the client. The client should be able to use any public/private key pair without it being part of a certificate chain. I looked at the SSL examples from the OTP source code and used make_certs.erl to create the key pairs. I can connect using the created client certificate, but not using a self signed certificate.

How can I get the client certificate without validating it?

My sample code is currently:

-module(simple_server).

-export([start/0]).

keep_alive() ->
    receive
        Any -> io:format("Listening socket: ~p~n",[Any])
    end.


start() ->
    ssl:start(),
    spawn(fun() ->
        start_parallel_server(3333),
        keep_alive()
    end).


start_parallel_server(Port) ->
    case ssl:listen(Port, [
        binary,
        {packet, 0},
        {reuseaddr, true},
        {active, true},
        {certfile,"../etc/server/cert.pem"},
        {keyfile,"../etc/server/key.pem"},
        {cacertfile,"../etc/server/cacerts.pem"},
        {verify,verify_peer},
        {fail_if_no_peer_cert,true}
    ]) of
        {ok,Listen} ->
            spawn(fun() -> par_connect(Listen) end);
        {error,Reason} ->
            io:format("error ~p~n",[Reason])
    end.



par_connect(Listen) ->
    case ssl:transport_accept(Listen) of
        {ok,Socket} ->
            spawn(fun() -> par_connect(Listen) end),
            ssl:ssl_accept(Socket),
            print_cert(Socket),
            get_request(Socket,[]);
        {error,Reason} ->
            io:format("Listening stopped (~p)~n",[Reason])
    end.

print_cert(Socket) ->
    case ssl:peercert(Socket) of
        {ok,Cert} ->
            io:format("Certificate: ~p~n",[Cert]);
        {error,Reason} ->
            io:format("Certificate error ~p~n",[Reason])
    end.


get_request(Socket,L) ->
    receive
        {ssl, Socket, Bin} ->
            io:format("Server received: ~p~n",[Bin]),
            get_request(Socket,L);
        {ssl_closed, Socket} ->
            io:format("Socket did disconnect~n");
        Reason ->
            io:format("Client error: ~p~n",[Reason])
    end.
like image 802
thomasguenzel Avatar asked Aug 24 '15 17:08

thomasguenzel


1 Answers

You need to supply a custom path verification function to ssl:listen that allows self-signed certificates.

The verify_fun option (see the docs) lets you specify a function that is called when a certification verification error is encountered. We can take the default implementation (given in the docs) and make sure the selfsigned_peer case returns success:

{verify_fun, {fun(_, {bad_cert, selfsigned_peer}, UserState) ->
                   {valid, UserState}; %% Allow self-signed certificates

                 (_,{bad_cert, _} = Reason, _) ->
                   {fail, Reason};

                 (_,{extension, _}, UserState) ->
                   {unknown, UserState};

                 (_, valid, UserState) ->
                   {valid, UserState};

                 (_, valid_peer, UserState) ->
                   {valid, UserState}
               end, []}}
like image 174
johlo Avatar answered Oct 13 '22 23:10

johlo