2 Using the SSL application
Here we provide an introduction to using the Erlang/OTP SSL application, which is accessed through the ssl interface module.
We also present example code in the Erlang module client_server, also provided in the directory ssl-X.Y.Z/examples, with source code in src and the compiled module in ebin of that directory.
2.1 The ssl Module
The ssl module provides the user interface to the Erlang/OTP SSL application. The interface functions provided are very similar to those provided by the gen_tcp and inet modules.
Servers use the interface functions listen and accept. The listen function specifies a TCP port to to listen to, and each call to the accept function establishes an incoming connection.
Clients use the connect function which specifies the address and port of a server to connect to, and a successful call establishes such a connection.
The listen and connect functions have almost all the options that the corresponding functions in gen_tcp/ have, but there are also additional options specific to the SSL protocol.
The most important SSL specific option is the cacertfile option which specifies a local file containing trusted CA certificates which are and used for peer authentication. This option is used by clients and servers in case they want to authenticate their peers.
The certfile option specifies a local path to a file containing the certificate of the holder of the connection endpoint. In case of a server endpoint this option is mandatory since the contents of the sever certificate is needed in the the handshake preceding the establishment of a connection.
Similarly, the keyfile option points to a local file containing the private key of the holder of the endpoint. If the certfile option is present, this option has to be specified as well, unless the private key is provided in the same file as specified by the certfile option (a certificate and a private key can thus coexist in the same file).
The verify option specifies how the peer should be verified:
- 0
- Do not verify the peer,
- 1
- Verify peer,
- 2
- Verify peer, fail the verification if the peer has no certificate.
The depth option specifies the maximum length of the verification certificate chain. Depth = 0 means the peer certificate, depth = 1 the CA certificate, depth = 2 the next CA certificate etc. If the verification process does not find a trusted CA certificate within the maximum length, the verification fails.
The ciphers option specifies which ciphers to use (a string of colon separated cipher names). To obtain a list of available ciphers, evaluate the ssl:ciphers/0 function (the SSL application has to be running).
2.2 A Client-Server Example
Here is a simple client server example.
%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2003-2009. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% %%% Purpose: Example of SSL client and server using example certificates. -module(client_server). -export([start/0, start/1, init_connect/1]). start() -> start([ssl, subject]). start(CertOpts) -> %% Start ssl application application:start(ssl), %% Always seed ssl:seed("ellynatefttidppohjeh"), %% Let the current process be the server that listens and accepts %% Listen {ok, LSock} = ssl:listen(0, mk_opts(listen)), {ok, {_, LPort}} = ssl:sockname(LSock), io:fwrite("Listen: port = ~w.~n", [LPort]), %% Spawn the client process that connects to the server spawn(?MODULE, init_connect, [{LPort, CertOpts}]), %% Accept {ok, ASock} = ssl:transport_accept(LSock), ok = ssl:ssl_accept(ASock), io:fwrite("Accept: accepted.~n"), {ok, Cert} = ssl:peercert(ASock, CertOpts), io:fwrite("Accept: peer cert:~n~p~n", [Cert]), io:fwrite("Accept: sending \"hello\".~n"), ssl:send(ASock, "hello"), {error, closed} = ssl:recv(ASock, 0), io:fwrite("Accept: detected closed.~n"), ssl:close(ASock), io:fwrite("Listen: closing and terminating.~n"), ssl:close(LSock), application:stop(ssl). %% Client connect init_connect({LPort, CertOpts}) -> {ok, Host} = inet:gethostname(), {ok, CSock} = ssl:connect(Host, LPort, mk_opts(connect)), io:fwrite("Connect: connected.~n"), {ok, Cert} = ssl:peercert(CSock, CertOpts), io:fwrite("Connect: peer cert:~n~p~n", [Cert]), {ok, Data} = ssl:recv(CSock, 0), io:fwrite("Connect: got data: ~p~n", [Data]), io:fwrite("Connect: closing and terminating.~n"), ssl:close(CSock). mk_opts(listen) -> mk_opts("server"); mk_opts(connect) -> mk_opts("client"); mk_opts(Role) -> Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), [{active, false}, {verify, 2}, {depth, 2}, {cacertfile, filename:join([Dir, Role, "cacerts.pem"])}, {certfile, filename:join([Dir, Role, "cert.pem"])}, {keyfile, filename:join([Dir, Role, "key.pem"])}].