cert-to-cwt 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|