Cryptographic Signatures: Ruby versus OpenSSL

A

Andy Stewart

Hello Everyone,

I am having problems matching up a signature created with openssl on
the command line with a signature created in Ruby.

The signature I am trying to get is an RSA encryption with my 1024
bit private key of a SHA-1 hash of data.

This is what I did on the command line:

$ openssl genrsa -out private.pem 1024
$ echo -n "Some text to sign" \
| openssl dgst -sha1 -binary \
| openssl rsautl -sign -inkey private.pem \
| openssl enc -base64

This is what I did in Ruby:

require 'base64'
require 'openssl'
include OpenSSL
include PKey
include Digest

private_key = RSA.new(File.open("/Users/andy/tmp/private.pem").read)
signature = private_key.sign(OpenSSL::Digest::SHA1.new, "Some text
to sign")
puts Base64.encode64(signature)

Unfortunately the base64-encoded signatures produced by each method
don't match.

I have spent quite a lot of time with Google and the RubyPKI source
-- but I am new to Ruby and have exhausted all the avenues I can
think of.

Any help would be much appreciated.

Thanks and regards,
Andy Stewart
 
R

Robert Klemme

Hello Everyone,

I am having problems matching up a signature created with openssl on the
command line with a signature created in Ruby.

The signature I am trying to get is an RSA encryption with my 1024 bit
private key of a SHA-1 hash of data.

This is what I did on the command line:

$ openssl genrsa -out private.pem 1024
$ echo -n "Some text to sign" \
| openssl dgst -sha1 -binary \
| openssl rsautl -sign -inkey private.pem \
| openssl enc -base64

This is what I did in Ruby:

require 'base64'
require 'openssl'
include OpenSSL
include PKey
include Digest

private_key = RSA.new(File.open("/Users/andy/tmp/private.pem").read)
signature = private_key.sign(OpenSSL::Digest::SHA1.new, "Some text to
sign")
puts Base64.encode64(signature)

Unfortunately the base64-encoded signatures produced by each method
don't match.

I have spent quite a lot of time with Google and the RubyPKI source --
but I am new to Ruby and have exhausted all the avenues I can think of.

Could it be that in your Ruby script you're missing out the equivalent
step of "| openssl dgst -sha1 -binary \"? I'd rather have expected
something like

signature = private_key.sign(OpenSSL::Digest::SHA1.new("Some text to
sign"))

For testing purposes I'd use the same *file* (i.e. in the file system).
And you should open that in binary mode from within Ruby to be sure
both tools see the same.

I'd probably work through this step by step, i.e. compare the output of
every step from the first step on. That way you can easily detect where
it goes wrong. HTH

Kind regards

robert
 
J

Jan Svitok

Could it be that in your Ruby script you're missing out the equivalent
step of "| openssl dgst -sha1 -binary \"? I'd rather have expected
something like

signature = private_key.sign(OpenSSL::Digest::SHA1.new("Some text to
sign"))

It seems it's not neccessary, sign will do that.
For testing purposes I'd use the same *file* (i.e. in the file system).
And you should open that in binary mode from within Ruby to be sure
both tools see the same.

I'd probably work through this step by step, i.e. compare the output of
every step from the first step on. That way you can easily detect where
it goes wrong. HTH

From what I've seen they both should produce PKCS#1-padded signature,
so in this case, only possible difference is that one produces block
type 00, and the other 01, but that's not very probable (=they should
use deterministic type of padding)

I'd try:
- the official test vectors, to see which one is right
- using cmdline tools and/or Bignumber ** and % to decrypt the
signature to see the padding
- crossverification of the respective signatures (i.e. whether cmdline
verifies ruby-generated signature and vice versa)
- stepping through RSA#sign, as it is written in ruby.

Disclaimer: I don't know what version do you have, nor what version
I've looked at, so I may be wrong.
 
A

Andy Stewart

Francis,
This Ruby code will give you the same result:
#--------------------------------------------------
[snip]
#--------------------------------------------------------

as this:

echo -n "Some Text" | openssl dgst -sha1 -binary | openssl rsautl - sign
-inkey private.pem | openssl enc -base64

That's perfect, thank you.

I'll now go and dig through RSA::private_encrypt and RSA::sign and
see how they differ.

Thank you also to Jan and Robert for your helpful comments.

Kind regards,
Andy
 
F

Francis Cianfrocca

That's perfect, thank you.

I'll now go and dig through RSA::private_encrypt and RSA::sign and
see how they differ.

Thank you also to Jan and Robert for your helpful comments.

Kind regards,
Andy

RSA#sign is basically a wrapper over EVP_SignInit, EVP_SignUpdate and
EVP_SignFinalize, which are themselves a high-level wrapper over the
actual crypto operations. The EVP_xxx calls do the hashing for you, so
since you're using RSA, you have to specify a digest algorithm.

rsautl -sign is a very dumb piece of code that only encrypts your
plaintext with a private key. You notice in your shell pipeline, you
did the sha digest yourself. That's what I did in the Ruby code I sent
you.

I assume you tested the Ruby output with rsautl -verify and it worked?
 
A

Andy Stewart

RSA#sign is basically a wrapper over EVP_SignInit, EVP_SignUpdate and
EVP_SignFinalize, which are themselves a high-level wrapper over the
actual crypto operations. The EVP_xxx calls do the hashing for you, so
since you're using RSA, you have to specify a digest algorithm.

So what's the difference between:

- a signature specifying RSA on top of SHA-1
- RSA encryption using a private key of SHA-1 hashed data?

Conceptually they are the same, I believe, so why do
RSA::private_encrypt and RSA::sign behave differently?
I assume you tested the Ruby output with rsautl -verify and it worked?

Yes indeed. I did:

$ ruby sig.rb | openssl enc -base64 -d | openssl rsautl -verify -
inkey public.pem -pubin | xxd

And that wrote out the SHA-1 digest of my original data.

By the way, the pipeline came from Allan Odgaard here: http://
macromates.com/sigpipe/archives/2004/09/05/using-openssl-for-license-
keys/

Thanks and regards,
Andy
 
F

Francis Cianfrocca

So what's the difference between:

- a signature specifying RSA on top of SHA-1
- RSA encryption using a private key of SHA-1 hashed data?

Conceptually they are the same, I believe, so why do
RSA::private_encrypt and RSA::sign behave differently?

The protocol for digital signatures (oversimplifying) is to encrypt
(sign) with a private key and decrypt (verify) with a public key.
That's what RSA::private_encrypt is for. (If you're using encryption
in order to hide data instead of verify it, you encrypt with a public
key.)

But asymmetric encryption is exceptionally costly, so signatures are
always done by encrypting a "digest" (a cryptographically-strong hash)
of the plaintext rather than the whole text- that's where SHA-1 comes
in. Now you only have to encrypt 20 bytes with your private key.

RSA::sign does the hashing as well as the encrypting for you. It's a
convenience function. Several layers below, a private-key encryption
gets done just as with RSA::private_encrypt.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,707
Messages
2,569,343
Members
44,635
Latest member
Matt231

Latest Threads

Top