brightball

brightball

Has anyone used Erlang's :public_key.der_decode/2 function? I can't seem to get it working

I’ve been trying to figure this out for a few hours now and I’m running into a wall. I’m trying to decode a public key to get the bit strength. I’ve done this in other languages via libraries that interface with OpenSSL but every code example I see using Elixir defer to :public_key.der_decode/2. I’ve even resorted to ChatGPT to give me working example code with sample data and those examples give me the same error.

EDIT: Originally posted this with a sample key that I couldn’t even get working with the OpenSSL code, so I’ve updated it to one that has been verified to work under OpenSSL. Updated error has been posted.

I’m using Erlang 27.1 and as I was researching this I saw that :public_key saw some deprecations for security reasons and I was wondering if it was affected, but I rolled back to OTP 26 and still get the same error.

Any ideas would be greatly appreciated.

Here is the error:

%MatchError{
      term:
        {:error,
         {:asn1,
          {{:wrong_tag,
            {{:expected, 2},
             {:got, 16, {16, [{6, <<42, 134, 72, 134, 247, 13, 1, 1, 1>>}, {5, ""}]}}}},
           [
             {:"OTP-PUB-KEY", :match_tags, 2, [file: ~c"../src/OTP-PUB-KEY.erl", line: 24244]},
             {:"OTP-PUB-KEY", :decode_integer, 2,
              [file: ~c"../src/OTP-PUB-KEY.erl", line: 23761]},
             {:"OTP-PUB-KEY", :dec_RSAPublicKey, 2,
              [file: ~c"../src/OTP-PUB-KEY.erl", line: 2976]},
             {:"OTP-PUB-KEY", :decode, 2, [file: ~c"../src/OTP-PUB-KEY.erl", line: 1239]},
             {:public_key, :der_decode, 2, [file: ~c"public_key.erl", line: 353]},
             {PublicKeyDecoder, :decode_rsa_public_key, 1,
              [file: ~c"lib/public_key_decoder.ex", line: 13]},
             {PublicKeyDecoder, :example_usage, 0,
              [file: ~c"lib/public_key_decoder.ex", line: 39]},
             {:elixir, :eval_external_handler, 3, [file: ~c"src/elixir.erl", line: 386]},
             {:erl_eval, :do_apply, 7, [file: ~c"erl_eval.erl", line: 750]},
             {:elixir, :eval_forms, 4, [file: ~c"src/elixir.erl", line: 364]},
             {Module.ParallelChecker, :verify, 1,
              [file: ~c"lib/module/parallel_checker.ex", line: 120]},
             {IEx.Evaluator, :eval_and_inspect, 3, [file: ~c"lib/iex/evaluator.ex", line: 336]},
             {IEx.Evaluator, :eval_and_inspect_parsed, 3,
              [file: ~c"lib/iex/evaluator.ex", line: 310]},
             {IEx.Evaluator, :parse_eval_inspect, 4, [file: ~c"lib/iex/evaluator.ex", line: 299]},
             {IEx.Evaluator, :loop, 1, [file: ~c"lib/iex/evaluator.ex", line: 189]},
             {IEx.Evaluator, :init, 5, [file: ~c"lib/iex/evaluator.ex", line: 34]},
             {:proc_lib, :init_p_do_apply, 3, [file: ~c"proc_lib.erl", line: 241]}
           ]}}}
    }

Below I’ll provide some sample code from ChatGPT that’s giving the same errors as my code if you’d like to reproduce it.

Just call PublicKeyDecoder.example_usage() to try it.

