From 9147e519ba5abad72672a61912dc16dde9aea3a7 Mon Sep 17 00:00:00 2001 From: gotoyuzo Date: Wed, 7 Sep 2005 07:40:55 +0000 Subject: * ext/openssl/ossl_asn1.c (asn1str_to_str): new function. * ext/openssl/ossl_pkcs7.c: new class OpenSSL::PKCS7::RecipientInfo. this class wraps PKCS7_RECIP_INFO struct. * ext/openssl/ossl_pkcs7.c: OpenSSL::PKCS7::Signer is renamed to OpenSSL::PKCS7::SignerInfo. ("Signer" remains as an alias of SignerInfo.) * test/openssl/test_pkcs7.rb: new file. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@9094 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 13 ++++ ext/openssl/ossl_asn1.c | 9 +++ ext/openssl/ossl_asn1.h | 5 ++ ext/openssl/ossl_pkcs7.c | 168 ++++++++++++++++++++++++++++++++++++++++----- ext/openssl/ossl_pkcs7.h | 3 +- test/openssl/test_pkcs7.rb | 148 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 329 insertions(+), 17 deletions(-) create mode 100644 test/openssl/test_pkcs7.rb diff --git a/ChangeLog b/ChangeLog index 546e901448..fbc573755c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Wed Sep 7 15:48:37 2005 GOTOU Yuuzou + + * ext/openssl/ossl_asn1.c (asn1str_to_str): new function. + + * ext/openssl/ossl_pkcs7.c: new class OpenSSL::PKCS7::RecipientInfo. + this class wraps PKCS7_RECIP_INFO struct. + + * ext/openssl/ossl_pkcs7.c: OpenSSL::PKCS7::Signer is renamed to + OpenSSL::PKCS7::SignerInfo. ("Signer" remains as an alias of + SignerInfo.) + + * test/openssl/test_pkcs7.rb: new file. + Wed Sep 7 12:55:08 2005 Tanaka Akira * lib/open-uri.rb: abolish mod === tempfile to avoid a problem diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 9f55f6c68c..8d6bd014ca 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -74,6 +74,15 @@ time_to_time_t(VALUE time) return (time_t)NUM2LONG(rb_Integer(time)); } +/* + * STRING conversion + */ +VALUE +asn1str_to_str(ASN1_STRING *str) +{ + return rb_str_new(str->data, str->length); +} + /* * ASN1_INTEGER conversions * TODO: Make a decision what's the right way to do this. diff --git a/ext/openssl/ossl_asn1.h b/ext/openssl/ossl_asn1.h index 919ede0f3b..8aad9f970d 100644 --- a/ext/openssl/ossl_asn1.h +++ b/ext/openssl/ossl_asn1.h @@ -17,6 +17,11 @@ VALUE asn1time_to_time(ASN1_TIME *); time_t time_to_time_t(VALUE); +/* + * ASN1_STRING conversions + */ +VALUE asn1str_to_str(ASN1_STRING *); + /* * ASN1_INTEGER conversions */ diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 8d4b1f206e..38a7dce7a2 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -44,6 +44,23 @@ GetPKCS7si(obj, p7si); \ } while (0) +#define WrapPKCS7ri(klass, obj, p7ri) do { \ + if (!p7ri) { \ + ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ + } \ + obj = Data_Wrap_Struct(klass, 0, PKCS7_RECIP_INFO_free, p7ri); \ +} while (0) +#define GetPKCS7ri(obj, p7ri) do { \ + Data_Get_Struct(obj, PKCS7_RECIP_INFO, p7ri); \ + if (!p7ri) { \ + ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ + } \ +} while (0) +#define SafeGetPKCS7ri(obj, p7ri) do { \ + OSSL_Check_Kind(obj, cPKCS7Recipient); \ + GetPKCS7ri(obj, p7ri); \ +} while (0) + #define numberof(ary) (sizeof(ary)/sizeof(ary[0])) #define ossl_pkcs7_set_data(o,v) rb_iv_set((o), "@data", (v)) @@ -57,6 +74,7 @@ VALUE mPKCS7; VALUE cPKCS7; VALUE cPKCS7Signer; +VALUE cPKCS7Recipient; VALUE ePKCS7Error; /* @@ -89,6 +107,32 @@ DupPKCS7SignerPtr(VALUE obj) return pkcs7; } +static VALUE +ossl_pkcs7ri_new(PKCS7_RECIP_INFO *p7ri) +{ + PKCS7_RECIP_INFO *pkcs7; + VALUE obj; + + pkcs7 = p7ri ? PKCS7_RECIP_INFO_dup(p7ri) : PKCS7_RECIP_INFO_new(); + if (!pkcs7) ossl_raise(ePKCS7Error, NULL); + WrapPKCS7ri(cPKCS7Recipient, obj, pkcs7); + + return obj; +} + +static PKCS7_RECIP_INFO * +DupPKCS7RecipientPtr(VALUE obj) +{ + PKCS7_RECIP_INFO *p7ri, *pkcs7; + + SafeGetPKCS7ri(obj, p7ri); + if (!(pkcs7 = PKCS7_RECIP_INFO_dup(p7ri))) { + ossl_raise(ePKCS7Error, NULL); + } + + return pkcs7; +} + /* * Private */ @@ -441,29 +485,49 @@ ossl_pkcs7_get_signer(VALUE self) } static VALUE -ossl_pkcs7_add_recipient(VALUE self, VALUE cert) +ossl_pkcs7_add_recipient(VALUE self, VALUE recip) { PKCS7 *pkcs7; PKCS7_RECIP_INFO *ri; - X509 *x509; - - x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ - if (!(ri = PKCS7_RECIP_INFO_new())) { - ossl_raise(ePKCS7Error, NULL); - } - if (!PKCS7_RECIP_INFO_set(ri, x509)) { - PKCS7_RECIP_INFO_free(ri); - ossl_raise(ePKCS7Error, NULL); - } + + ri = DupPKCS7RecipientPtr(recip); /* NEED TO DUP */ GetPKCS7(self, pkcs7); if (!PKCS7_add_recipient_info(pkcs7, ri)) { PKCS7_RECIP_INFO_free(ri); - ossl_raise(ePKCS7Error, NULL); + ossl_raise(ePKCS7Error, "Could not add recipient."); } return self; } +static VALUE +ossl_pkcs7_get_recipient(VALUE self) +{ + PKCS7 *pkcs7; + STACK_OF(PKCS7_RECIP_INFO) *sk; + PKCS7_RECIP_INFO *si; + int num, i; + VALUE ary; + + GetPKCS7(self, pkcs7); + if (PKCS7_type_is_enveloped(pkcs7)) + sk = pkcs7->d.enveloped->recipientinfo; + else if (PKCS7_type_is_signedAndEnveloped(pkcs7)) + sk = pkcs7->d.signed_and_enveloped->recipientinfo; + else sk = NULL; + if (!sk) return rb_ary_new(); + if ((num = sk_PKCS7_RECIP_INFO_num(sk)) < 0) { + ossl_raise(ePKCS7Error, "Negative number of recipient!"); + } + ary = rb_ary_new2(num); + for (i=0; iissuer_and_serial->issuer); +} + +static VALUE +ossl_pkcs7ri_get_serial(VALUE self) +{ + PKCS7_RECIP_INFO *p7ri; + + GetPKCS7ri(self, p7ri); + + return asn1integer_to_num(p7ri->issuer_and_serial->serial); +} + +static VALUE +ossl_pkcs7ri_get_enc_key(VALUE self) +{ + PKCS7_RECIP_INFO *p7ri; + + GetPKCS7ri(self, p7ri); + + return asn1str_to_str(p7ri->enc_key); +} + /* * INIT */ @@ -821,6 +947,7 @@ Init_ossl_pkcs7() rb_define_method(cPKCS7, "add_signer", ossl_pkcs7_add_signer, 1); rb_define_method(cPKCS7, "signers", ossl_pkcs7_get_signer, 0); rb_define_method(cPKCS7, "add_recipient", ossl_pkcs7_add_recipient, 1); + rb_define_method(cPKCS7, "recipients", ossl_pkcs7_get_recipient, 0); rb_define_method(cPKCS7, "add_certificate", ossl_pkcs7_add_certificate, 1); rb_define_method(cPKCS7, "certificates=", ossl_pkcs7_set_certificates, 1); rb_define_method(cPKCS7, "certificates", ossl_pkcs7_get_certificates, 0); @@ -835,12 +962,21 @@ Init_ossl_pkcs7() rb_define_alias(cPKCS7, "to_s", "to_pem"); rb_define_method(cPKCS7, "to_der", ossl_pkcs7_to_der, 0); - cPKCS7Signer = rb_define_class_under(mPKCS7, "Signer", rb_cObject); + cPKCS7Signer = rb_define_class_under(mPKCS7, "SignerInfo", rb_cObject); + rb_define_const(mPKCS7, "Signer", cPKCS7Signer); rb_define_alloc_func(cPKCS7Signer, ossl_pkcs7si_alloc); rb_define_method(cPKCS7Signer, "initialize", ossl_pkcs7si_initialize,3); - rb_define_method(cPKCS7Signer, "name", ossl_pkcs7si_get_name,0); + rb_define_method(cPKCS7Signer, "issuer", ossl_pkcs7si_get_issuer, 0); + rb_define_alias(cPKCS7Signer, "name", "issuer"); rb_define_method(cPKCS7Signer, "serial", ossl_pkcs7si_get_serial,0); - rb_define_method(cPKCS7Signer, "signed_time", ossl_pkcs7si_get_signed_time,0); + rb_define_method(cPKCS7Signer,"signed_time",ossl_pkcs7si_get_signed_time,0); + + cPKCS7Recipient = rb_define_class_under(mPKCS7,"RecipientInfo",rb_cObject); + rb_define_alloc_func(cPKCS7Recipient, ossl_pkcs7ri_alloc); + rb_define_method(cPKCS7Recipient, "initialize", ossl_pkcs7ri_initialize,1); + rb_define_method(cPKCS7Recipient, "issuer", ossl_pkcs7ri_get_issuer,0); + rb_define_method(cPKCS7Recipient, "serial", ossl_pkcs7ri_get_serial,0); + rb_define_method(cPKCS7Recipient, "enc_key", ossl_pkcs7ri_get_enc_key,0); #define DefPKCS7Const(x) rb_define_const(mPKCS7, #x, INT2NUM(PKCS7_##x)) diff --git a/ext/openssl/ossl_pkcs7.h b/ext/openssl/ossl_pkcs7.h index 9378397f25..f5942d65db 100644 --- a/ext/openssl/ossl_pkcs7.h +++ b/ext/openssl/ossl_pkcs7.h @@ -13,7 +13,8 @@ extern VALUE mPKCS7; extern VALUE cPKCS7; -extern VALUE cPKCS7SignerInfo; +extern VALUE cPKCS7Signer; +extern VALUE cPKCS7Recipient; extern VALUE ePKCS7Error; void Init_ossl_pkcs7(void); diff --git a/test/openssl/test_pkcs7.rb b/test/openssl/test_pkcs7.rb new file mode 100644 index 0000000000..a2153af363 --- /dev/null +++ b/test/openssl/test_pkcs7.rb @@ -0,0 +1,148 @@ +begin + require "openssl" + require File.join(File.dirname(__FILE__), "utils.rb") +rescue LoadError +end +require "test/unit" + +if defined?(OpenSSL) + +class OpenSSL::TestPKCS7 < Test::Unit::TestCase + def setup + @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 + @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 + ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") + ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") + ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") + + now = Time.now + ca_exts = [ + ["basicConstraints","CA:TRUE",true], + ["keyUsage","keyCertSign, cRLSign",true], + ["subjectKeyIdentifier","hash",false], + ["authorityKeyIdentifier","keyid:always",false], + ] + @ca_cert = issue_cert(ca, @rsa2048, 1, Time.now, Time.now+3600, ca_exts, + nil, nil, OpenSSL::Digest::SHA1.new) + ee_exts = [ + ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true], + ["authorityKeyIdentifier","keyid:always",false], + ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false], + ] + @ee1_cert = issue_cert(ee1, @rsa1024, 2, Time.now, Time.now+1800, ee_exts, + @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + @ee2_cert = issue_cert(ee2, @rsa1024, 3, Time.now, Time.now+1800, ee_exts, + @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new) + end + + def issue_cert(*args) + OpenSSL::TestUtils.issue_cert(*args) + end + + def test_signed + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + ca_certs = [@ca_cert] + + data = "aaaaa\r\nbbbbb\r\nccccc\r\n" + tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs) + p7 = OpenSSL::PKCS7::PKCS7.new(tmp.to_der) + certs = p7.certificates + signers = p7.signers + assert(p7.verify([], store)) + assert_equal(data, p7.data) + assert_equal(2, certs.size) + assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) + assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) + assert_equal(1, signers.size) + assert_equal(@ee1_cert.serial, signers[0].serial) + assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) + + # Normaly OpenSSL tries to translate the supplied content into canonical + # MIME format (e.g. a newline character is converted into CR+LF). + # If the content is a binary, PKCS7::BINARY flag should be used. + + data = "aaaaa\nbbbbb\nccccc\n" + flag = OpenSSL::PKCS7::BINARY + tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag) + p7 = OpenSSL::PKCS7::PKCS7.new(tmp.to_der) + certs = p7.certificates + signers = p7.signers + assert(p7.verify([], store)) + assert_equal(data, p7.data) + assert_equal(2, certs.size) + assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) + assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) + assert_equal(1, signers.size) + assert_equal(@ee1_cert.serial, signers[0].serial) + assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) + + # A signed-data which have multiple signatures can be created + # through the following steps. + # 1. create two signed-data + # 2. copy signerInfo and certificate from one to another + + tmp1 = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, [], flag) + tmp2 = OpenSSL::PKCS7.sign(@ee2_cert, @rsa1024, data, [], flag) + tmp1.add_signer(tmp2.signers[0]) + tmp1.add_certificate(@ee2_cert) + + p7 = OpenSSL::PKCS7::PKCS7.new(tmp1.to_der) + certs = p7.certificates + signers = p7.signers + assert(p7.verify([], store)) + assert_equal(data, p7.data) + assert_equal(2, certs.size) + assert_equal(2, signers.size) + assert_equal(@ee1_cert.serial, signers[0].serial) + assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) + assert_equal(@ee2_cert.serial, signers[1].serial) + assert_equal(@ee2_cert.issuer.to_s, signers[1].issuer.to_s) + end + + def test_detached_sign + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + ca_certs = [@ca_cert] + + data = "aaaaa\nbbbbb\nccccc\n" + flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED + tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag) + p7 = OpenSSL::PKCS7::PKCS7.new(tmp.to_der) + a1 = OpenSSL::ASN1.decode(p7) + + certs = p7.certificates + signers = p7.signers + assert(!p7.verify([], store)) + assert(p7.verify([], store, data)) + assert_equal(data, p7.data) + assert_equal(2, certs.size) + assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) + assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) + assert_equal(1, signers.size) + assert_equal(@ee1_cert.serial, signers[0].serial) + assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) + end + + def test_enveloped + certs = [@ee1_cert, @ee2_cert] + cipher = OpenSSL::Cipher::AES.new("128-CBC") + data = "aaaaa\nbbbbb\nccccc\n" + + tmp = OpenSSL::PKCS7.encrypt(certs, data, cipher, OpenSSL::PKCS7::BINARY) + p7 = OpenSSL::PKCS7::PKCS7.new(tmp.to_der) + recip = p7.recipients + assert_equal(:enveloped, p7.type) + assert_equal(2, recip.size) + + assert_equal(@ca_cert.subject.to_s, recip[0].issuer.to_s) + assert_equal(2, recip[0].serial) + assert_equal(data, p7.decrypt(@rsa1024, @ee1_cert)) + + assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s) + assert_equal(3, recip[1].serial) + assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert)) + end +end + +end -- cgit v1.2.3