From: Brian Candler Date: 2005-04-08T06:14:16+09:00 Subject: Re: Ruby ASN1 examples? --W/nzBZO5zC0uMSeA Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Sat, Mar 26, 2005 at 11:35:07AM +0000, Brian Candler wrote: > Does anyone have any example code I can see for dealing with ASN1 in Ruby? I finally hacked my way through this, and am posting it here for reference. The hardest part was how to make a tagged instance of a tagged type. That is, in the example in Annex A of X.690, we have: dateOfHire [1] Date, ... Date ::= [APPLICATION 3] IMPLICIT VisibleString -- YYYYMMDD So we have Date as a tagged type, and dateOfHire as a tagged instance of that type. The Annex shows that this encodes as: dateOfHire Length A1 0A <-----------------------> Date Length Contents 43 08 "19710917" It's easy to generate an untagged instance of the Date type, like this: date = OpenSSL::ASN1::ISO64String("19710917", 3, :IMPLICIT, :APPLICATION) p date.to_der # => 43 08 "19710917" And it's also easy to generate a tagged instance of a universal type: foo = OpenSSL::ASN1::ISO64String("19710917", 1, :EXPLICIT, :CONTEXT_SPECIFIC) p foo.to_der # => A1 0A 1A 08 "19710917" ^ `-- VisibleString (not Date) But a tagged instance of a tagged type is tricky. The only way I could figure out was: date = OpenSSL::ASN1::ISO64String("19710917", 3, :IMPLICIT, :APPLICATION) date2 = OpenSSL::ASN1::ASN1Data.new([date], 1, :CONTEXT_SPECIFIC) p date2.to_der # => A1 0A 43 08 "19710917" ext/openssl/ossl_asn1.c claims to be written by "'OpenSSL for Ruby' team members" - are there any of them here who would care to comment? Is there an easier way to achieve this? Regards, Brian. --W/nzBZO5zC0uMSeA Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="test-asn1.rb" require 'openssl' require 'test/unit' # This is a test which replicates the test case in Annex A of ITU-T Rec. X.690 # See http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf class TestASN < Test::Unit::TestCase def test_john_p_smith a = OpenSSL::ASN1::Set([], 0, :IMPLICIT, :APPLICATION) name = OpenSSL::ASN1::Sequence([], 1, :IMPLICIT, :APPLICATION) name.value << OpenSSL::ASN1::ISO64String("John") name.value << OpenSSL::ASN1::ISO64String("P") name.value << OpenSSL::ASN1::ISO64String("Smith") a.value << name a.value << OpenSSL::ASN1::ISO64String("Director", 0, :EXPLICIT, :CONTEXT_SPECIFIC) a.value << OpenSSL::ASN1::Integer(51, 2, :IMPLICIT, :APPLICATION) x = OpenSSL::ASN1::ISO64String("19710917", 3, :IMPLICIT, :APPLICATION) date = OpenSSL::ASN1::ASN1Data.new([x], 1, :CONTEXT_SPECIFIC) a.value << date x = OpenSSL::ASN1::Sequence([], 1, :IMPLICIT, :APPLICATION) x.value << OpenSSL::ASN1::ISO64String("Mary") x.value << OpenSSL::ASN1::ISO64String("T") x.value << OpenSSL::ASN1::ISO64String("Smith") spouse = OpenSSL::ASN1::ASN1Data.new([x], 2, :CONTEXT_SPECIFIC) a.value << spouse children = OpenSSL::ASN1::Sequence([], 3, :IMPLICIT, :CONTEXT_SPECIFIC) child = OpenSSL::ASN1::Set([]) childname = OpenSSL::ASN1::Sequence([], 1, :IMPLICIT, :APPLICATION) childname.value << OpenSSL::ASN1::ISO64String("Ralph") childname.value << OpenSSL::ASN1::ISO64String("T") childname.value << OpenSSL::ASN1::ISO64String("Smith") child.value << childname x = OpenSSL::ASN1::ISO64String("19571111", 3, :IMPLICIT, :APPLICATION) childdob = OpenSSL::ASN1::ASN1Data.new([x], 1, :CONTEXT_SPECIFIC) child.value << childdob children.value << child child = OpenSSL::ASN1::Set([]) childname = OpenSSL::ASN1::Sequence([], 1, :IMPLICIT, :APPLICATION) childname.value << OpenSSL::ASN1::ISO64String("Susan") childname.value << OpenSSL::ASN1::ISO64String("B") childname.value << OpenSSL::ASN1::ISO64String("Jones") child.value << childname x = OpenSSL::ASN1::ISO64String("19590717", 3, :IMPLICIT, :APPLICATION) childdob = OpenSSL::ASN1::ASN1Data.new([x], 1, :CONTEXT_SPECIFIC) child.value << childdob children.value << child a.value << children der = a.to_der bytes = der.unpack("H*")[0].scan(/(..)/).join(":") p bytes decode = OpenSSL::ASN1.decode(der) # decode != a because some source ASN1::Set or Sequence become # ASN1Data in the output, as do our application-defined types require 'pp' pp a pp decode end end --W/nzBZO5zC0uMSeA--