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