0
votes

I am writing a Let's Encrypt ACME client in Elixir+escript. The basics seem to be almost done except whenever I submit I get an error back that the key is too large. I have included full source code as well as the results from IO.inspect.

Code:

defmodule NginxDockerCerts do

  @base "https://acme-staging.api.letsencrypt.org"

  def main(_args) do
    { :ok, %{ body: body } } = HTTPoison.get(@base <> "/directory")
    endpoints = Poison.decode!(body)
    response = send_request(
        endpoints["new-reg"],
        %{ resource: "new-reg", contact: ["mailto:[email protected]"] } )
    IO.inspect(response)
  end


  defp send_request(url, payload) do
    [private_pem] = File.read!("acme_key/private.key") |> :public_key.pem_decode
    private_key = :public_key.pem_entry_decode(private_pem)
    IO.inspect(private_key)
    header = generate_header(private_key)
    request = %{
      payload: b64(payload),
      header: header,
      protected: Map.merge(header, %{ nonce: nonce() }) |> b64
    }
    signature = "#{request[:payload]}.#{request[:protected]}"
      |> :public_key.sign(:sha256, private_key)
      |> b64
    request = Map.put(request, :signature, signature)
    IO.inspect Poison.encode!(request)
    HTTPoison.post(url, Poison.encode!(request))
  end

  def nonce do
    { :ok, %{ headers: le_headers } } = HTTPoison.head(@base <> "/directory")
    %{ "Replay-Nonce" => ret } = Enum.into(le_headers, %{})
    ret
  end

  # From the erlang docs:
  # Key = public_key:pem_entry_decode(RSAEntry, "abcd1234").
  # 'RSAPrivateKey'{version = 'two-prime',
  #  modulus = 1112355156729921663373...2737107,
  #  publicExponent = 65537,
  #  privateExponent = 58064406231183...2239766033,
  #  prime1 = 11034766614656598484098...7326883017,
  #  prime2 = 10080459293561036618240...77738643771,
  #  exponent1 = 77928819327425934607...22152984217,
  #  exponent2 = 36287623121853605733...20588523793,
  #  coefficient = 924840412626098444...41820968343,
  #  otherPrimeInfos = asn1_NOVALUE}
  defp generate_header(tuple) do
    modulus = elem(tuple, 2) |> Integer.to_string |> b64
    exponent = elem(tuple, 3) |> Integer.to_string |> b64
    %{
      alg: "RS256",
      jwk: %{ e: exponent, kty: "RSA", n: modulus }
    }
  end

  defp b64(map) when is_map(map) do
    map |> Poison.encode! |> b64
  end

  defp b64(str) when is_binary(str) do
    Base.url_encode64(str, padding: false)
  end

end

IO.inspect(private_key):

{:RSAPrivateKey, :"two-prime",
 26481417694003873198106692499951294632119767655743036419434387596516366789190701823748669795645320777784137996798720069366288226752787622864701474496474449030506709232606243234272615772772466937031758607350665927756585455359455675325206612466347661342624412526201816026642296038870194629664917766048486662184822018742110266572261560976195129025393833146676682160704214817952205520453818572377683628606127111066471430372867661099887369197771195456883038219184710367619732291239260771178775267372942936652670907351820071078279876378927060202383261106965701867067167942374432028957977689700100770593457365162852102994011,
 65537,
 17914363694762679375590280482557038734074338145129736174308002717419978357245064091366667308389692794651262006012958394118656461740464764327121166238936702897104154200338706284869255844574321706109571964796859390986591027409882468918977602937203771390592387954119924829351148128283413627511087311865963595647490630788471521560987974810414561435248783963374268078499217692971351713183762668005601250077516937260913883929834502151959055904643039470485089018275006363270281504396441774935380614604745759736941304083977023940167676210942357786129557579261694171244151039301867300063202512362347353203092947886172660735393,
 178446771914665404540695905367251634656603255428643793397732858767097609718259368256180025741118899743098924191892492358642536804962239144370164933768905841306132489144152437556760519068703335707439676521498046102518700677420206003309978859591990308609070226460982935297571405985650480509898548679141561783409,
 148399533428755367732844203564110326749419930547476575029427037695010307782491560655098073907520278149390027076214510851700665327903331461330257316301355625227675837876554040085530270141360998128496771695290961076657330075330485591211033119775343431677271581646470921917040927840058534430663755569847957098379,
 173496648605071408699339341045453818586204782529834351152938645769270680587115197307101867039967578203618114203933621017140604584820619131790019979779680353116338191796637764448772807948772568899131006122851732603054596464051422956877924576259853677682245842193987696327661510566545050393063851309736807837969,
 104457312306054806733354226690510299270297554298879135185963633915322955098861989175280059316535971303378417368095146254784372068320316518037537578135981756067227887713555921436525884491068010508044075485229218997318755721273030971941897994237703401844823768755563271113340785238716148920776808645385905807487,
 167919839448914714974178630755108847455535988232500360818424531493548836346962617553207612824957810666270161293456074716336654588650394703385074422991192683242596666041121521405849960615014764544853974028554205453198599086267359147206158727144128096959265897638538959277458154234233457521650291895863656575525,
 :asn1_NOVALUE}