defmodule PublicKeyDecoder do
  @moduledoc """
  A module to demonstrate decoding a DER-encoded RSA public key using :public_key.der_decode.
  """

  @doc """
  Decodes a DER-encoded RSA public key and extracts its modulus and exponent.

  - `der_encoded_key` should be the binary representation of the public key.
  """
  def decode_rsa_public_key(der_encoded_key) do
    try do
      {:RSAPublicKey, modulus, exponent} = :public_key.der_decode(:RSAPublicKey, der_encoded_key)
      {:ok, %{modulus: modulus, exponent: exponent}}
    rescue
      e -> {:error, "Failed to decode RSA public key: #{inspect(e)}"}
    end
  end

  @doc """
  Calculate the bit strength of the modulus.

  - `modulus` is the integer modulus extracted from the decoded public key.
  """
  def calculate_bit_strength(modulus) do
    :erlang.bit_size(modulus)
  end

  def example_usage() do
    # Example Usage

    # This is a sample Base64-encoded DER-encoded RSA public key.
   base64_public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDW8q5LtUKpUOLpWqiGfDzbUMjP+MEBzfYOq8q1hCST/wyoBqJznRhhKLERfWQ7GKK8X/6hQotoPBEFF2PfZaIXvahalOs7Q40EdCtooCb0Vt/sGH5DIeTWSTFTwHlINHFKBKnH/0oW24XrjxW3jcBmgIxQNBFoOQFHhmjshjLjbQIDAQAB"


    # Decode the Base64 key into binary
    {:ok, der_encoded_key} = Base.decode64(base64_public_key)

    # Decode the RSA public key
    case PublicKeyDecoder.decode_rsa_public_key(der_encoded_key) do
      {:ok, %{modulus: modulus, exponent: exponent}} ->
        IO.puts("Modulus: #{inspect(modulus)}")
        IO.puts("Exponent: #{exponent}")
        bit_strength = PublicKeyDecoder.calculate_bit_strength(modulus)
        IO.puts("Bit strength: #{bit_strength} bits")

      {:error, reason} ->
        IO.puts("Error: #{reason}")
    end
  end
end

Marked As Solved

al2o3cr

al2o3cr

I don’t see the corresponding BN_num_bits function from OpenSSL exposed anywhere in OTP’s implementation - it’s used in a few places but only from C.

A simple alternative would be :binary.encode_unsigned/1 followed by bit_size:

{_, n, _} =
  :public_key.pem_decode("-----BEGIN PUBLIC KEY-----\n" <> base64_public_key <> "\n-----END PUBLIC KEY-----\n")
  |> hd()
  |> :public_key.pem_entry_decode()

bits = n |> :binary.encode_unsigned() |> bit_size()

# bits will be 1024

Also Liked

al2o3cr

al2o3cr

There are two different formats for an “RSA public key”, and der_decode expects one while the input is the opposite.

Backstory:

TLDR:

There are two different ASN.1 payloads for an RSA public key:

  • a “simple” one that’s just the modulus and exponent. Used with the -----BEGIN RSA PUBLiC KEY----- PEM header
  • a “generic” one that uses SubjectPublicKeyInfo and then nests the modulus + exponent inside

Your input is the latter, but der_decode is expecting the former.

The PEM machinery can handle this correctly:

:public_key.pem_decode("-----BEGIN PUBLIC KEY-----\n" <> base64_public_key <> "\n-----END PUBLIC KEY-----\n")
|> hd()
|> :public_key.pem_entry_decode()

# gives:
{:RSAPublicKey,
 150941599098518570126874683465281552058004089482812534451212203604139744439191153555431393200565235808092997600710339447131887772472013591195906357127812043735918563281687598329506755792366482304448753019436841738444187832077672451367879120396964808983355846518616258864015758142866753194926761306968328168301,
 65537}
brightball

brightball

Thanks! This worked like a charm. I’ll share the final function that resulted from it.

Still need to add some error checking probably, but this worked great. Tested it on an assortment of keys and it worked everytime.

  def calculate_key_strength(%{"k" => "ed25519"}), do: 256
  def calculate_key_strength(%{"k" => "rsa", "p" => key}) do

    { _key_type, modulus, _exponent} =
      :public_key.pem_decode("-----BEGIN PUBLIC KEY-----\n" <> key <> "\n-----END PUBLIC KEY-----\n")
      |> hd()
      |> :public_key.pem_entry_decode()

      modulus |> :binary.encode_unsigned() |> bit_size()
  end

Where Next?

Popular in Questions Top

baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: &lt;h1&gt;Create Post&lt;/h1&gt; &lt;%= ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New

Other popular topics Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement