I'm using SHA256 and RSA to sign a message on my Ubuntu machine using OpenSSL. My goal is to verify this message on Android using Android's Java.
Following commands were used on ubuntu:
openssl genrsa -out private.pem 1024
openssl rsa -in private.pem -out public.pem -outform PEM -pubout
echo 'foobar' > data.txt
openssl dgst -sha256 < data.txt > hash
openssl rsautl -sign -inkey private.pem -keyform PEM -in hash > signature
openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
openssl enc -base64 -in signature -out base64_signature
I have now created keys, signed the message, created a .der file for the public key that should be able to be accessed in Java and encoded the message with Base64. I then place the .der public key on my device and successfully load the key into the class PublicKey.
This method is used to verify the message:
public static boolean verify(PublicKey publicKey,String data,String verification){
java.security.Signature sig;
try {
sig = java.security.Signature.getInstance("SHA256WithRSA");
sig.initVerify(publicKey);
try {
sig.update(verification.getBytes());
} catch (Exception e) {
...
}
if (!sig.verify(Base64.decode(data, Base64.DEFAULT))) {
return false;
}
return true;
}
catch ....
return false;
}
Parameters when calling the method:
verify(PublicKey, Base64 encoded data in a String that is to be verified, "foobar");
Obviously the verification fails, but I can't understand why. I'm guessing it has to do something with the encoding(?).
Update!
So I managed to write the results of Base64.decode(data, Base64.DEFAULT))
to a file and compare it with the original signature file using a hexeditor. Completely different!
Java produces and expects to receive signatures in slightly different form. Hash of the message must be encoded in DER, then padded with PKCS#1 and only then signed with private key. And Openssl has a command for that (because it's actually a standard procedure). Instead of
you do
Also note:
data.txt
contains a newline, don't forget it inString verification
variablesig.update(verification.getBytes())
should explicitly indicate a charset - the same charset, that was used to fill thedata.txt
file, for example:sig.update(verification.getBytes("UTF-8"))
The rest of your commands/code seems OK.
UPD - to answer @GilCol about the differences:
The padding is the same for both signed messages (PKCS#1). But the messages are different.
When you use
openssl dgst -sha256 < data.txt > hash
,hash
will contain (depending on openssl version):or
It is just plain text and it is the message you will sign using
openssl rsautl -sign ...
. We can see that withopenssl rsautl -verify ...
:If you use
openssl dgst -sha256 -binary < data.txt > hash
to get hash in binary (pure) form, and then sign it, the result will be better, but still not right:But when you use
openssl dgst -sha256 -sign ...
, the message is different - it's now a standard ASN.1 structure for message digests (hashes). Let's see:As you can see, only the last
signature
file had proper ASN.1 structure, previous two were just "some arbitrary" messages, signed with RSA private key.