IO.inspect Poison.encode!(request):

"{\"signature\":\"bzWgP-iSDaFG1tl0nbm4jfWEeQbwpfPEGqKdyslskweSpZCMXdgP7NPCAYlbX8W5qExPA9wGtI0uR2tfRA-ALE6prvRxAwzh3BfwPNscwgXzTNUy79KLkW78eFMLrUvU8shtkwxXlmmPCxGd2PAMM5vXUHM_ovVhTHKqojdy5ECzpw6u03k2wJZzLIn68o-G4ZuOnThl2HzDky29CCYSZnkDk3uE2L5rt1fyqG2II6DhKYKlXB8fittUSJk4xDr7ufu4kKOlMqXB2JyfR6kpEVigqUsjSzAWCzdP3WXgk-M8A2ElqGuwzXgXiXiOMD04SIOCSxfe1Qu6KFeQBeEgPw\",\"protected\":\"eyJub25jZSI6IkVfUVZLVjlNVklRT29odjRnbG51VDEyem1NNE5sb0tBRUZzUFl6WWJuUXMiLCJqd2siOnsibiI6Ik1qWTBPREUwTVRjMk9UUXdNRE00TnpNeE9UZ3hNRFkyT1RJME9UazVOVEV5T1RRMk16SXhNVGszTmpjMk5UVTNORE13TXpZME1UazBNelF6T0RjMU9UWTFNVFl6TmpZM09Ea3hPVEEzTURFNE1qTTNORGcyTmprM09UVTJORFV6TWpBM056YzNPRFF4TXpjNU9UWTNPVGczTWpBd05qa3pOall5T0RneU1qWTNOVEkzT0RjMk1qSTROalEzTURFME56UTBPVFkwTnpRME5Ea3dNekExTURZM01Ea3lNekkyTURZeU5ETXlNelF5TnpJMk1UVTNOekkzTnpJME5qWTVNemN3TXpFM05UZzJNRGN6TlRBMk5qVTVNamMzTlRZMU9EVTBOVFV6TlRrME5UVTJOelV6TWpVeU1EWTJNVEkwTmpZek5EYzJOakV6TkRJMk1qUTBNVEkxTWpZeU1ERTRNVFl3TWpZMk5ESXlPVFl3TXpnNE56QXhPVFEyTWprMk5qUTVNVGMzTmpZd05EZzBPRFkyTmpJeE9EUTRNakl3TVRnM05ESXhNVEF5TmpZMU56SXlOakUxTmpBNU56WXhPVFV4TWprd01qVXpPVE00TXpNeE5EWTJOelkyT0RJeE5qQTNNRFF5TVRRNE1UYzVOVEl5TURVMU1qQTBOVE00TVRnMU56SXpOemMyT0RNMk1qZzJNRFl4TWpjeE1URXdOalkwTnpFME16QXpOekk0TmpjMk5qRXdPVGs0T0Rjek5qa3hPVGMzTnpFeE9UVTBOVFk0T0RNd016Z3lNVGt4T0RRM01UQXpOamMyTVRrM016SXlPVEV5TXpreU5qQTNOekV4TnpnM056VXlOamN6TnpJNU5ESTVNelkyTlRJMk56QTVNRGN6TlRFNE1qQXdOekV3TnpneU56azROell6TnpnNU1qY3dOakF5TURJek9ETXlOakV4TURZNU5qVTNNREU0Tmpjd05qY3hOamM1TkRJek56UTBNekl3TWpnNU5UYzVOemMyT0RrM01EQXhNREEzTnpBMU9UTTBOVGN6TmpVeE5qSTROVEl4TURJNU9UUXdNVEUiLCJrdHkiOiJSU0EiLCJlIjoiTmpVMU16YyJ9LCJhbGciOiJSUzI1NiJ9\",\"payload\":\"eyJyZXNvdXJjZSI6Im5ldy1yZWciLCJjb250YWN0IjpbIm1haWx0bzpzb21lQGR1ZGUuY29tIl19\",\"header\":{\"jwk\":{\"n\":\"MjY0ODE0MTc2OTQwMDM4NzMxOTgxMDY2OTI0OTk5NTEyOTQ2MzIxMTk3Njc2NTU3NDMwMzY0MTk0MzQzODc1OTY1MTYzNjY3ODkxOTA3MDE4MjM3NDg2Njk3OTU2NDUzMjA3Nzc3ODQxMzc5OTY3OTg3MjAwNjkzNjYyODgyMjY3NTI3ODc2MjI4NjQ3MDE0NzQ0OTY0NzQ0NDkwMzA1MDY3MDkyMzI2MDYyNDMyMzQyNzI2MTU3NzI3NzI0NjY5MzcwMzE3NTg2MDczNTA2NjU5Mjc3NTY1ODU0NTUzNTk0NTU2NzUzMjUyMDY2MTI0NjYzNDc2NjEzNDI2MjQ0MTI1MjYyMDE4MTYwMjY2NDIyOTYwMzg4NzAxOTQ2Mjk2NjQ5MTc3NjYwNDg0ODY2NjIxODQ4MjIwMTg3NDIxMTAyNjY1NzIyNjE1NjA5NzYxOTUxMjkwMjUzOTM4MzMxNDY2NzY2ODIxNjA3MDQyMTQ4MTc5NTIyMDU1MjA0NTM4MTg1NzIzNzc2ODM2Mjg2MDYxMjcxMTEwNjY0NzE0MzAzNzI4Njc2NjEwOTk4ODczNjkxOTc3NzExOTU0NTY4ODMwMzgyMTkxODQ3MTAzNjc2MTk3MzIyOTEyMzkyNjA3NzExNzg3NzUyNjczNzI5NDI5MzY2NTI2NzA5MDczNTE4MjAwNzEwNzgyNzk4NzYzNzg5MjcwNjAyMDIzODMyNjExMDY5NjU3MDE4NjcwNjcxNjc5NDIzNzQ0MzIwMjg5NTc5Nzc2ODk3MDAxMDA3NzA1OTM0NTczNjUxNjI4NTIxMDI5OTQwMTE\",\"kty\":\"RSA\",\"e\":\"NjU1Mzc\"},\"alg\":\"RS256\"}}"

