I have a python code for generating rsa signatures and I am trying to write this code in elixir. python code:
import sys
import base64
import datetime
import json
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA512
def generate_signature(key_id, private_key_content):
private_key = RSA.import_key(base64.b64decode(private_key_content))
timestamp = "2024-12-19T08:48:29.528457+00:00"
message_to_sign = key_id + timestamp
print("Message to sign:", message_to_sign)
hash_obj = SHA512.new(message_to_sign.encode())
signer = pkcs1_15.new(private_key)
signature_bytes = signer.sign(hash_obj)
signature_value = base64.b64encode(signature_bytes).decode()
return json.dumps({
"keyId": key_id,
"timestamp": timestamp,
"signature": signature_value
}, indent=2)
def main():
if len(sys.argv) < 3:
print("required keyId and privateKey")
else:
key_id = sys.argv[1]
private_key_content = sys.argv[2]
print(generate_signature(key_id, private_key_content))
if __name__ == "__main__":
main()
my elixir code:
defmodule RsaExample do
require Logger
defp api_key() do
Application.get_env(:rsa_example, :api_key)
end
defp key_id() do
Application.get_env(:rsa_example, :key_id)
end
def get_signature do
dt = "2024-12-19T08:48:29.528457+00:00"
message =
:crypto.hash(:sha512, key_id() <> dt)
|> Base.encode16()
|> String.downcase()
api_key = Base.decode64!(api_key())
<<_::binary-size(26), der::binary>> = api_key
key = :public_key.der_decode(:RSAPrivateKey, der)
signature = :public_key.sign(message, :sha, key) |> Base.encode64()
{:ok,
%{
keyId: key_id(),
timestamp: dt,
signature: signature
}}
end
end
I tested with these params:
private key:
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4CuxG1G1KiXFWpLXAGWI0ATKRP+Ixto9ElVqpFsPRB14Cu0TaRA6rEMW2C8LgafUYAJdrfzypB1tlfPpRH1fpQoFNkbYnec5/DJGMrh4fWkSZEflrOcSnY0CEHVG2fO0AMT4toVmgj6svfBSMf6S2sm2rjhQjMSqj87wOG3TNaIvQSaAz7UXa+hzaI7LQ0a41iUUDI6JLYH1EbvjZQQhwjgWlqy5LSeosBXtpPyfLaQDfUaV8eAB5mCt6uV5NlCGKMpvT+h9ZvT8gRn9P9PQeCScFyNdR32LVuU7WNkiu55XI5/ZXRgV6PNN3tOEHPLxn1Wl5cZ4kbQbciAF+eo0JAgMBAAECggEAC/BtdoTWWDUFXL0Pq1gaNkxzltehmV8B+U2FFZ4L8vX648t5Qn2SxAcxBEfMCxnkk/uJ1yfobAC1raZHaNbTMacmUz1mJzZwLm7GzG0JODh3ZcS0PQAA4Wj/aPKr994v08jeA0DJ2zAmx0xR2vF24oE9uxja9pN8dCleJD4kvCR1aVMJBlUow09PnZo6fsi9wAdIjs/BHVQT0mUl7Nx+fFqhLbWgJB3+EdB2sudiQ6r4P622zNR0BAeYuoS93LTFTER/6QYRYfQ4+aVYCacJs9hI0L+2SQmEteF+TS4P2PQxvryvmcYYGRnfQLtmQmdLU54eHkaY88+gKGoN9cxbbwKBgQDP/4qExV7lNhgpHnlv9KwXSRLk3nHvShqWKBm1ofRxGvF8bhp2s/6AG88AiGOUzMMJiS6sFasx6upznzWRUU6wpQYPP8SIftYPCv2f0dm4lIxi+o9KJHbt2zXYkoEwhgaWwo2h/InzWksVV4nRh1nm9UfUWbhGPg0f/DCjcjFgAwKBgQDihBjlN5HVhXn/PTmYRdad5Gv7wsRuJS3eSn0qSW2wHyQTav/4ulx5R21Vjbos5hEfP9wSzypy8VUgtpr9hWaWrV4noPInuo/STsUVYmdC81B3/dNWCVJQOVWogK6uD8Rh7jYPcR9fGMAiliYAygGekVNmy66r9talVLjkPmzPAwKBgQDAMWNATdPivjpS/Gq/rXi6x3Xi7zyeHH42k3U2JSFmxbuv+1kOqEEZBRkgZ+aEHzR1AkKTFi6rIGNlVQ69aUDp7oKL4qNqcLDBE6nJXLHuYaza5KU0oD8Yh/7YUH95Y86AzeLrdBvQNnW8kbeyWXwT8j9eE0038qxUCsaysJi5GwKBgQC8VYzFeHCekb4fg+5RNy/8U6Gc0BG2at86RcDP2gGDQEEkjACL85dTlxnG2nIDRr7RtqzqTqlCrHlVG76+L55ehYMVe/IbKjjOaYPgBdNJjThIrBh9Hn78CM+5rFSQkLd9nSwBptKopNGLcD/kxBCYoMgxXgS7Ih7RlCGuVGDvtQKBgQDFXhexzngPKboXvKl9rz19DSZ98IIbNTwX7Svc5rfvKGHN0XMGpc93SXA4WBY8ZlX6M/NNRk2Gd9ePf64dhn2uViMMAGwvsadiCV6Z2BqpQnDGKq2VpAbuFqtnCDPIfgPKY4fbHFPjcO3n/+wFqTL6grbIP7yM76Fv0oqt+/t2Yw==
key id: 1151022712
python code output:
Message to sign: 11510227122024-12-19T08:48:29.528457+00:00
b'11510227122024-12-19T08:48:29.528457+00:00'
{
"keyId": "1151022712",
"timestamp": "2024-12-19T08:48:29.528457+00:00",
"signature": "gumnKBrIiRn4nlxOYe+2JASgngiFh71jpX8u/wfqH0bOw2S3B+X3sk+2pCYc9w3bmjZL2uMwAcb8dnN8UEZFKs03xo4s4RFjaqQpfwCKv1IFidNhilIiF7oyx2PkGBxVVAMtH0srO6wxmYKGFjOvCNSS5/edM7AwuXb4ZRe+6wQrmNMV/nwiKQ3Onqc5SnyaFjxINLo2/eiG4Ks0ngkmdT6PhPYhTW8Zy0EAZszo0ZK3W6z+uEMfQxw1veFa0TqcdJSQws9YZZIqBdw8mbZmY1CUZD7SwPT3UQZRw6eJezBtq/wcNO/afaW2MB9ejpkxNYr/+cFYglBTbNaZLzOK9g=="
}
elixir code output:
{:ok,
%{
timestamp: "2024-12-19T08:48:29.528457+00:00",
signature: "lXl157EEip2OVA0UpR72oAiEjER0eDRXr5kr4fujwSZLj0/KPfkO5+1tRKY2rJVGIQ6/FAlMDtQqTijltLBvZXGqf8YWhU0zYiAzHmPZZ5WDg4Z08vwjhMDnhtsxk7MtTbv1ZzLPolCg0LOMlZqmVDaCADMl8QB9c8CHXKOogBGUP50+ML25OCvV6Ti9ni8QFfLR91fBeBYTFf0dZNqxOM/T3wX6qUAAcDnl0624Cp/UJOwDiHOivSYZJ+qsvR4ExoSYAK2RbZZOHuOIA2GrPgAOxsDjvHlBkMAa+e2lPdlAC6h7Thqla7sojeySiPWbkVf4KADBNOGisEVrnI0FaA==",
keyId: "1151022712"
}}
I tried different libs for working with rsa in elixir, but my elixir code output always mismatch with python code output. I am new to cryptography, maybe I am making some mistake? How to make signatures match?
Both codes generate different signatures, as the Elixir code does something different to the Python code:
In the Elixir code, first the SHA-512 hash of the message is generated and the result is hex encoded (with lowercase letters). This result is then hashed with SHA-1 and signed.
In the Python code, on the other hand, the message is "only" hashed and signed with SHA-512.
So that the Elixir code does the same as the Python code, you should omit the first hashing with SHA-512. When signing, you should use the message directly and SHA-512 as digest:
defmodule RsaExample do
require Logger
defp api_key() do
#Application.get_env(:rsa_example, :api_key)
"MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4CuxG1G1KiXFWpLXAGWI0ATKRP+Ixto9ElVqpFsPRB14Cu0TaRA6rEMW2C8LgafUYAJdrfzypB1tlfPpRH1fpQoFNkbYnec5/DJGMrh4fWkSZEflrOcSnY0CEHVG2fO0AMT4toVmgj6svfBSMf6S2sm2rjhQjMSqj87wOG3TNaIvQSaAz7UXa+hzaI7LQ0a41iUUDI6JLYH1EbvjZQQhwjgWlqy5LSeosBXtpPyfLaQDfUaV8eAB5mCt6uV5NlCGKMpvT+h9ZvT8gRn9P9PQeCScFyNdR32LVuU7WNkiu55XI5/ZXRgV6PNN3tOEHPLxn1Wl5cZ4kbQbciAF+eo0JAgMBAAECggEAC/BtdoTWWDUFXL0Pq1gaNkxzltehmV8B+U2FFZ4L8vX648t5Qn2SxAcxBEfMCxnkk/uJ1yfobAC1raZHaNbTMacmUz1mJzZwLm7GzG0JODh3ZcS0PQAA4Wj/aPKr994v08jeA0DJ2zAmx0xR2vF24oE9uxja9pN8dCleJD4kvCR1aVMJBlUow09PnZo6fsi9wAdIjs/BHVQT0mUl7Nx+fFqhLbWgJB3+EdB2sudiQ6r4P622zNR0BAeYuoS93LTFTER/6QYRYfQ4+aVYCacJs9hI0L+2SQmEteF+TS4P2PQxvryvmcYYGRnfQLtmQmdLU54eHkaY88+gKGoN9cxbbwKBgQDP/4qExV7lNhgpHnlv9KwXSRLk3nHvShqWKBm1ofRxGvF8bhp2s/6AG88AiGOUzMMJiS6sFasx6upznzWRUU6wpQYPP8SIftYPCv2f0dm4lIxi+o9KJHbt2zXYkoEwhgaWwo2h/InzWksVV4nRh1nm9UfUWbhGPg0f/DCjcjFgAwKBgQDihBjlN5HVhXn/PTmYRdad5Gv7wsRuJS3eSn0qSW2wHyQTav/4ulx5R21Vjbos5hEfP9wSzypy8VUgtpr9hWaWrV4noPInuo/STsUVYmdC81B3/dNWCVJQOVWogK6uD8Rh7jYPcR9fGMAiliYAygGekVNmy66r9talVLjkPmzPAwKBgQDAMWNATdPivjpS/Gq/rXi6x3Xi7zyeHH42k3U2JSFmxbuv+1kOqEEZBRkgZ+aEHzR1AkKTFi6rIGNlVQ69aUDp7oKL4qNqcLDBE6nJXLHuYaza5KU0oD8Yh/7YUH95Y86AzeLrdBvQNnW8kbeyWXwT8j9eE0038qxUCsaysJi5GwKBgQC8VYzFeHCekb4fg+5RNy/8U6Gc0BG2at86RcDP2gGDQEEkjACL85dTlxnG2nIDRr7RtqzqTqlCrHlVG76+L55ehYMVe/IbKjjOaYPgBdNJjThIrBh9Hn78CM+5rFSQkLd9nSwBptKopNGLcD/kxBCYoMgxXgS7Ih7RlCGuVGDvtQKBgQDFXhexzngPKboXvKl9rz19DSZ98IIbNTwX7Svc5rfvKGHN0XMGpc93SXA4WBY8ZlX6M/NNRk2Gd9ePf64dhn2uViMMAGwvsadiCV6Z2BqpQnDGKq2VpAbuFqtnCDPIfgPKY4fbHFPjcO3n/+wFqTL6grbIP7yM76Fv0oqt+/t2Yw=="
end
defp key_id() do
#Application.get_env(:rsa_example, :key_id)
"1151022712"
end
def get_signature do
dt = "2024-12-19T08:48:29.528457+00:00"
#message =
# :crypto.hash(:sha512, key_id() <> dt)
# |> Base.encode16()
# |> String.downcase()
message = key_id() <> dt # fix1: apply the message directly
api_key = Base.decode64!(api_key())
<<_::binary-size(26), der::binary>> = api_key
key = :public_key.der_decode(:RSAPrivateKey, der)
signature = :public_key.sign(message, :sha512, key) |> Base.encode64() # fix2: apply SHA-512
{:ok,
%{
keyId: key_id(),
timestamp: dt,
signature: signature
}}
end
end
IO.inspect RsaExample.get_signature() #... signature: "gumn...OK9g==",...