| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Crypt::ASN1; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
132648
|
use strict; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
122
|
|
|
4
|
2
|
|
|
2
|
|
26
|
use warnings; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
389
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.089'; |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
require Exporter; our @ISA = qw(Exporter); ### use Exporter 5.57 'import'; |
|
8
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( all => [qw( |
|
9
|
|
|
|
|
|
|
asn1_decode_der asn1_decode_pem asn1_decode_der_file asn1_decode_pem_file |
|
10
|
|
|
|
|
|
|
asn1_encode_der asn1_encode_pem asn1_encode_der_file asn1_encode_pem_file |
|
11
|
|
|
|
|
|
|
asn1_to_string |
|
12
|
|
|
|
|
|
|
)] ); |
|
13
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
|
14
|
|
|
|
|
|
|
our @EXPORT = qw(); |
|
15
|
|
|
|
|
|
|
|
|
16
|
2
|
|
|
2
|
|
16
|
use Carp 'croak'; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
174
|
|
|
17
|
2
|
|
|
2
|
|
15
|
use Config (); |
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
66
|
|
|
18
|
2
|
|
|
2
|
|
1017
|
use CryptX; |
|
|
2
|
|
|
|
|
9
|
|
|
|
2
|
|
|
|
|
99
|
|
|
19
|
2
|
|
|
2
|
|
1249
|
use Crypt::Misc qw(pem_to_der der_to_pem read_rawfile write_rawfile decode_b64); |
|
|
2
|
|
|
|
|
7
|
|
|
|
2
|
|
|
|
|
12429
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# --- decode (public) --- |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub asn1_decode_pem { |
|
24
|
4
|
|
|
4
|
1
|
9854
|
my $pem = shift; |
|
25
|
4
|
50
|
|
|
|
20
|
my $der = pem_to_der($pem) or croak "FATAL: asn1_decode_pem: failed to decode PEM data"; |
|
26
|
4
|
|
|
|
|
569
|
return asn1_decode_der($der, @_); |
|
27
|
|
|
|
|
|
|
} |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub asn1_decode_der_file { |
|
30
|
2
|
|
|
2
|
1
|
2491
|
my $file = shift; |
|
31
|
2
|
|
|
|
|
11
|
return asn1_decode_der(read_rawfile($file), @_); |
|
32
|
|
|
|
|
|
|
} |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub asn1_decode_pem_file { |
|
35
|
2
|
|
|
2
|
1
|
6252
|
my $file = shift; |
|
36
|
2
|
|
|
|
|
12
|
return asn1_decode_pem(read_rawfile($file), @_); |
|
37
|
|
|
|
|
|
|
} |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# --- encode (public) --- |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub asn1_encode_der { |
|
42
|
69
|
|
|
69
|
1
|
346084
|
my ($tree) = @_; |
|
43
|
69
|
50
|
|
|
|
335
|
croak "FATAL: asn1_encode_der: argument must be an arrayref" unless ref $tree eq 'ARRAY'; |
|
44
|
69
|
|
|
|
|
182
|
my $normalized = [ map { _normalize_node($_) } @$tree ]; |
|
|
69
|
|
|
|
|
192
|
|
|
45
|
42
|
|
|
|
|
909
|
return _asn1_encode_der($normalized); |
|
46
|
|
|
|
|
|
|
} |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub asn1_encode_pem { |
|
49
|
2
|
|
|
2
|
1
|
871
|
my ($tree, $header) = @_; |
|
50
|
2
|
50
|
|
|
|
11
|
$header = 'DATA' unless defined $header; |
|
51
|
2
|
|
|
|
|
7
|
my $der = asn1_encode_der($tree); |
|
52
|
2
|
|
|
|
|
25
|
return der_to_pem($der, $header); |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub asn1_encode_der_file { |
|
56
|
1
|
|
|
1
|
1
|
900
|
my ($tree, $file) = @_; |
|
57
|
1
|
|
|
|
|
4
|
my $der = asn1_encode_der($tree); |
|
58
|
1
|
|
|
|
|
9
|
write_rawfile($file, $der); |
|
59
|
1
|
|
|
|
|
5
|
return $der; |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub asn1_encode_pem_file { |
|
63
|
1
|
|
|
1
|
1
|
992
|
my ($tree, $header, $file) = @_; |
|
64
|
1
|
|
|
|
|
6
|
my $pem = asn1_encode_pem($tree, $header); |
|
65
|
1
|
|
|
|
|
6
|
write_rawfile($file, $pem); |
|
66
|
1
|
|
|
|
|
6
|
return $pem; |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# --- dump (public) --- |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub asn1_to_string { |
|
72
|
4
|
|
|
4
|
1
|
12525
|
my ($tree) = @_; |
|
73
|
4
|
50
|
|
|
|
18
|
croak "FATAL: asn1_to_string: argument must be an arrayref" unless ref $tree eq 'ARRAY'; |
|
74
|
4
|
|
|
|
|
11
|
my $out = ''; |
|
75
|
4
|
|
|
|
|
14
|
_dump_nodes(\$out, $tree, 0); |
|
76
|
4
|
|
|
|
|
19
|
return $out; |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
my %_LABEL = ( |
|
80
|
|
|
|
|
|
|
BOOLEAN => 'BOOLEAN', INTEGER => 'INTEGER', BIT_STRING => 'BIT STRING', |
|
81
|
|
|
|
|
|
|
OCTET_STRING => 'OCTET STRING', NULL => 'NULL', OID => 'OBJECT', |
|
82
|
|
|
|
|
|
|
UTF8_STRING => 'UTF8STRING', PRINTABLE_STRING => 'PRINTABLESTRING', |
|
83
|
|
|
|
|
|
|
TELETEX_STRING => 'TELETEXSTRING', IA5_STRING => 'IA5STRING', |
|
84
|
|
|
|
|
|
|
UTCTIME => 'UTCTIME', GENERALIZEDTIME => 'GENERALIZEDTIME', |
|
85
|
|
|
|
|
|
|
SEQUENCE => 'SEQUENCE', SET => 'SET', |
|
86
|
|
|
|
|
|
|
); |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub _dump_nodes { |
|
89
|
25
|
|
|
25
|
|
49
|
my ($out, $nodes, $depth) = @_; |
|
90
|
25
|
|
|
|
|
50
|
for my $node (@$nodes) { |
|
91
|
58
|
|
|
|
|
147
|
_dump_node($out, $node, $depth); |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub _dump_node { |
|
96
|
58
|
|
|
58
|
|
104
|
my ($out, $node, $depth) = @_; |
|
97
|
58
|
50
|
|
|
|
142
|
my $type = defined $node->{type} ? $node->{type} : '?'; |
|
98
|
58
|
|
|
|
|
111
|
my $val = $node->{value}; |
|
99
|
58
|
50
|
|
|
|
116
|
my $fmt = defined $node->{format} ? $node->{format} : ''; |
|
100
|
58
|
|
|
|
|
102
|
my $indent = ' ' x $depth; |
|
101
|
|
|
|
|
|
|
|
|
102
|
58
|
100
|
100
|
|
|
222
|
if ($type eq 'SEQUENCE' || $type eq 'SET') { |
|
103
|
18
|
50
|
|
|
|
45
|
my $n = ref $val eq 'ARRAY' ? scalar @$val : 0; |
|
104
|
18
|
|
|
|
|
52
|
$$out .= "${indent}$_LABEL{$type} ($n elem)\n"; |
|
105
|
18
|
50
|
|
|
|
64
|
_dump_nodes($out, $val, $depth + 1) if $n; |
|
106
|
18
|
|
|
|
|
71
|
return; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
|
|
109
|
40
|
100
|
|
|
|
83
|
if ($type eq 'CUSTOM') { |
|
110
|
3
|
50
|
|
|
|
13
|
my $cls = defined $node->{class} ? $node->{class} : 'CONTEXT_SPECIFIC'; |
|
111
|
3
|
50
|
|
|
|
10
|
my $tag = defined $node->{tag} ? $node->{tag} : 0; |
|
112
|
3
|
|
|
|
|
7
|
my $cons = $node->{constructed}; |
|
113
|
3
|
|
|
|
|
11
|
my $label = lc($cls) . " [$tag]"; |
|
114
|
3
|
50
|
33
|
|
|
15
|
if ($cons && ref $val eq 'ARRAY') { |
|
115
|
3
|
|
|
|
|
6
|
my $n = scalar @$val; |
|
116
|
3
|
|
|
|
|
11
|
$$out .= "${indent}$label cons ($n elem)\n"; |
|
117
|
3
|
|
|
|
|
9
|
_dump_nodes($out, $val, $depth + 1); |
|
118
|
|
|
|
|
|
|
} else { |
|
119
|
0
|
|
|
|
|
0
|
$$out .= "${indent}$label prim:${\ _dump_value_short($val, $fmt)}\n"; |
|
|
0
|
|
|
|
|
0
|
|
|
120
|
|
|
|
|
|
|
} |
|
121
|
3
|
|
|
|
|
9
|
return; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
37
|
50
|
|
|
|
118
|
my $label = exists $_LABEL{$type} ? $_LABEL{$type} : $type; |
|
125
|
37
|
|
|
|
|
53
|
$$out .= "${indent}${label}:${\ _dump_value($type, $val, $fmt, $node)}\n"; |
|
|
37
|
|
|
|
|
74
|
|
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub _dump_value { |
|
129
|
37
|
|
|
37
|
|
77
|
my ($type, $val, $fmt, $node) = @_; |
|
130
|
|
|
|
|
|
|
|
|
131
|
37
|
100
|
|
|
|
83
|
return '' if $type eq 'NULL'; |
|
132
|
|
|
|
|
|
|
|
|
133
|
33
|
100
|
|
|
|
89
|
if ($type eq 'BOOLEAN') { |
|
134
|
2
|
100
|
|
|
|
52
|
return $val ? 'TRUE' : 'FALSE'; |
|
135
|
|
|
|
|
|
|
} |
|
136
|
31
|
100
|
|
|
|
63
|
if ($type eq 'INTEGER') { |
|
137
|
4
|
50
|
|
|
|
10
|
return _dump_value_short($val, $fmt) if $fmt eq 'bytes'; |
|
138
|
4
|
50
|
|
|
|
16
|
my $s = defined $val ? "$val" : ''; |
|
139
|
4
|
50
|
|
|
|
29
|
return length($s) > 64 ? substr($s, 0, 61) . '...' : $s; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
27
|
100
|
|
|
|
56
|
if ($type eq 'OID') { |
|
142
|
10
|
50
|
|
|
|
27
|
my $s = defined $val ? $val : ''; |
|
143
|
10
|
100
|
|
|
|
27
|
$s .= " ($node->{name})" if defined $node->{name}; |
|
144
|
10
|
|
|
|
|
42
|
return $s; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
17
|
100
|
100
|
|
|
106
|
if ($type eq 'UTF8_STRING' || $type eq 'PRINTABLE_STRING' |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
147
|
|
|
|
|
|
|
|| $type eq 'IA5_STRING' || $type eq 'TELETEX_STRING') { |
|
148
|
6
|
50
|
|
|
|
14
|
my $s = defined $val ? $val : ''; |
|
149
|
6
|
50
|
|
|
|
33
|
return length($s) > 64 ? substr($s, 0, 61) . '...' : $s; |
|
150
|
|
|
|
|
|
|
} |
|
151
|
11
|
100
|
100
|
|
|
58
|
if ($type eq 'UTCTIME' || $type eq 'GENERALIZEDTIME') { |
|
152
|
4
|
50
|
|
|
|
24
|
return defined $val ? "$val" : ''; |
|
153
|
|
|
|
|
|
|
} |
|
154
|
7
|
50
|
66
|
|
|
26
|
if ($type eq 'OCTET_STRING' || $type eq 'BIT_STRING') { |
|
155
|
7
|
|
|
|
|
12
|
my $extra = ''; |
|
156
|
7
|
100
|
66
|
|
|
32
|
if ($type eq 'BIT_STRING' && defined $node->{bits}) { |
|
157
|
3
|
|
|
|
|
10
|
$extra = " ($node->{bits} bit)"; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
7
|
|
|
|
|
18
|
return _dump_value_short($val, $fmt) . $extra; |
|
160
|
|
|
|
|
|
|
} |
|
161
|
0
|
0
|
|
|
|
0
|
return defined $val ? "$val" : ''; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub _dump_value_short { |
|
165
|
7
|
|
|
7
|
|
17
|
my ($val, $fmt) = @_; |
|
166
|
7
|
50
|
33
|
|
|
30
|
return '' unless defined $val && length $val; |
|
167
|
7
|
|
|
|
|
13
|
my $hex; |
|
168
|
7
|
50
|
|
|
|
19
|
if ($fmt eq 'hex') { |
|
|
|
100
|
|
|
|
|
|
|
169
|
0
|
|
|
|
|
0
|
$hex = lc($val); |
|
170
|
|
|
|
|
|
|
} elsif ($fmt eq 'base64') { |
|
171
|
5
|
|
|
|
|
46
|
$hex = lc unpack("H*", decode_b64($val)); |
|
172
|
|
|
|
|
|
|
} else { |
|
173
|
2
|
|
|
|
|
11
|
$hex = lc unpack("H*", $val); |
|
174
|
|
|
|
|
|
|
} |
|
175
|
7
|
100
|
|
|
|
56
|
return length($hex) > 64 ? substr($hex, 0, 61) . '...' : $hex; |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# --- normalization (internal) --- |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
my %_CLASS_MAP = (UNIVERSAL => 0, APPLICATION => 1, CONTEXT_SPECIFIC => 2, PRIVATE => 3); |
|
181
|
|
|
|
|
|
|
my $_MAX_ULONG_DEC = _max_ulong_dec(); |
|
182
|
|
|
|
|
|
|
my $_MAX_OID_SECOND_ARC_DEC = _max_oid_second_arc_dec(); |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub _normalize_node { |
|
185
|
221
|
|
|
221
|
|
396
|
my ($node) = @_; |
|
186
|
221
|
50
|
|
|
|
630
|
croak "FATAL: asn1_encode: node must be a hashref" unless ref $node eq 'HASH'; |
|
187
|
221
|
50
|
|
|
|
653
|
my $type = defined $node->{type} ? $node->{type} : croak "FATAL: asn1_encode: node missing 'type'"; |
|
188
|
221
|
100
|
|
|
|
581
|
my $fmt = defined $node->{format} ? $node->{format} : ''; |
|
189
|
221
|
|
|
|
|
361
|
my $val = $node->{value}; |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# --- constructed types --- |
|
192
|
221
|
100
|
100
|
|
|
824
|
if ($type eq 'SEQUENCE' || $type eq 'SET') { |
|
193
|
63
|
50
|
|
|
|
141
|
croak "FATAL: asn1_encode: $type value must be arrayref" unless ref($val) eq 'ARRAY'; |
|
194
|
63
|
|
|
|
|
166
|
return { type => $type, value => [ map { _normalize_node($_) } @$val ] }; |
|
|
143
|
|
|
|
|
369
|
|
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
# --- INTEGER: always normalize to decimal string --- |
|
197
|
158
|
100
|
|
|
|
354
|
if ($type eq 'INTEGER') { |
|
198
|
25
|
50
|
|
|
|
61
|
croak "FATAL: asn1_encode: INTEGER missing value" unless defined $val; |
|
199
|
25
|
100
|
|
|
|
74
|
if ($fmt eq 'hex') { |
|
|
|
100
|
|
|
|
|
|
|
200
|
5
|
|
|
|
|
39
|
require Math::BigInt; |
|
201
|
5
|
|
|
|
|
11
|
my $hex = $val; |
|
202
|
5
|
|
|
|
|
20
|
my $neg = ($hex =~ s/^-//); |
|
203
|
5
|
|
|
|
|
74
|
my $bi = Math::BigInt->new("0x$hex"); |
|
204
|
5
|
100
|
|
|
|
2195
|
$bi->bneg() if $neg; |
|
205
|
5
|
|
|
|
|
253
|
$val = $bi->bstr(); |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
elsif ($fmt eq 'bytes') { |
|
208
|
3
|
|
|
|
|
1955
|
require Math::BigInt; |
|
209
|
3
|
|
|
|
|
63905
|
$val = Math::BigInt->new("0x" . unpack("H*", $val))->bstr(); |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
else { |
|
212
|
17
|
|
|
|
|
30
|
$val = "$val"; # stringify Perl number |
|
213
|
|
|
|
|
|
|
} |
|
214
|
25
|
|
|
|
|
43858
|
return { type => 'INTEGER', value => $val }; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
# --- BOOLEAN --- |
|
217
|
133
|
100
|
|
|
|
304
|
if ($type eq 'BOOLEAN') { |
|
218
|
4
|
100
|
|
|
|
31
|
return { type => 'BOOLEAN', value => $val ? 1 : 0 }; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
# --- NULL --- |
|
221
|
129
|
100
|
|
|
|
267
|
if ($type eq 'NULL') { |
|
222
|
11
|
|
|
|
|
107
|
return { type => 'NULL' }; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
# --- OID --- |
|
225
|
118
|
100
|
|
|
|
273
|
if ($type eq 'OID') { |
|
226
|
32
|
50
|
|
|
|
87
|
croak "FATAL: asn1_encode: OID missing value" unless defined $val; |
|
227
|
32
|
|
|
|
|
81
|
return { type => 'OID', value => _normalize_oid($val) }; |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
# --- OCTET_STRING: always normalize to raw bytes --- |
|
230
|
86
|
100
|
|
|
|
180
|
if ($type eq 'OCTET_STRING') { |
|
231
|
21
|
|
|
|
|
60
|
$val = _bin_to_raw($val, $fmt); |
|
232
|
17
|
|
|
|
|
142
|
return { type => 'OCTET_STRING', value => $val }; |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
# --- BIT_STRING: raw bytes + bits --- |
|
235
|
65
|
100
|
|
|
|
171
|
if ($type eq 'BIT_STRING') { |
|
236
|
14
|
|
|
|
|
35
|
$val = _bin_to_raw($val, $fmt); |
|
237
|
|
|
|
|
|
|
my $bits = exists $node->{bits} |
|
238
|
14
|
100
|
|
|
|
66
|
? _normalize_bit_count($node->{bits}, length($val)) |
|
239
|
|
|
|
|
|
|
: (length($val) * 8); |
|
240
|
11
|
|
|
|
|
89
|
return { type => 'BIT_STRING', value => $val, bits => $bits }; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
# --- string types --- |
|
243
|
51
|
100
|
|
|
|
119
|
if ($type eq 'UTF8_STRING') { |
|
244
|
8
|
50
|
|
|
|
120
|
return { type => 'UTF8_STRING', value => defined $val ? $val : '' }; |
|
245
|
|
|
|
|
|
|
} |
|
246
|
43
|
100
|
100
|
|
|
204
|
if ($type eq 'IA5_STRING' || $type eq 'PRINTABLE_STRING' || $type eq 'TELETEX_STRING') { |
|
|
|
|
100
|
|
|
|
|
|
247
|
5
|
50
|
|
|
|
38
|
return { type => $type, value => defined $val ? $val : '' }; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
# --- time types --- |
|
250
|
38
|
100
|
|
|
|
111
|
if ($type eq 'UTCTIME') { |
|
251
|
16
|
|
|
|
|
46
|
return { type => 'UTCTIME', value => _normalize_time($val, $fmt, 'utc') }; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
22
|
100
|
|
|
|
47
|
if ($type eq 'GENERALIZEDTIME') { |
|
254
|
3
|
|
|
|
|
9
|
return { type => 'GENERALIZEDTIME', value => _normalize_time($val, $fmt, 'gen') }; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
# --- CUSTOM --- |
|
257
|
19
|
50
|
|
|
|
40
|
if ($type eq 'CUSTOM') { |
|
258
|
19
|
50
|
|
|
|
55
|
my $class_name = defined $node->{class} ? $node->{class} : 'CONTEXT_SPECIFIC'; |
|
259
|
|
|
|
|
|
|
croak "FATAL: asn1_encode: invalid CUSTOM class '$class_name'" |
|
260
|
19
|
100
|
|
|
|
401
|
unless exists $_CLASS_MAP{$class_name}; |
|
261
|
17
|
|
|
|
|
37
|
my $cls = $_CLASS_MAP{$class_name}; |
|
262
|
17
|
100
|
|
|
|
43
|
my $constr = $node->{constructed} ? 1 : 0; |
|
263
|
17
|
50
|
|
|
|
60
|
my $tag = _normalize_custom_tag(defined $node->{tag} ? $node->{tag} : 0); |
|
264
|
13
|
|
|
|
|
66
|
my %n = (type => 'CUSTOM', class => $cls, constructed => $constr, tag => $tag); |
|
265
|
13
|
100
|
|
|
|
30
|
if ($constr) { |
|
266
|
11
|
100
|
|
|
|
180
|
croak "FATAL: asn1_encode: CUSTOM constructed value must be arrayref" |
|
267
|
|
|
|
|
|
|
unless ref($val) eq 'ARRAY'; |
|
268
|
10
|
|
|
|
|
24
|
$n{value} = [ map { _normalize_node($_) } @$val ]; |
|
|
9
|
|
|
|
|
26
|
|
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
else { |
|
271
|
2
|
100
|
|
|
|
155
|
croak "FATAL: asn1_encode: CUSTOM primitive value must not be a reference" |
|
272
|
|
|
|
|
|
|
if ref($val); |
|
273
|
1
|
|
|
|
|
4
|
$n{value} = _bin_to_raw($val, $fmt); |
|
274
|
|
|
|
|
|
|
} |
|
275
|
11
|
|
|
|
|
106
|
return \%n; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
0
|
|
|
|
|
0
|
croak "FATAL: asn1_encode: unsupported type '$type'"; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Convert binary value from hex/base64 to raw bytes |
|
281
|
|
|
|
|
|
|
sub _bin_to_raw { |
|
282
|
36
|
|
|
36
|
|
88
|
my ($val, $fmt) = @_; |
|
283
|
36
|
50
|
|
|
|
82
|
$val = '' unless defined $val; |
|
284
|
36
|
50
|
|
|
|
102
|
$fmt = '' unless defined $fmt; |
|
285
|
36
|
100
|
|
|
|
83
|
if ($fmt eq 'hex') { |
|
286
|
8
|
100
|
|
|
|
381
|
croak "FATAL: asn1_encode: invalid hex value" |
|
287
|
|
|
|
|
|
|
unless $val =~ /\A(?:[0-9A-Fa-f]{2})*\z/; |
|
288
|
6
|
|
|
|
|
69
|
return pack("H*", $val); |
|
289
|
|
|
|
|
|
|
} |
|
290
|
28
|
100
|
|
|
|
90
|
if ($fmt eq 'base64') { |
|
291
|
8
|
100
|
|
|
|
448
|
croak "FATAL: asn1_encode: invalid base64 value" |
|
292
|
|
|
|
|
|
|
unless $val =~ /\A(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?\z/; |
|
293
|
6
|
|
|
|
|
50
|
return decode_b64($val); |
|
294
|
|
|
|
|
|
|
} |
|
295
|
20
|
|
|
|
|
54
|
return $val; |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
sub _normalize_oid { |
|
299
|
32
|
|
|
32
|
|
73
|
my ($val) = @_; |
|
300
|
32
|
|
|
|
|
71
|
my $oid = "$val"; |
|
301
|
|
|
|
|
|
|
|
|
302
|
32
|
100
|
|
|
|
620
|
croak "FATAL: asn1_encode: invalid OID value '$oid'" |
|
303
|
|
|
|
|
|
|
unless $oid =~ /\A\d+(?:\.\d+)+\z/; |
|
304
|
|
|
|
|
|
|
|
|
305
|
30
|
|
|
|
|
145
|
my @arc = split /\./, $oid; |
|
306
|
30
|
50
|
|
|
|
85
|
croak "FATAL: asn1_encode: OID must have at least 2 arcs" |
|
307
|
|
|
|
|
|
|
unless @arc >= 2; |
|
308
|
30
|
50
|
33
|
|
|
176
|
croak "FATAL: asn1_encode: OID first arc must be 0, 1, or 2" |
|
309
|
|
|
|
|
|
|
unless $arc[0] >= 0 && $arc[0] <= 2; |
|
310
|
30
|
50
|
66
|
|
|
97
|
croak "FATAL: asn1_encode: OID second arc must be between 0 and 39 when first arc is 0 or 1" |
|
311
|
|
|
|
|
|
|
if $arc[0] < 2 && $arc[1] > 39; |
|
312
|
30
|
50
|
|
|
|
73
|
croak "FATAL: asn1_encode: OID has too many arcs (maximum 64)" |
|
313
|
|
|
|
|
|
|
if @arc > 64; |
|
314
|
|
|
|
|
|
|
|
|
315
|
30
|
|
|
|
|
132
|
for my $i (1 .. $#arc) { |
|
316
|
112
|
100
|
100
|
|
|
299
|
my $limit = ($i == 1 && $arc[0] == 2) ? $_MAX_OID_SECOND_ARC_DEC : $_MAX_ULONG_DEC; |
|
317
|
112
|
100
|
|
|
|
221
|
croak "FATAL: asn1_encode: OID arc '$arc[$i]' is too large for this encoder" |
|
318
|
|
|
|
|
|
|
unless _decimal_fits_limit($arc[$i], $limit); |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
28
|
|
|
|
|
190
|
return $oid; |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
sub _normalize_bit_count { |
|
325
|
13
|
|
|
13
|
|
29
|
my ($bits, $byte_len) = @_; |
|
326
|
13
|
|
|
|
|
23
|
my $max_bits = $byte_len * 8; |
|
327
|
|
|
|
|
|
|
|
|
328
|
13
|
50
|
|
|
|
27
|
croak "FATAL: asn1_encode: BIT_STRING bits missing" |
|
329
|
|
|
|
|
|
|
unless defined $bits; |
|
330
|
|
|
|
|
|
|
|
|
331
|
13
|
|
|
|
|
33
|
my $raw = "$bits"; |
|
332
|
13
|
100
|
|
|
|
400
|
croak "FATAL: asn1_encode: BIT_STRING bits must be a non-negative integer" |
|
333
|
|
|
|
|
|
|
unless $raw =~ /\A\d+\z/; |
|
334
|
|
|
|
|
|
|
|
|
335
|
11
|
|
|
|
|
29
|
$bits = int($raw); |
|
336
|
11
|
100
|
|
|
|
259
|
croak "FATAL: asn1_encode: BIT_STRING bits exceeds available data ($bits > $max_bits)" |
|
337
|
|
|
|
|
|
|
if $bits > $max_bits; |
|
338
|
|
|
|
|
|
|
|
|
339
|
10
|
|
|
|
|
26
|
return $bits; |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub _normalize_custom_tag { |
|
343
|
17
|
|
|
17
|
|
36
|
my ($tag) = @_; |
|
344
|
17
|
|
|
|
|
46
|
my $raw = "$tag"; |
|
345
|
|
|
|
|
|
|
|
|
346
|
17
|
100
|
|
|
|
15076
|
croak "FATAL: asn1_encode: CUSTOM tag must be a non-negative integer" |
|
347
|
|
|
|
|
|
|
unless $raw =~ /\A\d+\z/; |
|
348
|
14
|
100
|
|
|
|
37
|
croak "FATAL: asn1_encode: CUSTOM tag '$raw' is too large for this encoder" |
|
349
|
|
|
|
|
|
|
unless _decimal_fits_limit($raw, $_MAX_ULONG_DEC); |
|
350
|
|
|
|
|
|
|
|
|
351
|
13
|
|
|
|
|
38
|
return int($raw); |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
sub _decimal_fits_limit { |
|
355
|
126
|
|
|
126
|
|
238
|
my ($value, $limit) = @_; |
|
356
|
126
|
|
|
|
|
180
|
$value = "$value"; |
|
357
|
126
|
|
|
|
|
239
|
$value =~ s/\A0+(?=\d)//; |
|
358
|
126
|
100
|
|
|
|
417
|
return 1 if length($value) < length($limit); |
|
359
|
3
|
100
|
|
|
|
154
|
return 0 if length($value) > length($limit); |
|
360
|
2
|
|
|
|
|
326
|
return $value le $limit; |
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub _max_ulong_dec { |
|
364
|
2
|
|
50
|
2
|
|
186
|
my $bytes = $Config::Config{longsize} || 0; |
|
365
|
2
|
50
|
|
|
|
12
|
return '4294967295' if $bytes == 4; |
|
366
|
2
|
50
|
|
|
|
8
|
return '18446744073709551615' if $bytes == 8; |
|
367
|
|
|
|
|
|
|
|
|
368
|
0
|
|
|
|
|
0
|
require Math::BigInt; |
|
369
|
0
|
|
|
|
|
0
|
return Math::BigInt->new(2)->bpow($bytes * 8)->bsub(1)->bstr(); |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub _max_oid_second_arc_dec { |
|
373
|
2
|
|
50
|
2
|
|
14
|
my $bytes = $Config::Config{longsize} || 0; |
|
374
|
2
|
50
|
|
|
|
9
|
return '4294967215' if $bytes == 4; |
|
375
|
2
|
50
|
|
|
|
6
|
return '18446744073709551535' if $bytes == 8; |
|
376
|
|
|
|
|
|
|
|
|
377
|
0
|
|
|
|
|
0
|
require Math::BigInt; |
|
378
|
0
|
|
|
|
|
0
|
return Math::BigInt->new(_max_ulong_dec())->bsub(80)->bstr(); |
|
379
|
|
|
|
|
|
|
} |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# Normalize timestamp to ASN.1 wire format |
|
382
|
|
|
|
|
|
|
sub _normalize_time { |
|
383
|
19
|
|
|
19
|
|
57
|
my ($val, $fmt, $kind) = @_; |
|
384
|
19
|
50
|
|
|
|
41
|
croak "FATAL: asn1_encode: time value missing" unless defined $val; |
|
385
|
19
|
100
|
|
|
|
48
|
my $type = $kind eq 'utc' ? 'UTCTIME' : 'GENERALIZEDTIME'; |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# epoch (all digits, possibly negative) |
|
388
|
19
|
100
|
66
|
|
|
173
|
if ($fmt eq 'epoch' || ($fmt eq '' && $val =~ /^-?\d+$/)) { |
|
|
|
|
66
|
|
|
|
|
|
389
|
5
|
|
|
|
|
26
|
my @t = gmtime($val); |
|
390
|
5
|
|
|
|
|
15
|
my $year = $t[5] + 1900; |
|
391
|
5
|
50
|
|
|
|
44
|
if ($kind eq 'utc') { |
|
392
|
5
|
100
|
66
|
|
|
218
|
croak "FATAL: asn1_encode: UTCTIME year out of range: $year (expected 1950..2049)" |
|
393
|
|
|
|
|
|
|
if $year < 1950 || $year > 2049; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
4
|
50
|
|
|
|
58
|
return ($kind eq 'utc') |
|
396
|
|
|
|
|
|
|
? sprintf("%02d%02d%02d%02d%02d%02dZ", $t[5] % 100, $t[4]+1, $t[3], $t[2], $t[1], $t[0]) |
|
397
|
|
|
|
|
|
|
: sprintf("%04d%02d%02d%02d%02d%02dZ", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]); |
|
398
|
|
|
|
|
|
|
} |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
# RFC 3339 (e.g. "2024-01-15T10:30:00Z" or "2024-01-15T10:30:00.5+05:30") |
|
401
|
14
|
100
|
100
|
|
|
130
|
if ($fmt eq 'rfc3339' || $val =~ /^\d{4}-/) { |
|
402
|
10
|
50
|
|
|
|
94
|
$val =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(Z|([+-])(\d{2}):(\d{2}))$/ |
|
403
|
|
|
|
|
|
|
or croak "FATAL: asn1_encode: invalid RFC 3339 time for $type: $val"; |
|
404
|
10
|
|
|
|
|
93
|
my ($YYYY,$MM,$DD,$hh,$mm,$ss,$fs,$tz,$sign,$oh,$om) = ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11); |
|
405
|
10
|
|
|
|
|
16
|
my $r; |
|
406
|
10
|
100
|
|
|
|
24
|
if ($kind eq 'utc') { |
|
407
|
9
|
100
|
66
|
|
|
179
|
croak "FATAL: asn1_encode: UTCTIME does not allow fractional seconds: $val" |
|
408
|
|
|
|
|
|
|
if defined $fs && length $fs; |
|
409
|
8
|
100
|
100
|
|
|
348
|
croak "FATAL: asn1_encode: UTCTIME year out of range: $YYYY (expected 1950..2049)" |
|
410
|
|
|
|
|
|
|
if $YYYY < 1950 || $YYYY > 2049; |
|
411
|
6
|
50
|
|
|
|
17
|
my $YY = ($YYYY >= 2000) ? $YYYY - 2000 : $YYYY - 1900; |
|
412
|
6
|
|
|
|
|
36
|
$r = sprintf "%02d%02d%02d%02d%02d%02d", $YY, $MM, $DD, $hh, $mm, $ss; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
else { |
|
415
|
1
|
|
|
|
|
9
|
$r = sprintf "%04d%02d%02d%02d%02d%02d", $YYYY, $MM, $DD, $hh, $mm, $ss; |
|
416
|
1
|
50
|
33
|
|
|
11
|
$r .= ".$fs" if defined $fs && $fs > 0; |
|
417
|
|
|
|
|
|
|
} |
|
418
|
7
|
50
|
|
|
|
22
|
$r .= ($tz eq 'Z') ? 'Z' : sprintf("%s%02d%02d", $sign, $oh, $om); |
|
419
|
7
|
|
|
|
|
58
|
return $r; |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
|
|
422
|
4
|
|
|
|
|
688
|
croak "FATAL: asn1_encode: invalid $type value '$val' (expected RFC 3339 string or epoch)"; |
|
423
|
|
|
|
|
|
|
} |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
1; |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=pod |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=head1 NAME |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
Crypt::ASN1 - DER ASN.1 parser and encoder based on libtomcrypt |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
use Crypt::ASN1 qw(asn1_decode_der asn1_encode_der asn1_to_string); |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# --- decode --- |
|
438
|
|
|
|
|
|
|
my $tree = asn1_decode_der($der_bytes); |
|
439
|
|
|
|
|
|
|
my $tree = asn1_decode_der($der_bytes, { int => 'hex', bin => 'hex' }); |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
# --- inspect --- |
|
442
|
|
|
|
|
|
|
print asn1_to_string($tree); |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# --- encode a decoded tree --- |
|
445
|
|
|
|
|
|
|
my $der2 = asn1_encode_der($tree); |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# --- build from scratch --- |
|
448
|
|
|
|
|
|
|
my $der = asn1_encode_der([{ |
|
449
|
|
|
|
|
|
|
type => 'SEQUENCE', |
|
450
|
|
|
|
|
|
|
value => [ |
|
451
|
|
|
|
|
|
|
{ type => 'INTEGER', value => '42' }, |
|
452
|
|
|
|
|
|
|
{ type => 'BOOLEAN', value => 1 }, |
|
453
|
|
|
|
|
|
|
{ type => 'OID', value => '1.2.840.113549.1.1.11' }, |
|
454
|
|
|
|
|
|
|
{ type => 'UTF8_STRING', value => 'hello' }, |
|
455
|
|
|
|
|
|
|
{ type => 'OCTET_STRING', value => "\x00\x01\x02" }, |
|
456
|
|
|
|
|
|
|
{ type => 'BIT_STRING', value => "\x03\x02\x01", bits => 20 }, |
|
457
|
|
|
|
|
|
|
{ type => 'NULL' }, |
|
458
|
|
|
|
|
|
|
{ type => 'UTCTIME', value => '2025-06-15T12:00:00Z' }, |
|
459
|
|
|
|
|
|
|
{ type => 'CUSTOM', class => 'CONTEXT_SPECIFIC', |
|
460
|
|
|
|
|
|
|
constructed => 1, tag => 0, |
|
461
|
|
|
|
|
|
|
value => [{ type => 'INTEGER', value => '2' }] }, |
|
462
|
|
|
|
|
|
|
], |
|
463
|
|
|
|
|
|
|
}]); |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
I |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Parses DER-encoded ASN.1 data into a Perl data structure without requiring |
|
470
|
|
|
|
|
|
|
any schema, and encodes Perl data structures back to DER. |
|
471
|
|
|
|
|
|
|
Uses libtomcrypt's C for decoding. |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
Both the decoder output and the encoder input use the same B |
|
474
|
|
|
|
|
|
|
structure described below. When given a tree produced by the decoder, the |
|
475
|
|
|
|
|
|
|
encoder does its best to produce the same ASN.1 that was originally parsed, |
|
476
|
|
|
|
|
|
|
regardless of what decode options were used. |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=head1 EXPORT |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
Nothing is exported by default. |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
You can export selected functions: |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
use Crypt::ASN1 qw(asn1_decode_der asn1_encode_der); |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
Or all of them at once: |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
use Crypt::ASN1 ':all'; |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=head1 NODE HASH STRUCTURE |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
Both the decoder and encoder operate on the same data structure: an B
|
|
493
|
|
|
|
|
|
|
of node hashrefs>. Each hashref represents one ASN.1 TLV (Tag-Length-Value) |
|
494
|
|
|
|
|
|
|
element. |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=head2 Common keys |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Every node has three keys: |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=over |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=item C (string, required) |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
The ASN.1 type name. Built-in values include: |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
BOOLEAN INTEGER NULL OID |
|
507
|
|
|
|
|
|
|
OCTET_STRING BIT_STRING UTF8_STRING |
|
508
|
|
|
|
|
|
|
PRINTABLE_STRING IA5_STRING TELETEX_STRING |
|
509
|
|
|
|
|
|
|
UTCTIME GENERALIZEDTIME |
|
510
|
|
|
|
|
|
|
SEQUENCE SET CUSTOM |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
The list above is not exhaustive for decoded input. If the decoder encounters |
|
513
|
|
|
|
|
|
|
an ASN.1 tag that does not map to one of the built-in type names above, it is |
|
514
|
|
|
|
|
|
|
returned as C with the appropriate C, C, and |
|
515
|
|
|
|
|
|
|
C fields. This includes unsupported universal tags such as |
|
516
|
|
|
|
|
|
|
C, which decode as C with C<< class => "UNIVERSAL" >>. |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=item C (varies, required for most types) |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
The decoded value. Its Perl type depends on C and sometimes on the |
|
521
|
|
|
|
|
|
|
C key -- see L below. |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=item C (string, decoder sets it, encoder reads it) |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
Tells the encoder how the C is represented so it can convert it back |
|
526
|
|
|
|
|
|
|
to DER. Set automatically by the decoder; when building nodes from scratch |
|
527
|
|
|
|
|
|
|
you may omit it -- the encoder then assumes the default representation for |
|
528
|
|
|
|
|
|
|
each type. |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=back |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=head2 Per-type details |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
Each subsection below documents one C. For types where the C |
|
535
|
|
|
|
|
|
|
representation depends on the decode option used, a B lists |
|
536
|
|
|
|
|
|
|
every C/C combination. B
|
|
537
|
|
|
|
|
|
|
combination shown> -- it reads C and converts C back to DER |
|
538
|
|
|
|
|
|
|
automatically. |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=head3 BOOLEAN |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
B: C, C, C. |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
C is C<1> (true) or C<0> (false). C is always C<"bool">. |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
{ type => "BOOLEAN", format => "bool", value => 1 } |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=head3 INTEGER |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
B: C, C, C. |
|
551
|
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
C is an arbitrary-precision signed integer. C describes the |
|
553
|
|
|
|
|
|
|
representation: |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
format value decode option example |
|
556
|
|
|
|
|
|
|
-------- --------------------------- ---------------- --------------- |
|
557
|
|
|
|
|
|
|
decimal decimal string (default) "255" |
|
558
|
|
|
|
|
|
|
hex lowercase hex string int => 'hex' "ff" |
|
559
|
|
|
|
|
|
|
bytes big-endian binary string int => 'bytes' "\xff" |
|
560
|
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
All three forms are accepted by the encoder. When C is absent the |
|
562
|
|
|
|
|
|
|
encoder treats C as a decimal string (a Perl integer is fine too). |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
Negative integers: C and C carry a leading C<-> (e.g. C<"-5">). |
|
565
|
|
|
|
|
|
|
C stores the unsigned magnitude only and is intended for naturally |
|
566
|
|
|
|
|
|
|
unsigned values such as RSA moduli. When decoding with C<< int => 'bytes' >>, |
|
567
|
|
|
|
|
|
|
negative ASN.1 INTEGER values are rejected. |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=head3 NULL |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
B: C, C, C. |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
C is always C. C is always C<"null">. The encoder |
|
574
|
|
|
|
|
|
|
ignores C. |
|
575
|
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
{ type => "NULL", format => "null", value => undef } |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=head3 OID |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
B: C, C, C, and optionally C. |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
C is a dotted-decimal OID string (at least two arcs). C is |
|
583
|
|
|
|
|
|
|
always C<"oid">. |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
{ type => "OID", format => "oid", value => "1.2.840.113549.1.1.11" } |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
As a convenience, the encoder accepts textual arcs with leading zeros and |
|
588
|
|
|
|
|
|
|
lets DER encoding canonicalize them. For example, C<"2.000.1"> encodes and |
|
589
|
|
|
|
|
|
|
decodes back as C<"2.0.1">. |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
B: C -- present only when the C decode option |
|
592
|
|
|
|
|
|
|
is supplied and the OID is found in the map. Ignored by the encoder. |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
{ ..., name => "sha256WithRSAEncryption" } # when oidmap matches |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=head3 OCTET_STRING |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
B: C, C, C. |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
C is binary data. C describes the representation: |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
format value decode option example |
|
603
|
|
|
|
|
|
|
-------- --------------------------- ----------------- -------- |
|
604
|
|
|
|
|
|
|
bytes raw binary string (default) "\x04\x01" |
|
605
|
|
|
|
|
|
|
hex lowercase hex string bin => 'hex' "0401" |
|
606
|
|
|
|
|
|
|
base64 Base64-encoded string bin => 'base64' "BAE=" |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
All three forms are accepted by the encoder. When C is absent the |
|
609
|
|
|
|
|
|
|
encoder treats C as raw bytes. |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=head3 BIT_STRING |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
B: C, C, C, C. |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
C is the packed bit data (MSB-first). C follows the same |
|
616
|
|
|
|
|
|
|
rules as C (C<"bytes">, C<"hex">, or C<"base64">). All three |
|
617
|
|
|
|
|
|
|
forms are accepted by the encoder. |
|
618
|
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
C is the exact number of significant bits. The quantity |
|
620
|
|
|
|
|
|
|
C<< 8 * byte_length(value) - bits >> gives the number of unused trailing |
|
621
|
|
|
|
|
|
|
bits in the last byte. |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
When C is absent the encoder treats C as raw bytes. When |
|
624
|
|
|
|
|
|
|
C is absent it defaults to C<< 8 * length(value) >> (no unused bits). |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# default format (raw bytes, 25 significant bits) |
|
627
|
|
|
|
|
|
|
{ type => "BIT_STRING", format => "bytes", |
|
628
|
|
|
|
|
|
|
value => "\x03\x02\x01\x00", bits => 25 } |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# hex format |
|
631
|
|
|
|
|
|
|
{ type => "BIT_STRING", format => "hex", |
|
632
|
|
|
|
|
|
|
value => "03020100", bits => 25 } |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=head3 UTF8_STRING |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
B: C, C, C. |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
C is a Perl Unicode string (C flag on). C is always |
|
639
|
|
|
|
|
|
|
C<"utf8">. |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
{ type => "UTF8_STRING", format => "utf8", value => "caf\x{e9}" } |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=head3 PRINTABLE_STRING, IA5_STRING, TELETEX_STRING |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
B: C, C, C. |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
C is a byte string. C is always C<"string"> for all three. |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
{ type => "PRINTABLE_STRING", format => "string", value => "abc" } |
|
650
|
|
|
|
|
|
|
{ type => "IA5_STRING", format => "string", value => "ia5" } |
|
651
|
|
|
|
|
|
|
{ type => "TELETEX_STRING", format => "string", value => "tele" } |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
=head3 UTCTIME |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
B: C, C, C. |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
C is a timestamp. C describes the representation: |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
format value decode option example |
|
660
|
|
|
|
|
|
|
-------- ----------------------------- --------------- ----------------------- |
|
661
|
|
|
|
|
|
|
rfc3339 RFC 3339 string (default) "2024-01-15T10:30:00Z" |
|
662
|
|
|
|
|
|
|
epoch Unix timestamp (integer) dt => 'epoch' 1705314600 |
|
663
|
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
Both forms are accepted by the encoder. When C is absent, the |
|
665
|
|
|
|
|
|
|
encoder auto-detects: an all-digit value is treated as epoch, a value |
|
666
|
|
|
|
|
|
|
matching C is treated as RFC 3339. |
|
667
|
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
For C, encoder input must fall within the UTCTime year window |
|
669
|
|
|
|
|
|
|
C<1950..2049>; values outside that range are rejected. Fractional seconds |
|
670
|
|
|
|
|
|
|
are also rejected for C. |
|
671
|
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
Time validation in the encoder is currently B, not full calendar |
|
673
|
|
|
|
|
|
|
validation. The encoder checks the accepted input shape and ASN.1-specific |
|
674
|
|
|
|
|
|
|
constraints above, but it does not verify that every RFC 3339-looking date |
|
675
|
|
|
|
|
|
|
and time is semantically valid. |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
The decoder expands the 2-digit UTCTime year using the RFC 5280 window |
|
678
|
|
|
|
|
|
|
(YY E= 50 E 19YY, else 20YY). Timezone offsets are preserved |
|
679
|
|
|
|
|
|
|
(e.g. C<"2024-01-15T10:30:00+05:30">). |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=head3 GENERALIZEDTIME |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
B: C, C, C. |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
Same C rules as C; both forms are accepted by the encoder. |
|
686
|
|
|
|
|
|
|
Fractional seconds are preserved (e.g. C<"2024-01-15T10:30:00.125Z">). |
|
687
|
|
|
|
|
|
|
Validation is likewise syntactic only; semantically invalid calendar values |
|
688
|
|
|
|
|
|
|
that match the accepted timestamp syntax are not currently rejected. |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=head3 SEQUENCE |
|
691
|
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
B: C, C, C. |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
C is an arrayref of child node hashrefs (in order). C is |
|
695
|
|
|
|
|
|
|
always C<"array">. |
|
696
|
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
{ type => "SEQUENCE", format => "array", value => [ ...children... ] } |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=head3 SET |
|
700
|
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
B: C, C, C. |
|
702
|
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
Same structure as C. C is always C<"array">. Both |
|
704
|
|
|
|
|
|
|
ASN.1 SET and SET OF are represented as C "SET"> (they share |
|
705
|
|
|
|
|
|
|
the same DER tag C<0x31>). |
|
706
|
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
=head3 CUSTOM |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
Represents any tag that does not map to one of the built-in type names above. |
|
710
|
|
|
|
|
|
|
This is commonly used for context-specific implicit/explicit tags (C<[0]>, |
|
711
|
|
|
|
|
|
|
C<[1]>, ...) found in X.509 certificates and other ASN.1 schemas, but it can |
|
712
|
|
|
|
|
|
|
also be emitted by the decoder for unsupported universal tags. |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
B: C, C, C, C, C, C. |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
=over |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
=item C (string) -- C<"CONTEXT_SPECIFIC">, C<"APPLICATION">, C<"UNIVERSAL">, or C<"PRIVATE"> |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
=item C (integer) -- C<1> if constructed, C<0> if primitive |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
=item C (integer) -- the tag number (e.g. C<0> for C<[0]>) |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
Must be a non-negative integer within the range supported by the current |
|
725
|
|
|
|
|
|
|
encoder build. |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=back |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
B (C<< constructed => 1 >>): C is an arrayref of child |
|
730
|
|
|
|
|
|
|
nodes. C is C<"array">. |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
{ type => "CUSTOM", format => "array", |
|
733
|
|
|
|
|
|
|
class => "CONTEXT_SPECIFIC", constructed => 1, tag => 0, |
|
734
|
|
|
|
|
|
|
value => [ { type => "INTEGER", ... } ] } |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
B (C<< constructed => 0 >>): C is raw data. C |
|
737
|
|
|
|
|
|
|
follows the same rules as C (C<"bytes">, C<"hex">, or |
|
738
|
|
|
|
|
|
|
C<"base64"> depending on the C decode option). All three forms are |
|
739
|
|
|
|
|
|
|
accepted by the encoder. Primitive C values must not be references. |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
# default format |
|
742
|
|
|
|
|
|
|
{ type => "CUSTOM", format => "bytes", |
|
743
|
|
|
|
|
|
|
class => "CONTEXT_SPECIFIC", constructed => 0, tag => 1, |
|
744
|
|
|
|
|
|
|
value => "\xAA\xBB" } |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
# hex format (bin => 'hex') |
|
747
|
|
|
|
|
|
|
{ type => "CUSTOM", format => "hex", |
|
748
|
|
|
|
|
|
|
class => "CONTEXT_SPECIFIC", constructed => 0, tag => 1, |
|
749
|
|
|
|
|
|
|
value => "aabb" } |
|
750
|
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
=head2 Re-encoding Decoded Trees |
|
752
|
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
The encoder reads C and converts C back to DER before |
|
754
|
|
|
|
|
|
|
encoding. When given a tree returned by C, it does its best |
|
755
|
|
|
|
|
|
|
to produce the same ASN.1 that was originally parsed, regardless of the |
|
756
|
|
|
|
|
|
|
decode options used: |
|
757
|
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
my $tree = asn1_decode_der($der, { int=>'hex', bin=>'base64', dt=>'epoch' }); |
|
759
|
|
|
|
|
|
|
my $der2 = asn1_encode_der($tree); |
|
760
|
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
=head2 Building nodes from scratch |
|
762
|
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
When constructing nodes by hand you need C and C (plus the |
|
764
|
|
|
|
|
|
|
extra keys noted above for C and C). You may omit |
|
765
|
|
|
|
|
|
|
C; the encoder assumes: |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
Type default value interpretation |
|
768
|
|
|
|
|
|
|
---------------- ------------------------------------------ |
|
769
|
|
|
|
|
|
|
INTEGER decimal string or Perl integer |
|
770
|
|
|
|
|
|
|
OCTET_STRING raw bytes |
|
771
|
|
|
|
|
|
|
BIT_STRING raw packed bytes, bits = length(value) * 8 |
|
772
|
|
|
|
|
|
|
UTCTIME RFC 3339 string (or all-digit epoch) |
|
773
|
|
|
|
|
|
|
GENERALIZEDTIME RFC 3339 string (or all-digit epoch) |
|
774
|
|
|
|
|
|
|
CUSTOM primitive raw bytes |
|
775
|
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
You may also supply C explicitly if you prefer to work with hex |
|
777
|
|
|
|
|
|
|
or base64 representations: |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
# these two produce identical DER |
|
780
|
|
|
|
|
|
|
{ type => "OCTET_STRING", value => "\x04\x01" } |
|
781
|
|
|
|
|
|
|
{ type => "OCTET_STRING", format => "hex", value => "0401" } |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
784
|
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
=head2 asn1_decode_der |
|
786
|
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
my $tree = asn1_decode_der($der_bytes); |
|
788
|
|
|
|
|
|
|
my $tree = asn1_decode_der($der_bytes, \%opts); |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
Parses C<$der_bytes> and returns an arrayref of top-level node hashrefs. |
|
791
|
|
|
|
|
|
|
Croaks on parse error. |
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
The optional C<%opts> hashref controls value formatting: |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=over |
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
=item C 'hex' | 'bytes'> |
|
798
|
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
How to represent C values. Default is a decimal string |
|
800
|
|
|
|
|
|
|
(C<< format=>"decimal" >>). |
|
801
|
|
|
|
|
|
|
C<'hex'> gives a lowercase hex string (C<< format=>"hex" >>). |
|
802
|
|
|
|
|
|
|
C<'bytes'> gives a raw big-endian binary string (C<< format=>"bytes" >>) for |
|
803
|
|
|
|
|
|
|
non-negative INTEGER values only; decoding croaks if the DER INTEGER is |
|
804
|
|
|
|
|
|
|
negative. |
|
805
|
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
=item C 'hex' | 'base64'> |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
How to represent C, C, and primitive C |
|
809
|
|
|
|
|
|
|
values. Default is raw binary bytes (C<< format=>"bytes" >>). |
|
810
|
|
|
|
|
|
|
C<'hex'> gives a lowercase hex string (C<< format=>"hex" >>). |
|
811
|
|
|
|
|
|
|
C<'base64'> gives a Base64-encoded string (C<< format=>"base64" >>). |
|
812
|
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
=item C 'epoch'> |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
How to represent C and C values. Default is an |
|
816
|
|
|
|
|
|
|
RFC 3339 string (C<< format=>"rfc3339" >>). |
|
817
|
|
|
|
|
|
|
C<'epoch'> gives a Unix timestamp integer (C<< format=>"epoch" >>). |
|
818
|
|
|
|
|
|
|
This works reliably only on Perls with 64-bit integers; on 32-bit integer |
|
819
|
|
|
|
|
|
|
Perls, large timestamps may overflow or lose precision. |
|
820
|
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=item C \%map> |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
A hashref mapping dotted OID strings to friendly names. When a decoded |
|
824
|
|
|
|
|
|
|
C node's value exists as a key in C<%map>, the node gets an additional |
|
825
|
|
|
|
|
|
|
C key with the mapped value. Does not affect encoding. |
|
826
|
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
=back |
|
828
|
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
=head2 asn1_decode_pem |
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
my $tree = asn1_decode_pem($pem_string); |
|
832
|
|
|
|
|
|
|
my $tree = asn1_decode_pem($pem_string, \%opts); |
|
833
|
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
Decodes the PEM envelope first (via L), then parses |
|
835
|
|
|
|
|
|
|
the resulting DER bytes. Accepts the same C<%opts> as C. |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
=head2 asn1_decode_der_file |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
my $tree = asn1_decode_der_file($filename); |
|
840
|
|
|
|
|
|
|
my $tree = asn1_decode_der_file($filename, \%opts); |
|
841
|
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
Reads C<$filename> as raw binary and parses it as DER. |
|
843
|
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
=head2 asn1_decode_pem_file |
|
845
|
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
my $tree = asn1_decode_pem_file($filename); |
|
847
|
|
|
|
|
|
|
my $tree = asn1_decode_pem_file($filename, \%opts); |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
Reads C<$filename>, decodes the PEM envelope, then parses the DER bytes. |
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
=head2 asn1_encode_der |
|
852
|
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
my $der_bytes = asn1_encode_der($tree); |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
Encodes C<$tree> (an arrayref of node hashrefs) to DER bytes. The input |
|
856
|
|
|
|
|
|
|
may be a tree previously returned by C or one |
|
857
|
|
|
|
|
|
|
built from scratch. Croaks on invalid input. |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
The encoder normalizes every node before encoding: it reads C (if |
|
860
|
|
|
|
|
|
|
present) to determine how to interpret C, converts it to the canonical |
|
861
|
|
|
|
|
|
|
DER form, and encodes it. |
|
862
|
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
The current low-level encoder supports element content lengths up to |
|
864
|
|
|
|
|
|
|
C<0xffffffff> bytes; larger values are rejected. |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
=head2 asn1_encode_pem |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
my $pem_string = asn1_encode_pem($tree, $header); |
|
869
|
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
Encodes C<$tree> to DER, then wraps in a PEM envelope with the given |
|
871
|
|
|
|
|
|
|
C<$header> (e.g. C<"CERTIFICATE">, C<"RSA PRIVATE KEY">). Defaults to |
|
872
|
|
|
|
|
|
|
C<"DATA"> if C<$header> is omitted. |
|
873
|
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
=head2 asn1_encode_der_file |
|
875
|
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
asn1_encode_der_file($tree, $filename); |
|
877
|
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
Encodes C<$tree> to DER and writes it to C<$filename>. |
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
=head2 asn1_encode_pem_file |
|
881
|
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
asn1_encode_pem_file($tree, $header, $filename); |
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
Encodes C<$tree> to PEM and writes it to C<$filename>. |
|
885
|
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=head2 asn1_to_string |
|
887
|
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
my $text = asn1_to_string($tree); |
|
889
|
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
Returns a human-readable text representation of C<$tree> (an arrayref of |
|
891
|
|
|
|
|
|
|
node hashrefs as returned by any C function). Useful for |
|
892
|
|
|
|
|
|
|
debugging and inspection, similar to C output. |
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
print asn1_to_string(asn1_decode_pem_file("cert.pem")); |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
produces output like: |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
SEQUENCE (3 elem) |
|
899
|
|
|
|
|
|
|
SEQUENCE (8 elem) |
|
900
|
|
|
|
|
|
|
context_specific [0] cons (1 elem) |
|
901
|
|
|
|
|
|
|
INTEGER:2 |
|
902
|
|
|
|
|
|
|
INTEGER:17923815188543234454 |
|
903
|
|
|
|
|
|
|
SEQUENCE (2 elem) |
|
904
|
|
|
|
|
|
|
OBJECT:1.2.840.113549.1.1.11 |
|
905
|
|
|
|
|
|
|
NULL: |
|
906
|
|
|
|
|
|
|
... |
|
907
|
|
|
|
|
|
|
BIT STRING:3082010a0282010100c242299a49420c21dcf9b957afcdc49... (2160 bit) |
|
908
|
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
Binary values (C, C, primitive C) are |
|
910
|
|
|
|
|
|
|
shown as lowercase hex, truncated to 64 characters with C<...> for longer |
|
911
|
|
|
|
|
|
|
values. C additionally shows the bit count in parentheses. |
|
912
|
|
|
|
|
|
|
C nodes that have a C key (via C) show the name in |
|
913
|
|
|
|
|
|
|
parentheses after the dotted value. |
|
914
|
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
The function handles trees decoded with any combination of decode options |
|
916
|
|
|
|
|
|
|
(C, C, C). |
|
917
|
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
919
|
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
L, L |
|
921
|
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
=cut |