Skip to content

Commit 1aa5945

Browse files
authored
feat: add peercert/1 function to get peer SSL certificate (#599) (#813)
Add hackney:peercert/1 function that returns the DER-encoded peer certificate for SSL connections, similar to how peername/1 returns the peer address. The function returns: - {ok, Cert} where Cert is the DER-encoded certificate binary - {error, not_ssl} for non-SSL connections - {error, not_connected} if there's no active connection Fixes #599
1 parent 9d0eb82 commit 1aa5945

3 files changed

Lines changed: 35 additions & 0 deletions

File tree

‎src/hackney.erl‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
-export([connect/1, connect/2, connect/3, connect/4,
1212
close/1,
1313
peername/1,
14+
peercert/1,
1415
sockname/1,
1516
request/1, request/2, request/3, request/4, request/5,
1617
send_request/2,
@@ -401,6 +402,13 @@ normalize_transport(Other) -> Other.
401402
peername(ConnPid) when is_pid(ConnPid) ->
402403
hackney_conn:peername(ConnPid).
403404

405+
%% @doc Get the peer SSL certificate.
406+
%% Returns the DER-encoded certificate of the peer, or an error if the connection
407+
%% is not SSL or the certificate is unavailable.
408+
-spec peercert(conn()) -> {ok, binary()} | {error, term()}.
409+
peercert(ConnPid) when is_pid(ConnPid) ->
410+
hackney_conn:peercert(ConnPid).
411+
404412
%% @doc Get the local address and port.
405413
-spec sockname(conn()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, term()}.
406414
sockname(ConnPid) when is_pid(ConnPid) ->

‎src/hackney_conn.erl‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
%% Socket operations
5454
setopts/2,
5555
peername/1,
56+
peercert/1,
5657
sockname/1,
5758
%% Low-level socket operations (for hackney_request/hackney_response)
5859
send/2,
@@ -352,6 +353,13 @@ setopts(Pid, Opts) ->
352353
peername(Pid) ->
353354
gen_statem:call(Pid, peername).
354355

356+
%% @doc Get the peer SSL certificate.
357+
%% Returns {ok, Cert} where Cert is the DER-encoded certificate binary,
358+
%% or {error, Reason} if the connection is not SSL or the certificate is unavailable.
359+
-spec peercert(pid()) -> {ok, binary()} | {error, term()}.
360+
peercert(Pid) ->
361+
gen_statem:call(Pid, peercert).
362+
355363
%% @doc Get the local address and port.
356364
-spec sockname(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, term()}.
357365
sockname(Pid) ->
@@ -1405,6 +1413,14 @@ handle_common({call, From}, peername, _State, #conn_data{transport = Transport,
14051413
Result = Transport:peername(Socket),
14061414
{keep_state_and_data, [{reply, From, Result}]};
14071415

1416+
handle_common({call, From}, peercert, _State, #conn_data{transport = Transport, socket = Socket} = _Data)
1417+
when Socket =/= undefined ->
1418+
Result = case erlang:function_exported(Transport, peercert, 1) of
1419+
true -> Transport:peercert(Socket);
1420+
false -> {error, not_ssl}
1421+
end,
1422+
{keep_state_and_data, [{reply, From, Result}]};
1423+
14081424
handle_common({call, From}, sockname, _State, #conn_data{transport = Transport, socket = Socket} = _Data)
14091425
when Socket =/= undefined ->
14101426
Result = Transport:sockname(Socket),
@@ -1443,6 +1459,9 @@ handle_common({call, From}, {setopts, _Opts}, _State, _Data) ->
14431459
handle_common({call, From}, peername, _State, _Data) ->
14441460
{keep_state_and_data, [{reply, From, {error, not_connected}}]};
14451461

1462+
handle_common({call, From}, peercert, _State, _Data) ->
1463+
{keep_state_and_data, [{reply, From, {error, not_connected}}]};
1464+
14461465
handle_common({call, From}, sockname, _State, _Data) ->
14471466
{keep_state_and_data, [{reply, From, {error, not_connected}}]};
14481467

‎src/hackney_ssl.erl‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
setopts/2,
1616
controlling_process/2,
1717
peername/1,
18+
peercert/1,
1819
close/1,
1920
shutdown/2,
2021
sockname/1]).
@@ -237,6 +238,13 @@ controlling_process(Socket, Pid) ->
237238
peername(Socket) ->
238239
ssl:peername(Socket).
239240

241+
%% @doc Return the peer certificate of an SSL connection.
242+
%% @see ssl:peercert/1
243+
-spec peercert(ssl:sslsocket()) ->
244+
{ok, binary()} | {error, atom()}.
245+
peercert(Socket) ->
246+
ssl:peercert(Socket).
247+
240248
%% @doc Close a TCP socket.
241249
%% @see ssl:close/1
242250
-spec close(ssl:sslsocket()) -> ok.

0 commit comments

Comments
 (0)