Umer Kiani
Umer Kiani

Reputation: 3370

Elixir / Erlang: Certificate Chain Validation

I have a use case where the certificate chain has to be validated for PKI. I have two certs, one is the device cert and the other is the certificate_chain. Both are in pem format. The:public_key.pkix_path_validation/3 seems promising but I don’t know how to give the chain in der format. I am converting the device cert to der using X509.Certificate.to_der but how will I convert the chain to der, since it has 3 certificates( Root_CA, Intermediate_CA, Signing_CA) and when I convert it using the X509 library and give it to :public_key.pkix_path_validation/3 . Basically I want to achieve the alternative to “openssl verify -CAfile certs/root_ca.pem -untrusted cert_chain.pem certs/device_cert.pem” in elixir.

I made some progress and wrote a method to read the certificate and pass it for validation my method to read the certificate for chain validation is

  defmodule Cert do
  def stubChainValidation do
    certRaw = File.read!("software_signing.pem")
    {:ok, certEncoded} = X509.Certificate.from_pem(certRaw)
    certChainRaw = File.read!("chain.pem")
    certChain = :public_key.pem_decode(certChainRaw)

    cert_chain_decoded =
      Enum.map(
        cert_chain,
        fn {_, bin, _} -> bin end
      )

    :public_key.pkix_path_validation(certEncoded, 
cert_chain_decoded, [{:max_path_length, 0}])
  end
end

When I run this function I get the output of Invalid issuer

{:error, {:bad_cert, :invalid_issuer}}

Upvotes: 1

Views: 713

Answers (1)

Umer Kiani
Umer Kiani

Reputation: 3370

After spending nearly one complete week I Figured out what I was doing wrong, Erlang expects the parameters in a different way, you need to pass the root and then the chain which contains the intermediate and signing certificate.

I am attaching a sample implementation for the Erlang Community just in case someone gets stuck doing the validation in the future.

defmodule Cert do
  def verify_fun(_, {:extension, _}, state) do
    {:unknown, state}
  end

  def verify_fun(_, {:bad_cert, reason}, _state) do
    {:fail, reason}
  end

  def verify_fun(_, {:revoked, _}, _state) do
    {:fail, :revoked}
  end

  def verify_fun(_cert, _event, state) do
    {:unknown, state}
  end

  def stubChainValidation do
    Application.ensure_all_started(:inets)
    Application.ensure_all_started(:ssl)
    Application.ensure_all_started(:public_key)
    certRaw = File.read!("./certs/root_ca.pem")
    {:ok, certEncoded} = X509.Certificate.from_pem(certRaw)

    certChainRaw = File.read!("./certs/chain_2.pem")
    certChain = :public_key.pem_decode(certChainRaw)

    cert_chain_decoded =
      Enum.map(
        certChain,
        fn {_, bin, _} -> bin end
      )

case :public_key.pkix_path_validation(certEncoded, cert_chain_decoded, [
       {:verify_fun, {&verify_fun/3, {}}}
     ]) do
  {:ok, {_public_key_info, _policy_tree}} ->
    {:ok, cert_chain_decoded}

  {:error, {:bad_cert, reason}} ->
    {:error, reason}
    end
  end
end

Upvotes: 2

Related Questions