IO.inspect(response):

{:ok,
 %HTTPoison.Response{body: "{\n  \"type\": \"urn:acme:error:malformed\",\n  \"detail\": \"Key too large: 4934 \\u003e 4096\",\n  \"status\": 400\n}",
  headers: [{"Server", "nginx"}, {"Content-Type", "application/problem+json"},
   {"Content-Length", "104"},
   {"Boulder-Request-Id", "5BbL5f23yYxol0_rHyC7OT88PF5BJle7lY970szFsqg"},
   {"Replay-Nonce", "DFPac8kWo7OoL5LSHUI6CZr5nHjZzyeA64eXQdcniVg"},
   {"Expires", "Sat, 04 Feb 2017 05:12:57 GMT"},
   {"Cache-Control", "max-age=0, no-cache, no-store"}, {"Pragma", "no-cache"},
   {"Date", "Sat, 04 Feb 2017 05:12:57 GMT"}, {"Connection", "close"}],
  status_code: 400}}
1
I know that I am posting details about my private key. This isn't a development key that I can easily replace so I don't really care if it becomes public.eltiare
I haven't read the code completely but this line will not do anything: Map.put(request, :signature, signature). You need to do request = Map.put(request, :signature, signature).Dogbert
Haha. Oops. I'm still too used to OO languages. I'll give that a go.eltiare
Still reporting the error, sadly.eltiare
It's a long shot, but could you try replacing Integer.to_string with :binary.encode_unsigned? I doubt an ASCII string representation of an integer would be the correct thing here. Does this work or return a different size in the error message?Dogbert

1 Answers

1
votes

You're converting the integers in RSAPrivateKey to binary using String.to_integer, which creates an ASCII representation of the digits in base 10. You need convert every byte in the number into a binary with the appropriate total number of bytes.

One way to do this is to specify a size using the <<>> syntax:

iex(1)> n = 0x12345678
305419896
iex(2)> <<n::size(32)>> # or just <<n::32>>
<<18, 52, 86, 120>>