Cryptographic Signatures: Ruby versus OpenSSL

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 S.

On 11.08.2006 14:07, Andy S. wrote:

$ openssl genrsa -out private.pem 1024
include PKey
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

On 8/11/06, Robert K. [email protected] wrote: 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

I think he’s doing the Ruby part right. The OpenSSL code underneath the
Ruby library-calls automatically does the hashing for you, which is
why
you have to give it a hash algorithm when generating an RSA sig. I think
something is wrong with how he’s using rsautl.

I got it. Use RSA::private_encrypt rather than RSA::sign to replicate
what
you are doing on the command line.

On 8/11/06, Robert K. [email protected] wrote:

include OpenSSL

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”))

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.

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

This Ruby code will give you the same result:
#--------------------------------------------------
require ‘base64’
require ‘openssl’
require ‘sha1’
include OpenSSL
include PKey
include Digest

pkey = RSA.new(File.read(“private.pem”))
plaintext = SHA1.new( “Some Text” ).digest
enc = pkey.private_encrypt(plaintext)
puts Base64.encode64(enc)

#--------------------------------------------------------

as this:

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

On 8/11/06, Andy S. [email protected] wrote:

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?

On 11 Aug 2006, at 18:28, Francis C. wrote:

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

On 8/11/06, Andy S. [email protected] wrote:

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.