Current section
Files
Jump to
Current section
Files
lib/services/csr.ex
defmodule CA.CSR do
@moduledoc "CA/CSR library."
def ca do
root("secp384r1", rdn: "/C=UA/L=Київ/O=SYNRC/CN=CA")
end
def server(name) do
server("secp384r1", rdn: "/C=UA/L=Київ/O=SYNRC/CN=#{name}", cn: "#{name}")
end
def client(name) do
client("secp384r1", rdn: "/C=UA/L=Київ/O=SYNRC/CN=#{name}", cn: "#{name}")
end
def dir(profile) do
"synrc/ecc/#{profile}/"
end
def root(profile, rdn: rdn) do
:filelib.ensure_dir(dir(profile))
ca_key = X509.PrivateKey.new_ec(:erlang.binary_to_atom(profile))
:logger.info(~c"CSR CMP DN ~p~n", [rdn])
subject_rdn = map_utf8_tags(X509.RDNSequence.new(rdn))
ca =
X509.Certificate.self_signed(ca_key, subject_rdn,
template: %X509.Certificate.Template{
# 25 years
validity: round(25 * 365.2425),
hash: :sha256,
extensions: [
basic_constraints: X509.Certificate.Extension.basic_constraints(true, 1),
key_usage: X509.Certificate.Extension.key_usage([:digitalSignature, :keyCertSign, :cRLSign]),
subject_key_identifier: true,
authority_key_identifier: true
]
}
)
password = :application.get_env(:ca, :password, "0000")
pem = X509.PrivateKey.to_pem(ca_key, password: password)
:file.write_file("#{dir(profile)}/ca.key", pem)
:file.write_file("#{dir(profile)}/ca.pem", X509.Certificate.to_pem(ca))
{ca_key, ca}
end
def server(profile, rdn: rdn, cn: user) do
{ca_key, ca} = read_ca(profile)
:filelib.ensure_dir(dir(profile))
priv = X509.PrivateKey.new_ec(:erlang.binary_to_atom(profile))
password = :application.get_env(:ca, :password, "0000")
pem = X509.PrivateKey.to_pem(priv, password: password)
:file.write_file("#{dir(profile)}/#{user}.key", pem)
:logger.info(~c"CSR SERVER DN ~p~n", [rdn])
subject_rdn = map_utf8_tags(X509.RDNSequence.new(rdn))
csr =
X509.CSR.new(priv, subject_rdn,
extension_request: [
X509.Certificate.Extension.subject_alt_name(["synrc.com"])
]
)
:file.write_file("#{dir(profile)}/#{user}.csr", X509.CSR.to_pem(csr))
true = X509.CSR.valid?(csr)
subject = X509.CSR.subject(csr) |> map_utf8_tags()
server =
X509.Certificate.new(X509.CSR.public_key(csr), subject, ca, ca_key,
extensions: [subject_alt_name: X509.Certificate.Extension.subject_alt_name(["synrc.com"])]
)
:file.write_file("#{dir(profile)}/#{user}.cer", X509.Certificate.to_pem(server))
csr
end
def client(profile, rdn: rdn, cn: user) do
priv = X509.PrivateKey.new_ec(:erlang.binary_to_atom(profile))
password = :application.get_env(:ca, :password, "0000")
pem = X509.PrivateKey.to_pem(priv, password: password)
:file.write_file("#{dir(profile)}/#{user}.key", pem)
:logger.info(~c"CSR CLIENT DN ~p~n", [rdn])
subject_rdn = map_utf8_tags(X509.RDNSequence.new(rdn))
csr =
X509.CSR.new(priv, subject_rdn,
extension_request: [
X509.Certificate.Extension.subject_alt_name(["synrc.com"])
]
)
:file.write_file("#{dir(profile)}/#{user}.csr", X509.CSR.to_pem(csr))
csr
end
def init(profile) do
case :filelib.is_regular("#{CA.CSR.dir(profile)}/ca.key") do
false -> root(profile, rdn: "/C=UA/L=Київ/O=SYNRC/CN=CA")
true -> []
end
end
def read_ca(profile) do
init(profile)
{:ok, ca_key_bin} = :file.read_file("#{CA.CSR.dir(profile)}/ca.key")
{:ok, ca_bin} = :file.read_file("#{CA.CSR.dir(profile)}/ca.pem")
password = :application.get_env(:ca, :password, "0000")
ca_key =
case X509.PrivateKey.from_pem(ca_key_bin, password: password) do
{:ok, key} -> key
_ ->
case X509.PrivateKey.from_pem(ca_key_bin) do
{:ok, key} -> key
end
end
{:ok, ca} = X509.Certificate.from_pem(ca_bin)
{ca_key, ca}
end
def read_ca_public(profile) do
init(profile)
{:ok, ca_bin} = :file.read_file("#{CA.CSR.dir(profile)}/ca.pem")
{:ok, ca} = X509.Certificate.from_pem(ca_bin)
{:ok, bin} = :PKIX1Explicit88.encode(:Certificate, CA.RDN.encodeAttrsCert(ca))
bin
end
defp map_utf8_tags({:rdnSequence, list}) do
tag =
case System.otp_release() |> String.to_integer() do
ver when ver >= 26 -> :utf8String
_ -> :uTF8String
end
{:rdnSequence, Enum.map(list, fn sub_list ->
Enum.map(sub_list, fn
{:SingleAttribute, oid, {t, val}} when t in [:utf8String, :uTF8String] ->
{:SingleAttribute, oid, {tag, val}}
{:AttributeTypeAndValue, oid, {t, val}} when t in [:utf8String, :uTF8String] ->
{:AttributeTypeAndValue, oid, {tag, val}}
other -> other
end)
end)}
end
defp map_utf8_tags(other), do: other
end