Skip to content

Commit

Permalink
Add in arguments for supporting base64 serialization and domain name …
Browse files Browse the repository at this point in the history
…extraction
  • Loading branch information
Fitblip committed Mar 5, 2018
1 parent b43b417 commit d192d17
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 6 deletions.
51 changes: 46 additions & 5 deletions lib/easy_ssl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,56 @@ defmodule EasySSL do
}
}
"""
def parse_der(certificate_der) when is_binary(certificate_der) do
def parse_der(certificate_der, opts \\ [all_domains: false, serialize: false]) when is_binary(certificate_der) do
cert = :public_key.pkix_decode_cert(certificate_der, :otp) |> get_field(:tbsCertificate)

%{}
serialized_certificate = %{}
|> Map.put(:fingerprint, certificate_der |> fingerprint_cert)
|> Map.put(:serial_number, cert |> get_field(:serialNumber) |> Integer.to_string(16))
|> Map.put(:subject, cert |> parse_subject)
|> Map.put(:extensions, cert |> parse_extensions)
|> Map.merge(parse_expiry(cert))

Enum.reduce(opts, serialized_certificate, fn {option, flag}, serialized_certificate ->
case option do
:all_domains when flag == true ->
serialized_certificate
|> Map.put(:all_domains, get_all_domain_names(cert, serialized_certificate))

:serialize when flag == true ->
serialized_certificate
|> Map.put(:as_der, Base.encode64(certificate_der))
_ -> serialized_certificate
end
end)
end

def get_all_domain_names(cert, serialized_cert) do
domain_names = MapSet.new()

domain_names = case serialized_cert[:subject][:CN] do
nil -> domain_names
_ -> MapSet.put(domain_names, serialized_cert[:subject][:CN])
end

extensions = cert |> get_field(:extensions)

extensions
|> Enum.reduce(domain_names, fn extension, domain_names ->
case extension do
{:Extension, {2, 5, 29, 17}, _critical, san_entries} ->
san_entries
|> Enum.reduce(domain_names, fn entry, names ->
case entry do
{:dNSName, dns_name} -> MapSet.put(names, dns_name |> to_string)
_ -> names
end
end)
:asn1_NOVALUE -> domain_names
_ -> domain_names
end
end)
|> MapSet.to_list
end

@doc """
Expand Down Expand Up @@ -104,7 +145,7 @@ defmodule EasySSL do
}
"""
def parse_pem(cert_charlist) when is_list(cert_charlist) do parse_pem(cert_charlist |> to_string) end
def parse_pem(cert_pem) do
def parse_pem(cert_pem, opts \\ [all_domains: false, return_base64: false]) do
cert_regex = ~r/^\-{5}BEGIN\sCERTIFICATE\-{5}\n(?<certificate>[^\-]+)\-{5}END\sCERTIFICATE\-{5}/
match = Regex.named_captures(cert_regex, cert_pem)

Expand All @@ -115,7 +156,7 @@ defmodule EasySSL do
match["certificate"]
|> String.replace("\n", "")
|> Base.decode64!
|> parse_der
|> parse_der(opts)
end

defp get_field(record, field) do
Expand Down Expand Up @@ -284,7 +325,7 @@ defmodule EasySSL do

# Basically ignore those
{:directoryName, _sequence} -> san_list
{:otherName, other_name} -> san_list
{:otherName, _sequence} -> san_list

_ ->
raise("Unhandled SAN entry type #{inspect entry}")
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule EasySSL.MixProject do
[
app: :easy_ssl,
name: "EasySSL",
version: "1.0.2",
version: "1.0.3",
elixir: "~> 1.6",
description: "SSL/X509 parsing for humans.",
deps: deps(),
Expand Down
24 changes: 24 additions & 0 deletions test/easy_ssl_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,28 @@ defmodule EasySSLTest do
assert_has_normal_atom_keys(cert)
end

test "parses and adds all domains to the top level leaf node" do
cert_bytes = File.read!(@der_cert_dir <> "twitter.com.der")

serialized_cert = cert_bytes
|> EasySSL.parse_der()
refute Enum.member?(Map.keys(serialized_cert), :as_der)
refute Enum.member?(Map.keys(serialized_cert), :all_domains)

serialized_cert = cert_bytes
|> EasySSL.parse_der(all_domains: true)
refute Enum.member?(Map.keys(serialized_cert), :as_der)
assert Enum.member?(Map.keys(serialized_cert), :all_domains)

serialized_cert = cert_bytes
|> EasySSL.parse_der(serialize: true)
assert Enum.member?(Map.keys(serialized_cert), :as_der)
refute Enum.member?(Map.keys(serialized_cert), :all_domains)

serialized_cert = cert_bytes
|> EasySSL.parse_der(serialize: true, all_domains: true)
assert Enum.member?(Map.keys(serialized_cert), :as_der)
assert Enum.member?(Map.keys(serialized_cert), :all_domains)
end

end

0 comments on commit d192d17

Please sign in to comment.