cert-to-cwt 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/cert-to-cwt.rb +193 -0
- data/cert-to-cwt.gemspec +19 -0
- metadata +89 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 3f30a9c6e0631d3daaca07bac5a27a9dc34bd4cb
         | 
| 4 | 
            +
              data.tar.gz: e78ff0859be82052447ac491c072d78b5f217919
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 9ff25fea72d4b4ae22172b72ccf3f739d2607d221ac855a498a24dabb00f2f8900cd7b8f33a49217a102ddb091b781f9f93c8d25d83e6600372059729f432a86
         | 
| 7 | 
            +
              data.tar.gz: e1626c35abfbca01ad4d00157ce149add3330414ac5dee2ce532d6ed65709a3244f15f6814df534ad0fd18e23f08252887dba1da1df6f262e3a89b7c94b71f87
         | 
    
        data/bin/cert-to-cwt.rb
    ADDED
    
    | @@ -0,0 +1,193 @@ | |
| 1 | 
            +
            require 'asn1-pure.rb'
         | 
| 2 | 
            +
            require 'yaml'
         | 
| 3 | 
            +
            require 'time'
         | 
| 4 | 
            +
            require 'cbor-diagnostic'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            class String
         | 
| 7 | 
            +
              def hexi
         | 
| 8 | 
            +
                bytes.map{|x| "%02x" % x}.join
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
              def hexs
         | 
| 11 | 
            +
                bytes.map{|x| "%02x" % x}.join(" ")
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
              def xeh
         | 
| 14 | 
            +
                gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            if ARGV[0] == "-b"
         | 
| 19 | 
            +
              ARGV.shift
         | 
| 20 | 
            +
              $binary = true
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
            DUMPFILE = "dumped-cert.yml"
         | 
| 23 | 
            +
            if ARGV[0] == "-d"
         | 
| 24 | 
            +
              ARGV.shift
         | 
| 25 | 
            +
              $dump = true
         | 
| 26 | 
            +
            end
         | 
| 27 | 
            +
            if ARGV[0] == "-b"
         | 
| 28 | 
            +
              ARGV.shift
         | 
| 29 | 
            +
              $binary = true
         | 
| 30 | 
            +
            end
         | 
| 31 | 
            +
            f = ARGF.read.b
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            def numbertobytes(n)            # doesn't work for 0
         | 
| 34 | 
            +
              if n == 0
         | 
| 35 | 
            +
                "".b
         | 
| 36 | 
            +
              else
         | 
| 37 | 
            +
                s = n.to_s(16)
         | 
| 38 | 
            +
                s[0...0] = "0" if s.size.odd?
         | 
| 39 | 
            +
                [s].pack("H*").b
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| 42 | 
            +
            fail unless numbertobytes(0) == ""
         | 
| 43 | 
            +
            fail unless numbertobytes(10) == "\x0A"
         | 
| 44 | 
            +
            fail unless numbertobytes(160) == "\xA0".b
         | 
| 45 | 
            +
            fail unless numbertobytes(1000) == "\x03\xE8".b
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            def bitstringtobytes(s)
         | 
| 48 | 
            +
              fail "bitstring #{s}" if s[0] != "\0"
         | 
| 49 | 
            +
              s[1..-1]
         | 
| 50 | 
            +
            end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            def datetimeasn1(s)
         | 
| 53 | 
            +
              Time.iso8601(s.sub(/\A(..)(..)(..)(..)(..)(..)Z\z/){"20#$1-#$2-#$3T#$4:#$5:#$6Z"}).to_i
         | 
| 54 | 
            +
            end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            OIDLOOKUP = Hash.new {|h, k| k}
         | 
| 57 | 
            +
            %w(
         | 
| 58 | 
            +
            2.5.4.3                              CN
         | 
| 59 | 
            +
            1.2.840.113549.1.9.1                 E
         | 
| 60 | 
            +
            2.5.4.11                             OU
         | 
| 61 | 
            +
            2.5.4.10                             O
         | 
| 62 | 
            +
            2.5.4.7                              L
         | 
| 63 | 
            +
            2.5.4.8                              ST
         | 
| 64 | 
            +
            2.5.4.6                              C
         | 
| 65 | 
            +
            0.9.2342.19200300.100.1.25           DC
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            2.5.4.12                             TITLE
         | 
| 68 | 
            +
            0.9.2342.19200300.100.1.3            MAIL
         | 
| 69 | 
            +
            2.5.4.5                              SERIALNUMBER
         | 
| 70 | 
            +
            1.2.840.113549.1.9.2                 UNSTRUCTUREDNAME
         | 
| 71 | 
            +
            1.2.840.113549.1.9.8                 UNSTRUCTUREDADDRESS
         | 
| 72 | 
            +
            2.5.4.42                             GN
         | 
| 73 | 
            +
            2.5.4.4                              SN
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            2.5.4.9                              STREET
         | 
| 76 | 
            +
            0.9.2342.19200300.100.1.1            UID
         | 
| 77 | 
            +
            ).each_slice(2).map{ |x, y| OIDLOOKUP[x] = y}
         | 
| 78 | 
            +
            # middle group stolen from netscape certificate management system administrator guide
         | 
| 79 | 
            +
            # puts OIDLOOKUP.to_yaml
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            def dntostring(dn)              # almost RFC 4514
         | 
| 82 | 
            +
              sets = dn.fetch(:seq)
         | 
| 83 | 
            +
              sets.map do |s|
         | 
| 84 | 
            +
                g = s.fetch(:set)
         | 
| 85 | 
            +
                g.map do |elt|
         | 
| 86 | 
            +
                  aname, aval = elt.fetch(:seq)
         | 
