Skip to content

Instantly share code, notes, and snippets.

@leonardb
Last active March 9, 2020 13:01
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save leonardb/a4dc25651ba3682966bafe5c7d1f575c to your computer and use it in GitHub Desktop.
%% Extract basic information from PEM
-module(pem_info).
-export([details/1]).
-include_lib("public_key/include/public_key.hrl").
details(Pem) ->
[{'Certificate',Cert,_}|_] = public_key:pem_decode(Pem),
#'OTPCertificate'{'tbsCertificate' = #'OTPTBSCertificate'{
issuer = Issuer,
validity = #'Validity'{'notBefore' = Start,
'notAfter' = End},
subject = Subject0,
extensions = Exts,
'serialNumber' = Serial
}} = public_key:pkix_decode_cert(Cert, otp),
%% days before expiration
DiffDays = expire_days(End),
Subject = rdnSeq(Subject0, ?'id-at-commonName'),
AltNames = case exten(Exts, ?'id-ce-subjectAltName') of
undefined -> undefined;
[Subject] -> undefined;
Alts -> lists:delete(Subject, Alts)
end,
{ok, #{
cert_subject => Subject,
cert_issuer => rdnSeq(Issuer, ?'id-at-commonName'),
cert_start => to_date(Start),
cert_end => to_date(End),
cert_expire_days => DiffDays,
cert_altnames => AltNames,
serial => Serial
}}.
expire_days(End) ->
Start = calendar:datetime_to_gregorian_seconds(to_date(End)),
Now = calendar:datetime_to_gregorian_seconds(erlang:universaltime()),
{DiffDays, {_DiffHours,_,_}} = calendar:seconds_to_daystime(Start - Now),
DiffDays.
rdnSeq({'rdnSequence', Seq}, Match) ->
rdnSeq(Seq, Match);
rdnSeq([[{'AttributeTypeAndValue', Match, Result}]|_], Match) ->
str(Result);
rdnSeq([_|T], Match) ->
rdnSeq(T, Match);
rdnSeq([], _) ->
undefined.
str({'printableString', Str}) ->
Str;
str({'utf8String', Str}) ->
erlang:binary_to_list(Str);
str({'dNSName', Str}) ->
Str.
exten('asn1_NOVALUE', _) ->
undefined;
exten([], _) ->
undefined;
exten([#'Extension'{'extnID' = Match, 'extnValue' = Values}|_], Match) ->
[ str(DNS) || DNS <- Values];
exten([_|T], Match) ->
exten(T, Match).
to_date({{_,_,_}, {_,_,_}} = Date) ->
Date;
to_date({utcTime, Date}) ->
case re:run(Date, "(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})Z",[{capture,all_but_first,list}]) of
{match, Matches} ->
[Y,M,D,H,Mm,S] = lists:map(fun(X) -> erlang:list_to_integer(X) end, Matches),
{{2000+Y, M, D}, {H, Mm, S}};
_ -> error
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment