In an attempt to implement RSA signatures on a 32-bit MCU it was necessary to investigate and perform the hashing separate from the RSA signing. In doing so I have developed a method to test and verify RSA signatures.

This example uses RSA512 along with SHA1 for simplicity but can be altered to support better hashing or higher bit RSA keys. As SHA1 has a known high collision rate it is not recommended to be used in situations where security is important.

For the example first we will perform RSA signature and verification using the combined one-in-all signature function of OpenSSL.

Some useful information and links while working on this project.
Here is a great online site to test and see the inner workings of encryption.

RSA Decoder (can parse out private/public keys)
https://www.dcode.fr/rsa-cipher

Modular Exponentiation Calculator a^b mod n
https://www.dcode.fr/modular-exponentiation

The above websites require RSA keys to be in integer (not hex) format, this can be converted with the website below.
Decimal to Hexadecimal converter
https://www.rapidtables.com/convert/number/decimal-to-hex.html
Hexadecimal to Decimal converter
https://www.rapidtables.com/convert/number/hex-to-decimal.html

There will be a few files created during running this script, namely the following:
+ rsa512.pem (private key)
+ rsa512.pub (public key)
+ rsa512.sign (signature)
These above files can be safely deleted after use.

One note about the “echo” command, I had a hard time figuring this out, “echo -n” is used so it does not append a CRLF or any other characters on the end of the string. If the -n option is not specified it is possible there will be some non-printable characters on the end of the string resulting in different input than expected.

The command “xxd” is used to convert to/from printable hex and binary output. The below numbers in hex are not case sensitive but that can be adjusted with options to the xxd command if desired.

On to the actual commands, there are also some really useful HEX to binary to BASE64 functions in the commands, feel free to copy/paste/reuse anything here.

Many of the below commands are fairly long (and they auto wrap), it might be a good idea to copy/paste into a text editor first (to easily see the newlines, etc) and then copy/paste into a terminal from there.

================================================================
TEST WITH RSA512
================================================================
### BEGIN SENDER SIDE

#First make an RSA key
openssl genrsa -out rsa512.pem 512

#Use this exact private key (if desired)
echo "-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAMiKnr4qoL7FfZKLXPC02gPwrN9MAOYveAQTbId+RBR5hTf7gvyK
lgLLGCNns2b9gfQ7rvq5FIwl/R4sXIk2fpsCAwEAAQJBAIGENV2v/jz3I1B/vTig
NKWSzLMyTOpdAe/v84nWXHWzl7IbzngQEjDWkelhiEWo9fZG9YY/KOGdr0+TS2KM
7wECIQD7so3aQ7+63tcWVaUBOiY8TW2QFzpiSydRHi82V29YQQIhAMv4NCkXceVt
29JJ5ic26O90uEBQ+hlMzDMGyJJWvj/bAiEAt5be51TBdhHy+2SPDd0XZKbpgs+e
k3HUBNQqhc2y9sECIQDLbMYI3XsYiNKeDlnebmMuvsgsTRbCONfHZFKRsLWZ1QIg
Xk5fORLfkf+AEpUV7+rSL3biVwMTM9ofOYJBTeYvzbs=
-----END RSA PRIVATE KEY-----" > rsa512.pem

#Display the private key (if desired)
cat rsa512.pem
-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAMiKnr4qoL7FfZKLXPC02gPwrN9MAOYveAQTbId+RBR5hTf7gvyK
lgLLGCNns2b9gfQ7rvq5FIwl/R4sXIk2fpsCAwEAAQJBAIGENV2v/jz3I1B/vTig
NKWSzLMyTOpdAe/v84nWXHWzl7IbzngQEjDWkelhiEWo9fZG9YY/KOGdr0+TS2KM
7wECIQD7so3aQ7+63tcWVaUBOiY8TW2QFzpiSydRHi82V29YQQIhAMv4NCkXceVt
29JJ5ic26O90uEBQ+hlMzDMGyJJWvj/bAiEAt5be51TBdhHy+2SPDd0XZKbpgs+e
k3HUBNQqhc2y9sECIQDLbMYI3XsYiNKeDlnebmMuvsgsTRbCONfHZFKRsLWZ1QIg
Xk5fORLfkf+AEpUV7+rSL3biVwMTM9ofOYJBTeYvzbs=
-----END RSA PRIVATE KEY-----

#Generate the signature
echo -n "A test string of data" | openssl dgst -sha1 -binary -sign rsa512.pem | xxd -p -c256 -
962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9

#Convert to BASE64 for easy transmission as plain text
echo -n "962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9" | xxd -r -p - | base64
lizIVEYnwBgqlVTFrGZcQew22vHMe7kEfojeCW7GCgI5glR0SwaVNC4KJ8gA2YsjLQDrZyODF4N0BZxG+F0kuQ==

#Generate the public key (from the private key)
openssl rsa -in rsa512.pem -pubout > rsa512.pub

#Display the public key (if desired)
cat rsa512.pub
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMiKnr4qoL7FfZKLXPC02gPwrN9MAOYv
eAQTbId+RBR5hTf7gvyKlgLLGCNns2b9gfQ7rvq5FIwl/R4sXIk2fpsCAwEAAQ==
-----END PUBLIC KEY-----

### END SENDER SIDE

###
### PASS YOUR ORIGINAL MESSAGE, BASE64 SIGNATURE, AND PUBLIC KEY (BELOW) TO SOMEONE/SERVER FOR VERIFICATION
### message    = "A test string of data"
### signature  = "lizIVEYnwBgqlVTFrGZcQew22vHMe7kEfojeCW7GCgI5glR0SwaVNC4KJ8gA2YsjLQDrZyODF4N0BZxG+F0kuQ=="
### public_key = -----BEGIN PUBLIC KEY-----
###              MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMiKnr4qoL7FfZKLXPC02gPwrN9MAOYv
###              eAQTbId+RBR5hTf7gvyKlgLLGCNns2b9gfQ7rvq5FIwl/R4sXIk2fpsCAwEAAQ==
###              -----END PUBLIC KEY-----
###

### BEGIN RECEIVER SIDE

#Reverse the BASE64 to recover the original hex
echo -n "lizIVEYnwBgqlVTFrGZcQew22vHMe7kEfojeCW7GCgI5glR0SwaVNC4KJ8gA2YsjLQDrZyODF4N0BZxG+F0kuQ==" | base64 --decode | xxd -p -c256 -
962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9

#Create a file with the signature (binary format) (OpenSSL requires file input, can't use stdin)
echo -n '962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9' | xxd -r -p - > rsa512.sign

#Verify the signature (with the signature above)
echo -n "A test string of data" | openssl dgst -sha1 -verify rsa512.pub -signature rsa512.sign
Verified OK

#Signature has been verified
### END RECEIVER SIDE
================================================================

The above process is done using the built in signing all-in-one functions in OpenSSL.
Next let’s perform the SHA1 hash seperatly then perform a raw RSA signing (encryption).

Something to note is the padding. When we hash our original string we end up with 160 bits but we need 512 bits to use the RSA encryption (signing) so the hash needs to be padded up to 512 bytes.

There is a predetermined method of padding for PKCS#1 v1.5 outlined in the RFC 8017 standard (https://www.rfc-editor.org/rfc/rfc8017#section-9.2). This is the padding method we follow. This padding method can be implemented as the following.

padded_hash = 00 | 01 | PS | 00 | HID | hash
* where PS is filled with 0xFF bytes until padded_hash has a total length to match the RSA key size (in this case 512 bits or 64 bytes)
* where HID (DigestInfo) is the following bytes for SHA1
30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14
* where hash is the SHA1 hash (160 bits or 20 bytes)
* padded_hash should be the same length as the RSA key

This above padded hash will then be signed (encrypted) using the private key.

================================================================
TEST WITH RSA512 (seperate hash creation)
================================================================
### BEGIN SENDER SIDE

#First make an RSA key
openssl genrsa -out rsa512.pem 512

#Use this exact private key (if desired)
echo "-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAMiKnr4qoL7FfZKLXPC02gPwrN9MAOYveAQTbId+RBR5hTf7gvyK
lgLLGCNns2b9gfQ7rvq5FIwl/R4sXIk2fpsCAwEAAQJBAIGENV2v/jz3I1B/vTig
NKWSzLMyTOpdAe/v84nWXHWzl7IbzngQEjDWkelhiEWo9fZG9YY/KOGdr0+TS2KM
7wECIQD7so3aQ7+63tcWVaUBOiY8TW2QFzpiSydRHi82V29YQQIhAMv4NCkXceVt
29JJ5ic26O90uEBQ+hlMzDMGyJJWvj/bAiEAt5be51TBdhHy+2SPDd0XZKbpgs+e
k3HUBNQqhc2y9sECIQDLbMYI3XsYiNKeDlnebmMuvsgsTRbCONfHZFKRsLWZ1QIg
Xk5fORLfkf+AEpUV7+rSL3biVwMTM9ofOYJBTeYvzbs=
-----END RSA PRIVATE KEY-----" > rsa512.pem

#Display the private key (if desired)
cat rsa512.pem
-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAMiKnr4qoL7FfZKLXPC02gPwrN9MAOYveAQTbId+RBR5hTf7gvyK
lgLLGCNns2b9gfQ7rvq5FIwl/R4sXIk2fpsCAwEAAQJBAIGENV2v/jz3I1B/vTig
NKWSzLMyTOpdAe/v84nWXHWzl7IbzngQEjDWkelhiEWo9fZG9YY/KOGdr0+TS2KM
7wECIQD7so3aQ7+63tcWVaUBOiY8TW2QFzpiSydRHi82V29YQQIhAMv4NCkXceVt
29JJ5ic26O90uEBQ+hlMzDMGyJJWvj/bAiEAt5be51TBdhHy+2SPDd0XZKbpgs+e
k3HUBNQqhc2y9sECIQDLbMYI3XsYiNKeDlnebmMuvsgsTRbCONfHZFKRsLWZ1QIg
Xk5fORLfkf+AEpUV7+rSL3biVwMTM9ofOYJBTeYvzbs=
-----END RSA PRIVATE KEY-----

#Hash the message string with SHA1
echo -n "A test string of data" | openssl sha1
8fd2cc6627980b91cb661bb69c6e48375ed892c2

#Add the PKCS#1 v1.5 padding prefix.
#The prefix as defined by RFC 8017.
#https://www.rfc-editor.org/rfc/rfc8017#section-9.2
#prefix            = 0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A05000414
#hash (from above) = 8fd2cc6627980b91cb661bb69c6e48375ed892c2
#padded hash       = 0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A050004148fd2cc6627980b91cb661bb69c6e48375ed892c2

#Padded hash (of message) is the following (this is what we sign)
0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A050004148fd2cc6627980b91cb661bb69c6e48375ed892c2

#Create the signature
#this will create the signature (sign and decrypt are the same thing) 
echo -n '0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A050004148fd2cc6627980b91cb661bb69c6e48375ed892c2' | xxd -r -p - | openssl rsautl -raw -sign -inkey rsa512.pem | xxd -p -c256 -
echo -n '0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A050004148fd2cc6627980b91cb661bb69c6e48375ed892c2' | xxd -r -p - | openssl rsautl -raw -decrypt -inkey rsa512.pem | xxd -p -c256 -
962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9

#This signature equals the above (without seperate hash creation)
962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9

#Convert to BASE64 for easy transmission as plain text
echo -n "962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9" | xxd -r -p - | base64
lizIVEYnwBgqlVTFrGZcQew22vHMe7kEfojeCW7GCgI5glR0SwaVNC4KJ8gA2YsjLQDrZyODF4N0BZxG+F0kuQ==

#Generate the public key (from the private key)
openssl rsa -in rsa512.pem -pubout > rsa512.pub

#Display the public key (if desired)
cat rsa512.pub
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMiKnr4qoL7FfZKLXPC02gPwrN9MAOYv
eAQTbId+RBR5hTf7gvyKlgLLGCNns2b9gfQ7rvq5FIwl/R4sXIk2fpsCAwEAAQ==
-----END PUBLIC KEY-----

### END SENDER SIDE

###
### PASS YOUR ORIGINAL MESSAGE, BASE64 SIGNATURE, AND PUBLIC KEY (BELOW) TO SOMEONE/SERVER FOR VERIFICATION
### message    = "A test string of data"
### signature  = "lizIVEYnwBgqlVTFrGZcQew22vHMe7kEfojeCW7GCgI5glR0SwaVNC4KJ8gA2YsjLQDrZyODF4N0BZxG+F0kuQ=="
### public_key = -----BEGIN PUBLIC KEY-----
###              MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMiKnr4qoL7FfZKLXPC02gPwrN9MAOYv
###              eAQTbId+RBR5hTf7gvyKlgLLGCNns2b9gfQ7rvq5FIwl/R4sXIk2fpsCAwEAAQ==
###              -----END PUBLIC KEY-----
###

### BEGIN RECEIVER SIDE

#Reverse the BASE64 to recover the original hex
echo -n "lizIVEYnwBgqlVTFrGZcQew22vHMe7kEfojeCW7GCgI5glR0SwaVNC4KJ8gA2YsjLQDrZyODF4N0BZxG+F0kuQ==" | base64 --decode | xxd -p -c256 -
962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9

#Hash the message string with SHA1 (on the receiver end)
echo -n "A test string of data" | openssl sha1
8fd2cc6627980b91cb661bb69c6e48375ed892c2

#Add the PKCS#1 v1.5 padding prefix.
#The prefix as defined by RFC 8017.
#https://www.rfc-editor.org/rfc/rfc8017#section-9.2
#prefix            = 0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A05000414
#hash (from above) = 8fd2cc6627980b91cb661bb69c6e48375ed892c2
#padded hash       = 0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A050004148fd2cc6627980b91cb661bb69c6e48375ed892c2

#Computed padded hash (of message) is the following
0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A050004148fd2cc6627980b91cb661bb69c6e48375ed892c2

#Encrypt using the public key (reverse the above signing)
#This should recover the original padded hash from the sender
echo -n '962cc8544627c0182a9554c5ac665c41ec36daf1cc7bb9047e88de096ec60a02398254744b0695342e0a27c800d98b232d00eb672383178374059c46f85d24b9' | xxd -r -p - | openssl rsautl -raw -encrypt -inkey rsa512.pem | xxd -p -c256 -
0001ffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a050004148fd2cc6627980b91cb661bb69c6e48375ed892c2

#Chedk the computed padded hash and the sender's padded hash match
#Computed_padded_hash = 0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003021300906052B0E03021A050004148fd2cc6627980b91cb661bb69c6e48375ed892c2
#Sender_padded_hash   = 0001ffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a050004148fd2cc6627980b91cb661bb69c6e48375ed892c2

#The two above strings match exactly
#Signature has been verified
### END RECEIVER SIDE
================================================================

Leave a Reply

Your email address will not be published. Required fields are marked *