Creating p7s/pkcs7 files with Ruby


Y

Yves Vogl

Hi,

after fiddling around a lot these days I'm giving up and asking you =20
due to the lack of documentation of Ruby OpenSSL and too less =20
knowledge to figure it out on my own.

I want to sign an arbitrary document with a private key / certificate =20=

pair to get either a detached signature (p7s file) or a self =20
containing one (pkcs7 file).
Let me illustrate my steps and problems by showing what I tried:

1. For testing purposes I created a private key and a certificate with =20=

OpenSSL

openssl genrsa -out app_key.pem 1024
openssl req -new -key app_key.pem -x509 -days 365 -out =
app_cert.pem

2. In Ruby I load those files and the document I want to sign

private_key =3D File.read('app_key.pem')
certificate =3D File.read('app_cert.pem')
data =3D File.read('test.pdf')


3. I use those files with Ruby OpenSSL classes

key =3D OpenSSL::pKey::RSA.new(private_key)
crt =3D OpenSSL::X509::Certificate.new(certificate)


4. I create a detached signature:

signed =3D OpenSSL::pKCS7::sign(crt, key, data, [], =20
OpenSSL::pKCS7::DETACHED)

This actually works and gives me an p7s file. When using =20
OpenSSL::pKCS7::BINARY it seems to me that I get a pkcs7 file which is =20=

containing the payload of my testfile.

The problem here:

This convenient methods seems to use a SHA-1 hash for signing the =20
document =96 but I need to utilize at least SHA-256 or better SHA-512 as =
=20
it is a legal requirement for my application (qualified digital =20
signatures as defined in SiG of German law).
So I tried another way which was outlined on the web at =
https://www.koders.com/ruby/fid1543851B1354DA253F0B0D3E21DFDED96E30FFF4.as=
px

Continuing by step 3.) I did the following:
=09
p7 =3D OpenSSL::pKCS7::pKCS7.new(OpenSSL::pKCS7::SIGNED)
Problem: OpenSSL::pKCS7::SIGNED is undefined=85 and I don't know what to =
=20
specify here, else.

signer =3D OpenSSL::pKCS7::Signer.new(crt, key, =
Digest::SHA1.new)

Here it seems to me that I can modify the digest algorithmus =96 but =20
using Digest::SHA256.new or Digest::SHA512.new fails.
Weird is, that I in irb the following works:
require 'digest/sha2'
Digest::SHA512.hexdigest('test')
=3D> =20
=
"ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a=
0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff=20
"

Anyway=85 going in with SHA1.
p7.add_signer(signer, key)
This command complained that the 2nd parameter is too much. So I =20
stripped this and get the error that signer is the wrong type.
signer actually is from kind of SignerInfo.

Next step would be:

p7.add_certificate(cert)

=85which makes no problem.
But the next methods throws an error because of the second parameter. =20=

The 2nd parameter should have been to specify wether the signatur =20
should be detached or containing the payload (compare =20
OpenSSL::pKCS7::DETACHED, OpenSSL::pKCS7::BINARY above)
=09
p7.add_data(data, true)

In the end I would have been able to put out the signature either in =20
PEM or DER format:
p7.to_pem
p7.to_der

So here's my question:

How can I sign a PDF document to get a detached signature in p7s =20
format which was created by the usage of a private key, a certificate =20=

and SHA512 for hashing?


Just for your interest=85 I'm currently working on an open source =20
project (http://github.com/yves-vogl/kimei) which should allow signing =20=

documents which a private key / certificate as a webservice.
To make it compliant with european law, especially when signing =20
documents like invoices, it need to have the format mentioned above =20
and to utilize a private key / certficate from a smartcard provided by =20=

the government.
To implement the latter feature I talked to Victor Costan who built =20
"smartcard", a STL for accessing smartcard readers by their PCSC =20
interface. If you are interested in this or would like to help - just =20=

tell us. This would be really appreciated.

Cheers from Germany,

Yves
 
Ad

Advertisements

S

Sandor Szücs

Hi Yves,

I am far from an expert, but maybe you just missunderstood a few things
what you have read.
Disclaimer: I just read your E-Mail and tried to guess in irb.

On 24.01.2009, at 15:13, Yves Vogl wrote:

--------------------8<-------------------
4. I create a detached signature:

signed =3D OpenSSL::pKCS7::sign(crt, key, data, [], =20
OpenSSL::pKCS7::DETACHED)

This actually works and gives me an p7s file. When using =20
OpenSSL::pKCS7::BINARY it seems to me that I get a pkcs7 file which =20=
is containing the payload of my testfile.

The problem here:

This convenient methods seems to use a SHA-1 hash for signing the =20
document =96 but I need to utilize at least SHA-256 or better SHA-512 =20=
as it is a legal requirement for my application (qualified digital =20
signatures as defined in SiG of German law).
So I tried another way which was outlined on the web at = https://www.koders.com/ruby/fid1543851B1354DA253F0B0D3E21DFDED96E30FFF4.as=
px

Continuing by step 3.) I did the following:
=09
p7 =3D OpenSSL::pKCS7::pKCS7.new(OpenSSL::pKCS7::SIGNED)
Problem: OpenSSL::pKCS7::SIGNED is undefined=85 and I don't know what =20=
to specify here, else.

I think what you want is:
p7 =3D OpenSSL::pKCS7::pKCS7.new(signed)

The variable signed is the signature in your example and is of type =20
OpenSSL::pKCS7.

signer =3D OpenSSL::pKCS7::Signer.new(crt, key, =
Digest::SHA1.new)

The following just works here:
signer =3D OpenSSL::pKCS7::Signer.new(crt, key, =20
OpenSSL::Digest::SHA512.new)

Try OpenSSL::Digest::SHA512.new, Digest is not the same as =20
Openssl::Digest.
irb> Digest.class
=3D> Module
irb> OpenSSL::Digest.class
=3D> Class

Here it seems to me that I can modify the digest algorithmus =96 but =20=
using Digest::SHA256.new or Digest::SHA512.new fails.
Weird is, that I in irb the following works:
require 'digest/sha2'
Digest::SHA512.hexdigest('test')
=3D> =20
= "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a=
0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff=20
"

Anyway=85 going in with SHA1.
p7.add_signer(signer, key)
This command complained that the 2nd parameter is too much. So I =20
stripped this and get the error that signer is the wrong type.
signer actually is from kind of SignerInfo.

Maybe I am naive, but p7.add_signer(signer) should be enough,
because signer got the key already.

Next step would be:

p7.add_certificate(cert)

You mean p7.add_certificate(crt) or am I missing something?

=85which makes no problem.
But the next methods throws an error because of the second =20
parameter. The 2nd parameter should have been to specify wether the =20=
signatur should be detached or containing the payload (compare =20
OpenSSL::pKCS7::DETACHED, OpenSSL::pKCS7::BINARY above)
=09
p7.add_data(data, true)

Maybe I am naive, but p7.add_data(data), should be enough, because
signed is detached already.
In the end I would have been able to put out the signature either in =20=
PEM or DER format:
p7.to_pem
p7.to_der


So here's my question:

How can I sign a PDF document to get a detached signature in p7s =20
format which was created by the usage of a private key, a =20
certificate and SHA512 for hashing?

I am sure you will get it.
Just for your interest=85 I'm currently working on an open source =20
project (http://github.com/yves-vogl/kimei) which should allow =20
signing documents which a private key / certificate as a webservice.
To make it compliant with european law, especially when signing =20
documents like invoices, it need to have the format mentioned above =20=
and to utilize a private key / certficate from a smartcard provided =20=
by the government.
To implement the latter feature I talked to Victor Costan who built =20=
"smartcard", a STL for accessing smartcard readers by their PCSC =20
interface. If you are interested in this or would like to help - =20
just tell us. This would be really appreciated.

Nice.

Hope this helps, regards
Sandor Sz=FCcs
--
 
B

Brian Candler

Yves said:
This convenient methods seems to use a SHA-1 hash for signing the
document

Yes. ruby's openssl library is just a thin wrapper around the openssl C
API.

If you look at [ruby]/ext/openssl/ossl_pkcs7.c you'll see that this
method just gets some arguments and then calls [openssl] PKCS7_sign()

On my system, typing 'man PKCS7_sign' says:

BUGS
....

The SHA1 digest algorithm is currently always used.

So the question is really not a Ruby one, but an openssl one. If you can
find a way to generate the signature you want, using either the C API or
the openssl command line tool, it can probably be converted to Ruby
easily. But first you need to solve the openssl library problem, and I'd
suggest that's best done by asking the question on an openssl mailing
list.

Sorry I can't be more helpful than that...

Regards,

Brian.
 
Y

Yves Vogl

Hi,

I think what you want is:
p7 =3D OpenSSL::pKCS7::pKCS7.new(signed)

The variable signed is the signature in your example and is of type =20=
OpenSSL::pKCS7.

But isn't this redundant then?


signed =3D OpenSSL::pKCS7::sign(crt, key, data, [], =20
OpenSSL::pKCS7::DETACHED)


At this point I have a complete p7s file already (see signed.inspect)


When adding certificate and data again, I'm signing a signature=85

p7 =3D OpenSSL::pKCS7::pKCS7.new(signed)
p7.add_certificate(crt)
p7.add_data(data)

At least my signature is as big as the input file then=85 no matter if I =
=20
say "DETACHED" or "BINARY" in the example above.

Try OpenSSL::Digest::SHA512.new, Digest is not the same as =20
Openssl::Digest.
irb> Digest.class
=3D> Module
irb> OpenSSL::Digest.class
=3D> Class
NameError: uninitialized constant Digest
from (irb):11=3D> Module


irb 0.9.5(05/04/13)
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
Mac OS X 10.5.6, Darwin Kernel Version 9.6.0: Mon Nov 24 17:37:00 PST =20=

2008; root:xnu-1228.9.59~1/RELEASE_I386


Cheers, Yves
 
Ad

Advertisements

B

ben *******

is it possible, instead of "DETACHED" in 'signed =
OpenSSL::pKCS7::sign(crt, key, data, [],OpenSSL::pKCS7::DETACHED)" to
replace by "EMBEDDED" ?

If i want create an embedded signature, how can i do it ?
 

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

Top