1

I'm trying to implement RSA SHA1 signature verification. To my surprise, command line OpenSSL tool doesn't generate the same key as the Ruby OpenSSL.

If I run those commands :

MacBook-Pro-de-Geoffrey:ssl_tests Escaflowne$ cat data.txt 
000
MacBook-Pro-de-Geoffrey:ssl_tests Escaflowne$ openssl dgst -sha1 -binary -sign prvkey.pem -out sig.bin data.txt
MacBook-Pro-de-Geoffrey:ssl_tests Escaflowne$ openssl base64 -in sig.bin -out sig64.txt
MacBook-Pro-de-Geoffrey:ssl_tests Escaflowne$ cat sig64.txt
AJEh2kA7O3j624Kdl7UCGN1HiEk/v2LQudB+cjxw1CfmRTjcSPBjUE/EAwy8NEut
K4zYgfRwwTs7NY3AwYiUEtAe5yohUM0Qv17qSDW+G4IWjwe9PKE7Sl00umiMdszA
q/1hqeQlHKgjme7YO7H6i1UcAXmriOOjn+ySRaovsHw=

So final base64 result in command line is : AJEh2kA7O3j624Kdl7UCGN1HiEk/v2LQudB+cjxw1CfmRTjcSPBjUE/EAwy8NEut K4zYgfRwwTs7NY3AwYiUEtAe5yohUM0Qv17qSDW+G4IWjwe9PKE7Sl00umiMdszA q/1hqeQlHKgjme7YO7H6i1UcAXmriOOjn+ySRaovsHw=

Now, if I try signing it through my ruby script :

def sign_message(message)
    privkey = OpenSSL::PKey::RSA.new(File.read(Rails.root.join('lib', 'payment', 'prvkey.pem')))

    digest = OpenSSL::Digest.new('sha1')

    expected_sign = privkey.sign(digest, message)
    base_64_expected_sign = [expected_sign].pack('m')

    puts "Expected Signature"
    puts expected_sign
    puts "Base 64 Expected Signature"
    puts base_64_expected_sign

    return base_64_expected_sign
  end

And calling the function like this :

  def test_sign
    message = "000"
    message_signature = sign_message(message)
    puts "Message Signature : #{message_signature}"

    puts "Valid : #{verify_signature(message_signature, message)}"
  end

I get the output :

Expected Signature
??|??n?^~?T_1Y@??BR??u???k      x?*????S?L?:.7
                          t??tc?)崪? ?}DMp?p2??4?D-f??jT;!e
                                                              ?k??5??
Base 64 Expected Signature
QjhL1zQoUdGFLVCMg06/CKeE/HdhRTOhJ/p09wkWeK0qD/afsxfcU7tMtDou
Nw3rwXw/5W68XhZ+BK1UXwIxWUDbFYlCUpu6HnWTmI5rC3QP+f50Y8kp5bSq
gQkekH1ETXDmcDKvExeSNKVELWYe3uwTalQ7IWUMyWvnF541rvo=
Message Signature : QjhL1zQoUdGFLVCMg06/CKeE/HdhRTOhJ/p09wkWeK0qD/afsxfcU7tMtDou
Nw3rwXw/5W68XhZ+BK1UXwIxWUDbFYlCUpu6HnWTmI5rC3QP+f50Y8kp5bSq
gQkekH1ETXDmcDKvExeSNKVELWYe3uwTalQ7IWUMyWvnF541rvo=

So final ruby OpenSSL signature is : QjhL1zQoUdGFLVCMg06/CKeE/HdhRTOhJ/p09wkWeK0qD/afsxfcU7tMtDou Nw3rwXw/5W68XhZ+BK1UXwIxWUDbFYlCUpu6HnWTmI5rC3QP+f50Y8kp5bSq gQkekH1ETXDmcDKvExeSNKVELWYe3uwTalQ7IWUMyWvnF541rvo=

Versus command line : AJEh2kA7O3j624Kdl7UCGN1HiEk/v2LQudB+cjxw1CfmRTjcSPBjUE/EAwy8NEut K4zYgfRwwTs7NY3AwYiUEtAe5yohUM0Qv17qSDW+G4IWjwe9PKE7Sl00umiMdszA q/1hqeQlHKgjme7YO7H6i1UcAXmriOOjn+ySRaovsHw=

I've been struggling with this for some time now and I don't understand what could be making a difference!

UPDATE :

Well, apparently results match if I replace my message variable with File.read(Rails.root.join('lib', 'payment', 'data.txt')) So basically, using a string with the same value as what's in the text file doesn't give the same result. This means it's encoding related right ?

UPDATE 2 :

So the file says its encoded in us-ascii if I run file -I data.txt However, if I do message.encoding.name it says its loaded as UTF-8 Also, message.encode('ascii') does not alter the result of the generated signature, it still corresponds with the command line openssl. As soon as I switch to a string "000".encode('utf-8') or "000".encode('ascii'), the signatures don't match anymore. So encoding doesn't seem to play a role at all.

How come there's a difference between the exact same content whether it comes from reading a file or written as a string ?

2
  • I think you just need to use message = "000\n" in your Ruby. It looks like you are not taking into account the trailing newline in the file. Commented May 25, 2018 at 20:25
  • Damn, yes that was it, I thought about it but since I had checked there were no blank characters at the end, I thought it couldn't be that. I guess its added in anyways. Thanks, can you write an answer so I can accept it ? Commented May 28, 2018 at 10:07

1 Answer 1

2

The file data.txt has a trailing newline that you are not taking into account in your code. Using

message = "000\n"

should work.

You could also do

message = File.binread("data.txt")

to make sure you get the exact data as the command line.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.