ntrepid8 / ex_crypto

Wrapper around the Erlang crypto module for Elixir.
MIT License
144 stars 48 forks source link

Loading private key from PEM file fails #27

Open adamu opened 6 years ago

adamu commented 6 years ago

This is the same issue as reported here: https://stackoverflow.com/questions/41336947/erlang-generate-rsa-keys-from-pem-files

In this case it causes ExPublicKey.load/2 to return an {:error, _} tuple.

Loading the corresponding public key produces the :RSAPublicKey as expected.

Steps to reproduce

I'm using OpenSSL 1.0.2o

  1. Generate keys
    openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out test_private.pem
    openssl pkey -pubout -inform PEM -outform PEM -in test_private.pem -out test_public.pem
  2. Load keys
iex(1)> ExPublicKey.load("test_public.pem")
{:ok, #ExPublicKey.RSAPublicKey<
   fingerprint_sha256=
     a5...>}
iex(2)> ExPublicKey.load("test_private.pem")
{:error,
 "invalid argument, expected one of[ExPublicKey.RSAPublicKey, ExPublicKey.RSAPrivateKey], found: PrivateKeyInfo"}

Possible solution

Use :public_key.der_decode/2 with the data provided from the PrivateKeyInfo tuple. I'm assuming the the algorithm identifier is contained inside the PrivateKeyInfo data, but I don't know how to extract it so I've hard-coded :RSAPrivateKey here.

defmodule Key do
  def load_priv_key() do
    {:PrivateKeyInfo, :v1, _, private_key_binary, _} =
      File.read!("test_private.pem")
      |> :public_key.pem_decode() |> hd
      |> :public_key.pem_entry_decode()

    :public_key.der_decode(:RSAPrivateKey, private_key_binary)
    |> ExPublicKey.RSAPrivateKey.from_sequence()
  end
end
iex(1)> Key.load_priv_key
#ExPublicKey.RSAPrivateKey<
  fingerprint_sha256=
    a5...>
adamu commented 6 years ago

I realised the fact that the key is unencrypted is not important. This is the same for encrypted keys.

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out priv.pem -des3 -pass pass:foo

  def load_priv_key() do
    {:PrivateKeyInfo, :v1, _, private_key_binary, _} =
      File.read!("priv.pem")
      |> :public_key.pem_decode() |> hd
      |> :public_key.pem_entry_decode("foo")

    :public_key.der_decode(:RSAPrivateKey, private_key_binary)
    |> ExPublicKey.RSAPrivateKey.from_sequence()
iex(1)> ExPublicKey.load("priv.pem", "foo")
{:error,
 "invalid argument, expected one of[ExPublicKey.RSAPublicKey, ExPublicKey.RSAPrivateKey], found: PrivateKeyInfo"}
iex(2)> Key.load_priv_key
#ExPublicKey.RSAPrivateKey<
  fingerprint_sha256=
    7a