| 87 | 
            +
                  [OIDLOOKUP[aname], aval.values.first.gsub(/[ #"+,;<>\\]/){"\\#$&"}].join("=")
         | 
| 88 | 
            +
                end.join("+")
         | 
| 89 | 
            +
              end.join(",")
         | 
| 90 | 
            +
            end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            ALGLOOKUP = Hash.new {|h, k| k}
         | 
| 93 | 
            +
            %w(
         | 
| 94 | 
            +
            1.2.840.113549.1.1.11 sha256WithRSAEncryption
         | 
| 95 | 
            +
            1.2.840.113549.1.1.1  rsaEncryption
         | 
| 96 | 
            +
            ).each_slice(2).map{ |x, y| ALGLOOKUP[x] = y.intern}
         | 
| 97 | 
            +
             | 
| 98 | 
            +
            SYMLOOKUP = Hash.new {|h, k| warn "label unknown for key :#{k}"; k}
         | 
| 99 | 
            +
            %w(
         | 
| 100 | 
            +
            iss   1
         | 
| 101 | 
            +
            sub   2
         | 
| 102 | 
            +
            aud   3
         | 
| 103 | 
            +
            exp   4
         | 
| 104 | 
            +
            nbf   5
         | 
| 105 | 
            +
            iat   6
         | 
| 106 | 
            +
            cti   7
         | 
| 107 | 
            +
             | 
| 108 | 
            +
             | 
| 109 | 
            +
            kty 1
         | 
| 110 | 
            +
            n -1
         | 
| 111 | 
            +
            e -2
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            serial -100000
         | 
| 114 | 
            +
            pk -100001
         | 
| 115 | 
            +
            ).each_slice(2).map{ |x, y| SYMLOOKUP[x.intern] = y.to_i}
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            def desymbolicate(o)
         | 
| 118 | 
            +
              case o
         | 
| 119 | 
            +
              when Symbol
         | 
| 120 | 
            +
                SYMLOOKUP[o]
         | 
| 121 | 
            +
              when Hash
         | 
| 122 | 
            +
                Hash[o.map {|k, v| [desymbolicate(k), desymbolicate(v)]}]
         | 
| 123 | 
            +
              when Array
         | 
| 124 | 
            +
                o.map {|x| desymbolicate(x)}
         | 
| 125 | 
            +
              else
         | 
| 126 | 
            +
                o
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
            end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
            #  RFC 4514
         | 
| 131 | 
            +
             | 
| 132 | 
            +
            cert = ASN1.decode(f)
         | 
| 133 | 
            +
            claimset = {}
         | 
| 134 | 
            +
            if $dump
         | 
| 135 | 
            +
              warn "Dumping cert as #{DUMPFILE}..."
         | 
| 136 | 
            +
              File.write(DUMPFILE, cert.to_yaml)
         | 
| 137 | 
            +
            end
         | 
| 138 | 
            +
            tbs, sigalg, sigval = cert.fetch(:seq)
         | 
| 139 | 
            +
            ver, ser, sigalg1, iss, validity, sub, spki, *rest = tbs.fetch(:seq)
         | 
| 140 | 
            +
            fail [:ver, ver].inspect unless ver == {exp0: [2]}
         | 
| 141 | 
            +
            fail [:ser, set].inspect unless Integer === ser
         | 
| 142 | 
            +
            claimset[:serial] = ser
         | 
| 143 | 
            +
            fail [:alg, sigalg, sigalg1].inspect unless sigalg == sigalg1
         | 
| 144 | 
            +
             | 
| 145 | 
            +
            decoded_sigalg = ALGLOOKUP[sigalg.fetch(:seq).first]
         | 
| 146 | 
            +
            # p decoded_sigalg
         | 
| 147 | 
            +
             | 
| 148 | 
            +
            notbefore, notafter = validity.fetch(:seq).map{|x| datetimeasn1(x.fetch(:t))}
         | 
| 149 | 
            +
            # p [notbefore, notafter]
         | 
| 150 | 
            +
            claimset[:nbf] = notbefore
         | 
| 151 | 
            +
            claimset[:exp] = notafter
         | 
| 152 | 
            +
             | 
| 153 | 
            +
            claimset[:iss] = dntostring(iss)
         | 
| 154 | 
            +
            claimset[:sub] = dntostring(sub)
         | 
| 155 | 
            +
             | 
| 156 | 
            +
            pkalg, pkbits = spki.fetch(:seq)
         | 
| 157 | 
            +
            decoded_pkalg = ALGLOOKUP[pkalg.fetch(:seq).first]
         | 
| 158 | 
            +
            kk = bitstringtobytes(pkbits.fetch(:bits))
         | 
| 159 | 
            +
            case decoded_pkalg
         | 
| 160 | 
            +
            when  :rsaEncryption
         | 
| 161 | 
            +
              kk = ASN1.decode(kk)
         | 
| 162 | 
            +
              n, e = kk.fetch(:seq)
         | 
| 163 | 
            +
              u = CBOR.encode(n)
         | 
| 164 | 
            +
              claimset[:pk] = {kty: 3, n: numbertobytes(n), e: numbertobytes(e)}
         | 
| 165 | 
            +
            else
         | 
| 166 | 
            +
              claimset[:pk] = [decoded_pkalg, kk] # TODO convert to COSE key
         | 
| 167 | 
            +
            end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            rest1 = rest.reduce({}, :merge)
         | 
| 170 | 
            +
             | 
| 171 | 
            +
            exp3 = rest1.delete(:exp3)
         | 
| 172 | 
            +
            puts [:rest1, rest1].to_yaml unless rest1 == {}
         | 
| 173 | 
            +
             | 
| 174 | 
            +
            exp3[0].fetch(:seq).each do |e3|
         | 
| 175 | 
            +
              name, crit, val = e3.fetch(:seq)
         | 
| 176 | 
            +
              if Hash === crit
         | 
| 177 | 
            +
                val = crit
         | 
| 178 | 
            +
                crit = false
         | 
| 179 | 
            +
              end
         | 
| 180 | 
            +
              val = val.fetch(:b)
         | 
| 181 | 
            +
              # val = ASN1.decode(val)
         | 
| 182 | 
            +
              claimset[name] = val          # er, ignoring crit right now
         | 
| 183 | 
            +
            end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
            claimset = desymbolicate(claimset)
         | 
| 186 | 
            +
             | 
| 187 | 
            +
            claimset_cbor = claimset.to_cbor
         | 
| 188 | 
            +
            warn "CBOR size: #{claimset_cbor.size}"
         | 
| 189 | 
            +
            if $binary
         | 
| 190 | 
            +
              print claimset_cbor
         | 
| 191 | 
            +
            else
         | 
| 192 | 
            +
              puts CBOR.decode(claimset_cbor).cbor_diagnostic
         | 
| 193 | 
            +
            end
         | 
    
        data/cert-to-cwt.gemspec
    ADDED
    
    | @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            Gem::Specification.new do |s|
         | 
| 2 | 
            +
              s.name = "cert-to-cwt"
         | 
| 3 | 
            +
              s.version = "0.0.1"
         | 
| 4 | 
            +
              s.summary = "Convert an X.509 cert into a CWT claim set"
         | 
| 5 | 
            +
              s.description = %q{cert-to-cwt is a highly experimental converter for X.509 certificates into CWT claim sets.}
         | 
| 6 | 
            +
              s.author = "Carsten Bormann"
         | 
| 7 | 
            +
              s.email = "cabo@tzi.org"
         | 
| 8 | 
            +
              s.license = "Apache-2.0"
         | 
| 9 | 
            +
              s.has_rdoc = false
         | 
| 10 | 
            +
              s.files = Dir['lib/**/*.rb'] + %w(cert-to-cwt.gemspec) + Dir['bin/**/*.rb']
         | 
| 11 | 
            +
              s.executables = Dir['bin/**/*.rb'].map {|x| File.basename(x)}
         | 
| 12 | 
            +
              s.required_ruby_version = '>= 2.4.1'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              s.require_paths = ["lib"]
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              s.add_development_dependency 'bundler', '~>1'
         | 
| 17 | 
            +
              s.add_dependency 'cbor-diag'
         | 
| 18 | 
            +
              s.add_dependency 'asn1-diag'
         | 
| 19 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: cert-to-cwt
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Carsten Bormann
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2017-08-21 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: bundler
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '1'
         | 
| 20 | 
            +
              type: :development
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '1'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: cbor-diag
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - ">="
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '0'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - ">="
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '0'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: asn1-diag
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - ">="
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '0'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - ">="
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '0'
         | 
| 55 | 
            +
            description: cert-to-cwt is a highly experimental converter for X.509 certificates
         | 
| 56 | 
            +
              into CWT claim sets.
         | 
| 57 | 
            +
            email: cabo@tzi.org
         | 
| 58 | 
            +
            executables:
         | 
| 59 | 
            +
            - cert-to-cwt.rb
         | 
| 60 | 
            +
            extensions: []
         | 
| 61 | 
            +
            extra_rdoc_files: []
         | 
| 62 | 
            +
            files:
         | 
| 63 | 
            +
            - bin/cert-to-cwt.rb
         | 
| 64 | 
            +
            - cert-to-cwt.gemspec
         | 
| 65 | 
            +
            homepage: 
         | 
| 66 | 
            +
            licenses:
         | 
| 67 | 
            +
            - Apache-2.0
         | 
| 68 | 
            +
            metadata: {}
         | 
| 69 | 
            +
            post_install_message: 
         | 
| 70 | 
            +
            rdoc_options: []
         | 
| 71 | 
            +
            require_paths:
         | 
| 72 | 
            +
            - lib
         | 
| 73 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 74 | 
            +
              requirements:
         | 
| 75 | 
            +
              - - ">="
         | 
| 76 | 
            +
                - !ruby/object:Gem::Version
         | 
| 77 | 
            +
                  version: 2.4.1
         | 
| 78 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
              requirements:
         | 
| 80 | 
            +
              - - ">="
         | 
| 81 | 
            +
                - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                  version: '0'
         | 
| 83 | 
            +
            requirements: []
         | 
| 84 | 
            +
            rubyforge_project: 
         | 
| 85 | 
            +
            rubygems_version: 2.6.11
         | 
| 86 | 
            +
            signing_key: 
         | 
| 87 | 
            +
            specification_version: 4
         | 
| 88 | 
            +
            summary: Convert an X.509 cert into a CWT claim set
         | 
| 89 | 
            +
            test_files: []
         |