File Coverage

blib/lib/Web/ID/Certificate.pm
Criterion Covered Total %
statement 21 23 91.3
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 29 31 93.5


line stmt bran cond sub pod time code
1             package Web::ID::Certificate;
2              
3 1     1   27040 use 5.010;
  1         4  
  1         55  
4 1     1   1091 use utf8;
  1         12  
  1         6  
5              
6             BEGIN {
7 1     1   86 $Web::ID::Certificate::AUTHORITY = 'cpan:TOBYINK';
8 1         24 $Web::ID::Certificate::VERSION = '1.927';
9             }
10              
11 1     1   17315 use Crypt::X509 0.50 (); # why the hell does this export anything?!
  1         179592  
  1         37  
12 1     1   1495 use DateTime 0;
  1         435859  
  1         63  
13 1     1   6449 use Digest::SHA qw(sha1_hex);
  1         4427  
  1         151  
14 1     1   1084 use MIME::Base64 0 qw(decode_base64);
  1         10768  
  1         126  
15 1     1   4947 use Web::ID::Types -types;
  0            
  0            
16             use Web::ID::SAN;
17             use Web::ID::SAN::Email;
18             use Web::ID::SAN::URI;
19             use Web::ID::Util qw(:default part);
20              
21             # Partly sorts a list of Web::ID::SAN objects,
22             # prioritising URIs and Email addresses.
23             #
24             sub _sort_san
25             {
26             map { ref($_) eq 'ARRAY' ? (@$_) : () }
27             part {
28             if ($_->isa("Web::ID::SAN::URI")) { 0 }
29             elsif ($_->isa("Web::ID::SAN::Email")) { 1 }
30             else { 2 }
31             }
32             @_;
33             }
34              
35             use Moose;
36             use namespace::sweep -also => '_sort_san';
37              
38             has pem => (
39             is => read_only,
40             isa => Str,
41             required => true,
42             coerce => false,
43             );
44              
45             has _der => (
46             is => read_only,
47             isa => Str,
48             required => true,
49             lazy_build => true,
50             );
51              
52             has _x509 => (
53             is => read_only,
54             isa => Type::Utils::class_type({ class => "Crypt::X509" }),
55             lazy_build => true,
56             );
57              
58             has public_key => (
59             is => read_only,
60             isa => Rsakey,
61             lazy_build => true,
62             handles => [qw(modulus exponent)],
63             );
64              
65             has subject_alt_names => (
66             is => read_only,
67             isa => ArrayRef,
68             lazy_build => true,
69             );
70              
71             has $_ => (
72             is => read_only,
73             isa => DateTime,
74             lazy_build => true,
75             coerce => true,
76             ) for qw( not_before not_after );
77              
78             has san_factory => (
79             is => read_only,
80             isa => CodeRef,
81             lazy_build => true,
82             );
83              
84             has fingerprint => (
85             is => read_only,
86             isa => Str,
87             lazy_build => true,
88             );
89              
90             sub _build_fingerprint
91             {
92             lc sha1_hex( shift->_der );
93             }
94              
95             sub _build__der
96             {
97             my @lines = split /\n/, shift->pem;
98             decode_base64(join "\n", grep { !/--(BEGIN|END) CERTIFICATE--/ } @lines);
99             }
100              
101             sub _build__x509
102             {
103             return "Crypt::X509"->new(cert => shift->_der);
104             }
105              
106             sub _build_public_key
107             {
108             my ($self) = @_;
109             Rsakey->new($self->_x509->pubkey_components);
110             }
111              
112             sub _build_subject_alt_names
113             {
114             my ($self) = @_;
115             my $factory = $self->san_factory;
116              
117             [_sort_san(
118             map {
119             my ($type, $value) = split /=/, $_, 2;
120             $factory->(type => $type, value => $value);
121             }
122             @{ $self->_x509->SubjectAltName }
123             )];
124             }
125              
126             sub _build_not_before
127             {
128             my ($self) = @_;
129             return $self->_x509->not_before;
130             }
131              
132             sub _build_not_after
133             {
134             my ($self) = @_;
135             return $self->_x509->not_after;
136             }
137              
138             my $default_san_factory = sub
139             {
140             my (%args) = @_;
141             my $class = {
142             uniformResourceIdentifier => 'Web::ID::SAN::URI',
143             rfc822Name => 'Web::ID::SAN::Email',
144             }->{ $args{type} }
145             // "Web::ID::SAN";
146             $class->new(%args);
147             };
148              
149             sub _build_san_factory
150             {
151             return $default_san_factory;
152             }
153              
154             sub timely
155             {
156             my ($self, $now) = @_;
157             $now //= DateTime->coerce('now');
158            
159             return if $now > $self->not_after;
160             return if $now < $self->not_before;
161            
162             return $self;
163             }
164              
165             __PACKAGE__
166             __END__
167              
168             =head1 NAME
169              
170             Web::ID::Certificate - an x509 certificate
171              
172             =head1 SYNOPSIS
173              
174             my $cert = Web::ID::Certificate->new(pem => $pem_encoded_x509);
175             foreach (@{ $cert->subject_alt_names })
176             {
177             say "SAN: ", $_->type, " = ", $_->value;
178             }
179              
180             =head1 DESCRIPTION
181              
182             =head2 Constructor
183              
184             =over
185              
186             =item C<< new >>
187              
188             Standard Moose-style constructor.
189              
190             =back
191              
192             =head2 Attributes
193              
194             =over
195              
196             =item C<< pem >>
197              
198             A PEM-encoded string for the certificate.
199              
200             This is usually the only attribute you want to pass to the constructor.
201             Allow the others to be built automatically.
202              
203             =item C<< public_key >>
204              
205             A L<Web::ID::RSAKey> object.
206              
207             =item C<< fingerprint >>
208              
209             A string identifier for the certificate. It is the lower-cased
210             hexadecimal SHA1 hash of the DER-encoded certificate.
211              
212             This is not used in WebID authentication, but may be used as an
213             identifier for the certificate if you need to keep it in a cache.
214              
215             =item C<< not_before >>
216              
217             L<DateTime> object indicating when the certificate started (or will
218             start) to be valid.
219              
220             =item C<< not_after >>
221              
222             L<DateTime> object indicating when the certificate will cease (or
223             has ceased) to be valid.
224              
225             =item C<< subject_alt_names >>
226              
227             An arrayref containing a list of subject alt names (L<Web::ID::SAN>
228             objects) associated with the certificate. These are sorted in the order
229             they'll be tried for WebID authentication.
230              
231             =item C<< san_factory >>
232              
233             A coderef used for building L<Web::ID::SAN> objects. It's very unlikely
234             you need to play with this - the default is probably OK. But changing this
235             is "supported" (in so much as any of this is supported).
236              
237             The coderef is passed a hash (not hashref) along the lines of:
238              
239             (
240             type => 'uniformResourceIdentifier',
241             value => 'http://example.com/id/alice',
242             )
243              
244             =back
245              
246             =head2 Methods
247              
248             =over
249              
250             =item C<< timely >>
251              
252             Checks C<not_before> and C<not_after> against the current system time to
253             indicate whether the certifixate is temporally valid. Returns a boolean.
254              
255             You can optionally pass it a L<DateTime> object to use instead of the
256             current system time.
257              
258             =item C<< exponent >>
259              
260             Delegated to the C<public_key> attribute.
261              
262             =item C<< modulus >>
263              
264             Delegated to the C<public_key> attribute.
265              
266             =back
267              
268             =head1 BUGS
269              
270             Please report any bugs to
271             L<http://rt.cpan.org/Dist/Display.html?Queue=Web-ID>.
272              
273             =head1 SEE ALSO
274              
275             L<Web::ID>,
276             L<Web::ID::SAN>,
277             L<Web::ID::RSAKey>.
278              
279             L<Web::ID::Certificate::Generator> - augments this class to add the
280             ability to generate new WebID certificates.
281              
282             L<Crypt::X509> provides a pure Perl X.509 certificate parser, and is
283             used internally by this module.
284              
285             =head1 AUTHOR
286              
287             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
288              
289             =head1 COPYRIGHT AND LICENCE
290              
291             This software is copyright (c) 2012 by Toby Inkster.
292              
293             This is free software; you can redistribute it and/or modify it under
294             the same terms as the Perl 5 programming language system itself.
295              
296             =head1 DISCLAIMER OF WARRANTIES
297              
298             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
299             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
300             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
301