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.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/cert-to-cwt.rb +193 -0
  3. data/cert-to-cwt.gemspec +19 -0
  4. 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
@@ -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
@@ -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: []