johninvictus

johninvictus

Hi Guys
Help me rewrite this Javascript function to Elixir.

security: function security() {
        //sandbox value = Security Credential (Shortcode 1)
        //production value = api initiator password
        var bufferToEncrypt = new Buffer("Safaricom133!");
        //read the sandbox/production certificate data
        // PATH - e.g "./keys/sandbox-cert.cer"
        var data = fs.readFileSync("PATH TO CERTIFICATE FILE");
        //convert data to string
        var privateKey = String(data);
        //encrypt the credential using the privatekey
        var encrypted = crypto.publicEncrypt({
            key: privateKey,
            padding: constants.RSA_PKCS1_PADDING
        }, bufferToEncrypt);
        //convert encrypted value to string and encode to base64
        var securityCredential = encrypted.toString("base64");
        //return value to invoking method
        return securityCredential;
}

The certificate file looks something like this.

-----BEGIN CERTIFICATE-----
MIIGgDCCBWigAwIBAgIKMvrulAAAAARG5DANBgkqhkiG9w0BAQsFADBbMRMwEQYK
CZImiZPyLGQBGRYDbmV0MRkwFwYKCZImiZPyLGQBGRYJc2FmYXJpY29tMSkwJwYD
VQQDEyBTYWZhcmljb20gSW50ZXJuYWwgSXNzdWluZyBDQSAwMjAeFw0xNDExMTIw
NzEyNDVaFw0xNjExMTEwNzEyNDVaMHsxCzAJBgNVBAYTAktFMRAwDgYDVQQIEwdO
-----END CERTIFICATE-----

I have tried writing it but the task has proven to be tough.

Please help

Showing Posts 1 to 8

cmkarlsson

cmkarlsson

Assuming this is what you want. RSA with RSA_PKCS1_PADDING has known flaws which is exploitable though the Bleichenbacker’s CCA attack. The improved version claims to crack RSA with as little as 15,000 attempts (assuming it has access to an oracle of some sort).

Anyway what you are trying to do is:

  1. Read a PEM encoded certificate with a public key
  2. Encrypt a buffer of data (max 245 for PKCS1 padding)
  3. Base64 encode it.

You can use the public_key module to achieve this. From memory you do something like this:

def security(plaintext, certificate) do

    #1) Decode the certificate. This assumes only one entry in the cert.
    [pem_entry]  = :public_key.pem_decode(certificate)
    plk = :public_key.pem_entry_decode(pem_entry)

    #2) Encrypt the plaintext
    ciphertext = :public_key.encrypt_public(plaintext, plk, [{:rsa_pad, :rsa_pkcs1_padding}])

     #3) Base64 encode and return the result
     :base64.encode(ciphertext)
end

end

johninvictus

johninvictus OP

@cmkarlsson
Thank you for the answer.

The issue here is that the above certificate is not a pem file but cer file. This is what I get after I pem decode it.

[
  {:Certificate,

   <<48, 130, 6, 128, 48, 130, 5, 104, 160, 3, 2, 1, 2, 2, 10, 50, 250, 238,
     148, 0, 0, 0, 4, 70, 228, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1,
     11, 5, 0, 48, 91, 49, 19, 48, 17, 6, ...>>, :not_encrypted}
]

Here is my full implementation.

def security do
    cert_text = File.read!("./lib/mpesa_elixir/keys/sandbox_cert.cer")
    plain_text = " Safaricom133!"
    [pem_entry] = :public_key.pem_decode(cert_text)
    plk = :public_key.pem_entry_decode(pem_entry)

    ciphertext = :public_key.encrypt_public(plain_text, plk, [{:rsa_pad, :rsa_pkcs1_padding}])

    :base64.encode(ciphertext)
  end

Here is the error am getting.

It is not my choice am trying to write an Elixir API wrapper for MPesa (Safaricom Mobile Money Transfer Service)(M-Pesa - Wikipedia).

cmkarlsson

cmkarlsson

Ok, this means that pem_entry_decode returns a certificate and not a public_key directly. You then need to extract the public_key from the certificate data structure. I have not got anything available to test this right now and can’t remember from heart.

The public key user guide might help you:

It is in erlang and they return erlang records, so you may want to read up on elixir Records as well to handle them in the proper way.

johninvictus

johninvictus OP

@cmkarlsson
Sorry am just 1 yr old at this elixir erlang game :smile:

Here is :term am getting from pem_entry_decode

{:Certificate,
 {:TBSCertificate, :v3, 240747201527587228894948,
  {:AlgorithmIdentifier, {1, 2, 840, 113549, 1, 1, 11}, <<5, 0>>},
  {:rdnSequence,
   [
     [
       {:AttributeTypeAndValue, {0, 9, 2342, 19200300, 100, 1, 25},
        <<22, 3, 110, 101, 116>>}
     ],
     [
       {:AttributeTypeAndValue, {0, 9, 2342, 19200300, 100, 1, 25},
        <<22, 9, 115, 97, 102, 97, 114, 105, 99, 111, 109>>}
     ],
     [

       {:AttributeTypeAndValue, {2, 5, 4, 3},
        <<19, 32, 83, 97, 102, 97, 114, 105, 99, 111, 109, 32, 73, 110, 116,
          101, 114, 110, 97, 108, 32, 73, 115, 115, 117, 105, 110, 103, 32, 67,
          65, 32, 48, 50>>}
     ]
   ]}, {:Validity, {:utcTime, '141112071245Z'}, {:utcTime, '161111071245Z'}},
  {:rdnSequence,
   [
     [{:AttributeTypeAndValue, {2, 5, 4, 6}, <<19, 2, 75, 69>>}],
     [
       {:AttributeTypeAndValue, {2, 5, 4, 8},
        <<19, 7, 78, 97, 105, 114, 111, 98, 105>>}
     ],
     [
       {:AttributeTypeAndValue, {2, 5, 4, 7},
        <<19, 7, 78, 97, 105, 114, 111, 98, 105>>}
     ],
     [
       {:AttributeTypeAndValue, {2, 5, 4, 10},
        <<19, 7, 78, 97, 105, 114, 111, 98, 105>>}
     ],
     [
       {:AttributeTypeAndValue, {2, 5, 4, 11},
        <<19, 10, 84, 101, 99, 104, 110, 111, 108, 111, 103, 121>>}
     ],
     [
       {:AttributeTypeAndValue, {2, 5, 4, 3},
        <<19, 24, 97, 112, 105, 99, 114, 121, 112, 116, 46, 115, 97, 102, 97,
          114, 105, 99, 111, 109, 46, 99, 111, 46, 107, 101>>}
     ]
   ]},
  {:SubjectPublicKeyInfo,
   {:AlgorithmIdentifier, {1, 2, 840, 113549, 1, 1, 1}, <<5, 0>>},
   <<48, 130, 1, 10, 2, 130, 1, 1, 0, 168, 183, 5, 117, 87, 21, 236, 119, 68,
     58, 139, 108, 52, 186, 12, 62, 16, 251, 224, 37, 245, 122, 60, 220, 129,
     243, 110, 136, ...>>}, :asn1_NOVALUE, :asn1_NOVALUE,
  [
    {:Extension, {2, 5, 29, 14}, false,
     <<4, 20, 193, 71, 196, 248, 216, 6, 157, 213, 131, 55, 112, 242, 86, 159,
       130, 2, 33, 117, 102, 72>>},
    {:Extension, {2, 5, 29, 35}, false,
     <<48, 22, 128, 20, 235, 50, 212, 79, 126, 96, 154, 58, 152, 32, 99, 186,
       13, 94, 190, 232, 121, 78, 213, 36>>},
    {:Extension, {2, 5, 29, 31}, false,
     <<48, 130, 1, 46, 48, 130, 1, 42, 160, 130, 1, 38, 160, 130, 1, 34, 134,
       129, 214, 108, 100, 97, 112, 58, 47, 47, 47, 67, 78, 61, ...>>},
    {:Extension, {1, 3, 6, 1, 5, 5, 7, 1, 1}, false,
     <<48, 129, 249, 48, 129, 201, 6, 8, 43, 6, 1, 5, 5, 7, 48, 2, 134, 129,
       188, 108, 100, 97, 112, 58, 47, 47, 47, 67, 78, ...>>},
    {:Extension, {2, 5, 29, 15}, false, <<3, 2, 5, 160>>},
    {:Extension, {1, 3, 6, 1, 4, 1, 311, 21, 7}, false,
     <<48, 46, 6, 38, 43, 6, 1, 4, 1, 130, 55, 21, 8, 135, 207, 140, 86, 132,
       194, 196, 3, 133, 233, 133, 54, 132, 222, ...>>},
    {:Extension, {2, 5, 29, 37}, false,
     <<48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1>>},
    {:Extension, {1, 3, 6, 1, 4, 1, 311, 21, 10}, false,
     <<48, 24, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 10, 6, 8, 43, 6, 1, 5,
       5, 7, 3, ...>>}
  ]}, {:AlgorithmIdentifier, {1, 2, 840, 113549, 1, 1, 11}, <<5, 0>>},
 <<76, 20, 169, 103, 113, 128, 200, 211, 172, 226, 71, 70, 116, 254, 183, 169,
   180, 130, 81, 8, 202, 62, 174, 170, 63, 113, 75, 143, 40, 201, 137, 9, 141,
   236, 95, 17, 71, 179, 152, 165, 53, 122, 60, 97, 171, 224, ...>>}

I can`t see any public key attribute anywhere.

cmkarlsson

cmkarlsson

It is hidden in the SubjectPublicKeyInfo, there should be a subjectPublicKey in there somewhere

 {:SubjectPublicKeyInfo,
   {:AlgorithmIdentifier, {1, 2, 840, 113549, 1, 1, 1}, <<5, 0>>},
   <<48, 130, 1, 10, 2, 130, 1, 1, 0, 168, 183, 5, 117, 87, 21, 236, 119, 68,
     58, 139, 108, 52, 186, 12, 62, 16, 251, 224, 37, 245, 122, 60, 220, 129,
     243, 110, 136, ...>>}, :asn1_NOVALUE, :asn1_NOVALUE,

The <<48, 130, 1, 10 ...>> is the DER encoded public key I think. So then public_key:der_decode(:"RSAPublicKey", DerValue)

johninvictus

johninvictus OP

@cmkarlsson
Thanks alot, your solution worked wonderfully :smile:.

To extract the public key from the certificate record was hard but I have managed to do it using trial and error hacks. (If you know a better way please advice) .

Here is my final working solution incase someone else needs help.

  def security do
    cert_text = File.read!("./lib/mpesa_elixir/keys/sandbox_cert.cer") |> String.trim()
    [pem_entry] = :public_key.pem_decode(cert_text)
    plk = :public_key.pem_entry_decode(pem_entry)
    list = Tuple.to_list(elem(plk, 1))
    der_value = List.keyfind(list, :SubjectPublicKeyInfo, 0) |> elem(2)

    plain_text = "Safaricom133!"
    
    public_key = :public_key.der_decode(:RSAPublicKey, der_value)

    ciphertext =
      :public_key.encrypt_public(plain_text, public_key, [{:rsa_pad, :rsa_pkcs1_padding}])

    :base64.encode(ciphertext)
  end

Regards.

cmkarlsson

cmkarlsson

Great! I’d say the proper way to do it is to use the erlang records and access the information that way.

I’d define the Records in a separate module and then require them from the module where you want to use them.

Something like:

defmodule MyPublicKey.Records do

    require Record
    import Record, only: [defrecord: 2, extract: 2]

    @public_key "public_key/include/public_key.hrl"

    defrecord :"Certificate", extract(:Certificate, from_lib: @public_key)
    defrecord :"TBSCertificate", extract(:TBSCertificate, from_lib: @public_key)
    defrecord :"SubjectPublicKeyInfo", extract(:SubjectPublicKeyInfo, from_lib: @public_key)

end

defmodule MyPublicKey do


  require MyPublicKey.Records

  def extract_public_from_cert(certfile) do

    cert_text = File.read!(certfile) |> String.trim()
    [pem_entry] = :public_key.pem_decode(cert_text)
    cert_decoded = :public_key.pem_entry_decode(pem_entry)
    plk_der = cert_decoded
              |> MyPublicKey.Records."Certificate"(:tbsCertificate)
              |> MyPublicKey.Records."TBSCertificate"(:subjectPublicKeyInfo)
              |> MyPublicKey.Records."SubjectPublicKeyInfo"(:subjectPublicKey)

    :public_key.der_decode(:RSAPublicKey, plk_der)

  end
end
johninvictus

johninvictus OP

@cmkarlsson
Thank you very much, everything works better now. This was a great learning experience :+1:

Regards.

— All posts loaded —

Where Next? Top

Trending in Questions Top

water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New

Other Trending Topics Top

budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews