diff options
author | Steve Hay <steve.m.hay@googlemail.com> | 2014-06-26 08:30:31 +0100 |
---|---|---|
committer | Steve Hay <steve.m.hay@googlemail.com> | 2014-06-26 08:30:31 +0100 |
commit | 6d3dbc8ef9f82e0991a68a6c37b5cda72c9c62c8 (patch) | |
tree | 3daaa2a1072294794c69b1fcd62fe3af8077e17e | |
parent | 4f43a25d533a2baa41bfaf0209f1831ba8ef210d (diff) | |
parent | 57610506fd43d0b47b5c5a962af803ba8bc1d00c (diff) | |
download | perl-libnet-6d3dbc8ef9f82e0991a68a6c37b5cda72c9c62c8.tar.gz |
Merge pull request #5 from noxxi/ipv6_ssl_ftp
added IPv6 and SSL support for Net::FTP
-rw-r--r-- | Changes | 4 | ||||
-rw-r--r-- | MANIFEST | 1 | ||||
-rw-r--r-- | lib/Net/FTP.pm | 312 | ||||
-rw-r--r-- | lib/Net/FTP/dataconn.pm | 4 | ||||
-rw-r--r-- | t/external/ftp-ssl.t | 173 |
5 files changed, 416 insertions, 78 deletions
@@ -16,8 +16,8 @@ libnet 1.28 -- TODO username. If a plain text username is specified then use the first reported SMTP AUTH method supported, as usual. [Ewen McNeill; resolves CPAN RT#58002] - * Add support for IPv6 and SSL to Net::SMTP and Net::POP3. These features are - only available if the user has + * Add support for IPv6 and SSL to Net::SMTP, Net::POP3 and Net::FTP. + These features are only available if the user has a recent IO::Socket::SSL for SSL support a recent IO::Socket::IP or an older IO::Socket::INET6 for IPv6 support @@ -30,6 +30,7 @@ t/critic.t t/datasend.t t/external/smtp-ssl.t t/external/pop3-ssl.t +t/external/ftp-ssl.t t/ftp.t t/hostname.t t/libnet_t.pl diff --git a/lib/Net/FTP.pm b/lib/Net/FTP.pm index 302c81a..66befc8 100644 --- a/lib/Net/FTP.pm +++ b/lib/Net/FTP.pm @@ -25,7 +25,35 @@ use Socket 1.3; use Time::Local; our $VERSION = '2.80'; -our @ISA = qw(Exporter Net::Cmd IO::Socket::INET); + +our $IOCLASS; +BEGIN { + # Code for detecting if we can use SSL + my $ssl_class = eval { + require IO::Socket::SSL; + # first version with default CA on most platforms + IO::Socket::SSL->VERSION(1.994); + } && 'IO::Socket::SSL'; + + my $nossl_warn = !$ssl_class && + 'To use SSL please install IO::Socket::SSL with version>=1.994'; + + # Code for detecting if we can use IPv6 + my $inet6_class = eval { + require IO::Socket::IP; + IO::Socket::IP->VERSION(0.20); + } && 'IO::Socket::IP' || eval { + require IO::Socket::INET6; + IO::Socket::INET6->VERSION(2.62); + } && 'IO::Socket::INET6'; + + sub can_ssl { $ssl_class }; + sub can_inet6 { $inet6_class }; + + $IOCLASS = $ssl_class || $inet6_class || 'IO::Socket::INET'; +} + +our @ISA = ('Exporter','Net::Cmd',$IOCLASS); use constant TELNET_IAC => 255; use constant TELNET_IP => 244; @@ -65,16 +93,32 @@ sub new { } } + my %tlsargs; + if (can_ssl()) { + # for name verification strip port from domain:port, ipv4:port, [ipv6]:port + (my $hostname = $host) =~s{(?<!:):\d+$}{}; + %tlsargs = ( + SSL_verifycn_scheme => 'ftp', + SSL_verifycn_name => $hostname, + # reuse SSL session of control connection in data connections + SSL_session_cache => Net::FTP::SSL_SingleSessionCache->new, + ); + # user defined SSL arg + $tlsargs{$_} = $arg{$_} for(grep { m{^SSL_} } keys %arg); + + } elsif ($arg{SSL}) { + croak("IO::Socket::SSL >= 1.944 needed for SSL support"); + } + my $ftp = $pkg->SUPER::new( PeerAddr => $peer, - PeerPort => $arg{Port} || 'ftp(21)', + PeerPort => $arg{Port} || ($arg{SSL} ? 'ftps(990)' : 'ftp(21)'), LocalAddr => $arg{'LocalAddr'}, Proto => 'tcp', - Timeout => defined $arg{Timeout} - ? $arg{Timeout} - : 120 - ) - or return; + Timeout => defined $arg{Timeout} ? $arg{Timeout} : 120, + %tlsargs, + $arg{SSL} ? ():( SSL_startHandshake => 0 ), + ) or return; ${*$ftp}{'net_ftp_host'} = $host; # Remote hostname ${*$ftp}{'net_ftp_type'} = 'A'; # ASCII/binary/etc mode @@ -93,6 +137,12 @@ sub new { : defined $fire ? $NetConfig{ftp_ext_passive} : $NetConfig{ftp_int_passive}; # Whew! :-) + ${*$ftp}{net_ftp_tlsargs} = \%tlsargs if %tlsargs; + if ($arg{SSL}) { + ${*$ftp}{net_ftp_tlsprot} = 'P'; + ${*$ftp}{net_ftp_tlsdirect} = 1; + } + $ftp->hash(exists $arg{Hash} ? $arg{Hash} : 0, 1024); $ftp->autoflush(1); @@ -235,6 +285,35 @@ sub size { } +sub starttls { + my $ftp = shift; + can_ssl() or croak("IO::Socket::SSL >= 1.944 needed for SSL support"); + $ftp->is_SSL and croak("called starttls within SSL session"); + $ftp->_AUTH('TLS') == CMD_OK or return; + + $ftp->connect_SSL or return; + $ftp->prot('P'); + return 1; +} + +sub prot { + my ($ftp,$prot) = @_; + $prot eq 'C' or $prot eq 'P' or croak("prot must by C or P"); + $ftp->_PBSZ(0) or return; + $ftp->_PROT($prot) or return; + ${*$ftp}{net_ftp_tlsprot} = $prot; + return 1; +} + +sub stoptls { + my $ftp = shift; + $ftp->is_SSL or croak("called stoptls outside SSL session"); + ${*$ftp}{net_ftp_tlsdirect} and croak("cannot stoptls direct SSL session"); + $ftp->_CCC() or return; + $ftp->stop_SSL(); + return 1; +} + sub login { my ($ftp, $user, $pass, $acct) = @_; my ($ok, $ruser, $fwtype); @@ -787,38 +866,41 @@ sub _store_cmd { sub port { - @_ == 1 || @_ == 2 or croak 'usage: $ftp->port([PORT])'; - - my ($ftp, $port) = @_; - my $ok; - - delete ${*$ftp}{'net_ftp_intern_port'}; - - unless (defined $port) { + @_ == 1 || @_ == 2 or croak 'usage: $self->port([PORT])'; + return _eprt('PORT',@_); +} - # create a Listen socket at same address as the command socket +sub eprt { + @_ == 1 || @_ == 2 or croak 'usage: $self->eprt([PORT])'; + return _eprt('EPRT',@_); +} - ${*$ftp}{'net_ftp_listen'} ||= IO::Socket::INET->new( - Listen => 5, - Proto => 'tcp', +sub _eprt { + my ($cmd,$ftp,$port) = @_; + delete ${*$ftp}{net_ftp_intern_port}; + unless ($port) { + my $listen = ${*$ftp}{net_ftp_listen} ||= $IOCLASS->new( + Listen => 1, Timeout => $ftp->timeout, LocalAddr => $ftp->sockhost, + can_ssl() ? ( + %{ ${*$ftp}{net_ftp_tlsargs} }, + SSL_startHandshake => 0, + ):(), ); - - my $listen = ${*$ftp}{'net_ftp_listen'}; - - my ($myport, @myaddr) = ($listen->sockport, split(/\./, $listen->sockhost)); - - $port = join(',', @myaddr, $myport >> 8, $myport & 0xff); - - ${*$ftp}{'net_ftp_intern_port'} = 1; + ${*$ftp}{net_ftp_intern_port} = 1; + my $fam = ($listen->sockdomain == AF_INET) ? 1:2; + if ( $cmd eq 'EPRT' || $fam == 2 ) { + $port = "|$fam|".$listen->sockhost."|".$listen->sockport."|"; + $cmd = 'EPRT'; + } else { + my $p = $listen->sockport; + $port = join(',',split(m{\.},$listen->sockhost),$p >> 8,$p & 0xff); + } } - - $ok = $ftp->_PORT($port); - - ${*$ftp}{'net_ftp_port'} = $port; - - $ok; + my $ok = $cmd eq 'EPRT' ? $ftp->_EPRT($port) : $ftp->_PORT($port); + ${*$ftp}{net_ftp_port} = $port if $ok; + return $ok; } @@ -827,14 +909,27 @@ sub dir { shift->_list_cmd("LIST", @_); } sub pasv { - @_ == 1 or croak 'usage: $ftp->pasv()'; - my $ftp = shift; + @_ and croak 'usage: $ftp->port()'; + return $ftp->epsv if $ftp->sockdomain != AF_INET; + delete ${*$ftp}{net_ftp_intern_port}; + + if ( $ftp->_PASV && + $ftp->message =~ m{(\d+,\d+,\d+,\d+),(\d+),(\d+)} ) { + my $port = 256 * $2 + $3; + ( my $ip = $1 ) =~s{,}{.}g; + return ${*$ftp}{net_ftp_pasv} = [ $ip,$port ]; + } + return; +} - delete ${*$ftp}{'net_ftp_intern_port'}; +sub epsv { + my $ftp = shift; + @_ and croak 'usage: $ftp->epsv()'; + delete ${*$ftp}{net_ftp_intern_port}; - $ftp->_PASV && $ftp->message =~ /(\d+(,\d+)+)/ - ? ${*$ftp}{'net_ftp_pasv'} = $1 + $ftp->_EPSV && $ftp->message =~ m{\(([\x33-\x7e])\1\1(\d+)\1\)} + ? ${*$ftp}{net_ftp_pasv} = [ $ftp->peerhost, $2 ] : undef; } @@ -918,41 +1013,51 @@ sub _extract_path { sub _dataconn { - my $ftp = shift; - my $data = undef; - my $pkg = "Net::FTP::" . $ftp->type; - - eval "require " . $pkg; ## no critic (BuiltinFunctions::ProhibitStringyEval) - + my $ftp = shift; + my $pkg = "Net::FTP::" . $ftp->type; + eval "require " . $pkg ## no critic (BuiltinFunctions::ProhibitStringyEval) + or croak("cannot load $pkg required for type ".$ftp->type); $pkg =~ s/ /_/g; - - delete ${*$ftp}{'net_ftp_dataconn'}; - - if (defined ${*$ftp}{'net_ftp_pasv'}) { - my @port = map { 0 + $_ } split(/,/, ${*$ftp}{'net_ftp_pasv'}); - - $data = $pkg->new( - PeerAddr => join(".", @port[0 .. 3]), - PeerPort => $port[4] * 256 + $port[5], - LocalAddr => ${*$ftp}{'net_ftp_localaddr'}, - Proto => 'tcp', - Timeout => $ftp->timeout - ); - } - elsif (defined ${*$ftp}{'net_ftp_listen'}) { - $data = ${*$ftp}{'net_ftp_listen'}->accept($pkg); - close(delete ${*$ftp}{'net_ftp_listen'}); + delete ${*$ftp}{net_ftp_dataconn}; + + my $conn; + my $pasv = ${*$ftp}{net_ftp_pasv}; + if ($pasv) { + $conn = $pkg->new( + PeerAddr => $pasv->[0], + PeerPort => $pasv->[1], + LocalAddr => ${*$ftp}{net_ftp_localaddr}, + Timeout => $ftp->timeout, + can_ssl() ? ( + SSL_startHandshake => 0, + $ftp->is_SSL ? ( + SSL_reuse_ctx => $ftp, + SSL_verifycn_name => ${*$ftp}{net_ftp_tlsargs}{SSL_verifycn_name}, + ) :( %{${*$ftp}{net_ftp_tlsargs}} ), + ):(), + ) or return; + } elsif (my $listen = delete ${*$ftp}{net_ftp_listen}) { + $conn = $listen->accept($pkg) or return; + $conn->timeout($ftp->timeout); + close($listen); + } else { + croak("no listener in active mode"); } - if ($data) { - ${*$data} = ""; - $data->timeout($ftp->timeout); - ${*$ftp}{'net_ftp_dataconn'} = $data; - ${*$data}{'net_ftp_cmd'} = $ftp; - ${*$data}{'net_ftp_blksize'} = ${*$ftp}{'net_ftp_blksize'}; + if (( ${*$ftp}{net_ftp_tlsprot} || '') eq 'P') { + if ($conn->connect_SSL) { + # SSL handshake ok + } else { + carp("failed to ssl upgrade dataconn: $IO::Socket::SSL::SSL_ERROR"); + return; + } } - $data; + ${*$ftp}{net_ftp_dataconn} = $conn; + ${*$conn} = ""; + ${*$conn}{net_ftp_cmd} = $ftp; + ${*$conn}{net_ftp_blksize} = ${*$ftp}{net_ftp_blksize}; + return $conn; } @@ -1009,10 +1114,7 @@ sub _data_cmd { && !defined ${*$ftp}{'net_ftp_pasv'} && !defined ${*$ftp}{'net_ftp_port'}) { - my $data = undef; - return unless defined $ftp->pasv; - $data = $ftp->_dataconn() or return; if ($where and !$ftp->_REST($where)) { my ($status, $message) = ($ftp->status, $ftp->message); @@ -1021,13 +1123,17 @@ sub _data_cmd { return; } + # first send command, then open data connection + # otherwise the peer might not do a full accept (with SSL + # handshake if PROT P) $ftp->command($cmd, @_); + my $data = $ftp->_dataconn(); if (CMD_INFO == $ftp->response()) { $data->reading - if $cmd =~ /RETR|LIST|NLST/; + if $data && $cmd =~ /RETR|LIST|NLST/; return $data; } - $data->_close; + $data->_close if $data; return; } @@ -1206,7 +1312,7 @@ sub cmd { shift->command(@_)->response() } ######################################## # -# RFC959 commands +# RFC959 + RFC2428 + RFC4217 commands # @@ -1230,6 +1336,11 @@ sub _SIZE { shift->command("SIZE", @_)->response() == CMD_OK } sub _HELP { shift->command("HELP", @_)->response() == CMD_OK } sub _STAT { shift->command("STAT", @_)->response() == CMD_OK } sub _FEAT { shift->command("FEAT", @_)->response() == CMD_OK } +sub _PBSZ { shift->command("PBSZ", @_)->response() == CMD_OK } +sub _PROT { shift->command("PROT", @_)->response() == CMD_OK } +sub _CCC { shift->command("CCC", @_)->response() == CMD_OK } +sub _EPRT { shift->command("EPRT", @_)->response() == CMD_OK } +sub _EPSV { shift->command("EPSV", @_)->response() == CMD_OK } sub _APPE { shift->command("APPE", @_)->response() == CMD_INFO } sub _LIST { shift->command("LIST", @_)->response() == CMD_INFO } sub _NLST { shift->command("NLST", @_)->response() == CMD_INFO } @@ -1261,6 +1372,26 @@ sub _SYST { shift->unsupported(@_) } sub _STRU { shift->unsupported(@_) } sub _REIN { shift->unsupported(@_) } +{ + # Session Cache with single entry + # used to make sure that we reuse same session for control and data channels + package Net::FTP::SSL_SingleSessionCache; + sub new { my $x; return bless \$x,shift } + sub add_session { + my ($cache,$key,$session) = @_; + Net::SSLeay::SESSION_free($$cache) if $$cache; + $$cache = $session; + } + sub get_session { + my $cache = shift; + return $$cache + } + sub DESTROY { + my $cache = shift; + Net::SSLeay::SESSION_free($$cache) if $$cache; + } +} + 1; __END__ @@ -1364,6 +1495,13 @@ transfers. (defaults to 10240) B<Port> - The port number to connect to on the remote machine for the FTP connection +B<SSL> - If the connection should be done from start with SSL, contrary to later +upgrade with C<starttls>. + +B<SSL_*> - SSL arguments which will be applied when upgrading the control or +data connection to SSL. You can use SSL arguments as documented in +L<IO::Socket::SSL>, but it will usually use the right arguments already. + B<Timeout> - Set a timeout value in seconds (defaults to 120) B<Debug> - debug level (see the debug method in L<Net::Cmd>) @@ -1420,6 +1558,19 @@ will be used for password. If the connection is via a firewall then the C<authorize> method will be called with no arguments. +=item starttls + +Upgrade existing plain connection to SSL. +The SSL arguments have to be given in C<new> already because they are needed for +data connections too. + +=item stoptls + +Downgrade existing SSL connection back to plain. +This is needed to work with some FTP helpers at firewalls, which need to see the +PORT and PASV commands and responses to dynamically open the necessary ports. +In this case C<starttls> is usually only done to protect the authorization. + =item host () Returns the value used by the constructor, and passed to IO::Socket::INET, @@ -1729,6 +1880,14 @@ Returns most significant digit of the response code. B<WARNING> This call should only be used on commands that do not require data connections. Misuse of this method can hang the connection. +=item can_inet6 () + +Returns whether we can use IPv6. + +=item can_ssl () + +Returns whether we can use SSL. + =back =head1 THE dataconn CLASS @@ -1799,9 +1958,12 @@ Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version L<Net::Netrc> L<Net::Cmd> +L<IO::Socket::SSL> -ftp(1), ftpd(8), RFC 959 +ftp(1), ftpd(8), RFC 959, RFC 2428, RFC 4217 http://www.ietf.org/rfc/rfc959.txt +http://www.ietf.org/rfc/rfc2428.txt +http://www.ietf.org/rfc/rfc4217.txt =head1 USE EXAMPLES diff --git a/lib/Net/FTP/dataconn.pm b/lib/Net/FTP/dataconn.pm index 3719a27..c9c3c14 100644 --- a/lib/Net/FTP/dataconn.pm +++ b/lib/Net/FTP/dataconn.pm @@ -14,7 +14,9 @@ use Errno; use Net::Cmd; our $VERSION = '0.13'; -our @ISA = qw(IO::Socket::INET); + +$Net::FTP::IOCLASS or die "please load Net::FTP before Net::FTP::dataconn"; +our @ISA = $Net::FTP::IOCLASS; sub reading { my $data = shift; diff --git a/t/external/ftp-ssl.t b/t/external/ftp-ssl.t new file mode 100644 index 0000000..3036630 --- /dev/null +++ b/t/external/ftp-ssl.t @@ -0,0 +1,173 @@ +#!perl + +use 5.008001; + +use strict; +use warnings; + +use Net::FTP; +use Test::More; +use File::Temp; +use IO::Socket::INET; + +my $server = 'test.rebex.net'; +my $debug = 0; + +plan skip_all => "no SSL support" if ! Net::FTP->can_ssl; +require IO::Socket::SSL; + + +# first try to connect w/o ftp +# plain +diag( "connect inet to $server:21" ); +IO::Socket::INET->new( "$server:21" ) or do { + plan skip_all => "$server:21 not reachable"; +}; + +# ssl to the right host +diag( "connect inet to $server:990" ); +my $sock = IO::Socket::INET->new( "$server:990") or do { + plan skip_all => "$server:990 not reachable"; +}; + +# now we need CAs +my $cafh = File::Temp->new( UNLINK => 0, SUFFIX => '.crt' ); +my %sslargs = ( SSL_ca_file => $cafh->filename ); +print $cafh <DATA>; +close($cafh); + +diag( "upgrade to ssl $server:990" ); +IO::Socket::SSL->start_SSL($sock, + SSL_verify_mode => 1, + SSL_verifycn_name => $server, + SSL_verifycn_scheme => 'ftp', + %sslargs, +) or do { + plan skip_all => "$server:990 not upgradable to SSL: ". + $IO::Socket::SSL::SSL_ERROR; +}; + +plan tests => 9; + +# first direct SSL +diag( "connect ftp over ssl to $server" ); +my $ftp = Net::FTP->new($server, + SSL => 1, + %sslargs, + Debug => $debug, + Passive => 1, +); +ok($ftp,"ftp ssl connect $server"); +$ftp->login("anonymous",'net-sslglue-ftp@test.perl') + or die "login to $server failed"; +diag("logged in"); +# check that we can talk on connection +ok(~~$ftp->ls,"directory listing protected"); +$ftp->prot('C'); +ok(~~$ftp->ls,"directory listing clear"); + +# then TLS upgrade inside plain connection +$ftp = Net::FTP->new($server, + Passive => 1, + Debug => $debug, + %sslargs +); +ok($ftp,"ftp plain connect $server"); +my $ok = $ftp->starttls; +ok($ok,"ssl upgrade"); +$ftp->login("anonymous",'net-sslglue-ftp@test.perl') + or die "login to $server failed"; +diag("logged in"); +# check that we can talk on connection +ok(~~$ftp->ls,"directory listing protected"); +$ftp->prot('C'); +ok(~~$ftp->ls,"directory listing clear"); +$ok = $ftp->stoptls; +ok($ok,"ssl downgrade"); +ok(~~$ftp->ls,"directory listing after downgrade"); + + +__DATA__ +# Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Class 2 Primary Intermediate Server CA +# Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority +-----BEGIN CERTIFICATE----- +MIIGNDCCBBygAwIBAgIBGjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NzA5WhcNMTcxMDI0MjA1NzA5WjCB +jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT +IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0 +YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4k85L6GMmoWtCA4IPlfyiAEh +G5SpbOK426oZGEY6UqH1D/RujOqWjJaHeRNAUS8i8gyLhw9l33F0NENVsTUJm9m8 +H/rrQtCXQHK3Q5Y9upadXVACHJuRjZzArNe7LxfXyz6CnXPrB0KSss1ks3RVG7RL +hiEs93iHMuAW5Nq9TJXqpAp+tgoNLorPVavD5d1Bik7mb2VsskDPF125w2oLJxGE +d2H2wnztwI14FBiZgZl1Y7foU9O6YekO+qIw80aiuckfbIBaQKwn7UhHM7BUxkYa +8zVhwQIpkFR+ZE3EMFICgtffziFuGJHXuKuMJxe18KMBL47SLoc6PbQpZ4rEAwID +AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFBHbI0X9VMxqcW+EigPXvvcBLyaGMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov +L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0 +YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3 +dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0 +c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu +BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0 +BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl +LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAnQfh7pB2MWcWRXCMy4SLS1doRKWJwfJ+ +yyiL9edwd9W29AshYKWhdHMkIoDW2LqNomJdCTVCKfs5Y0ULpLA4Gmj0lRPM4EOU +7Os5GuxXKdmZbfWEzY5zrsncavqenRZkkwjHHMKJVJ53gJD2uSl26xNnSFn4Ljox +uMnTiOVfTtIZPUOO15L/zzi24VuKUx3OrLR2L9j3QGPV7mnzRX2gYsFhw3XtsntN +rCEnME5ZRmqTF8rIOS0Bc2Vb6UGbERecyMhK76F2YC2uk/8M1TMTn08Tzt2G8fz4 +NVQVqFvnhX76Nwn/i7gxSZ4Nbt600hItuO3Iw/G2QqBMl3nf/sOjn6H0bSyEd6Si +BeEX/zHdmvO4esNSwhERt1Axin/M51qJzPeGmmGSTy+UtpjHeOBiS0N9PN7WmrQQ +oUCcSyrcuNDUnv3xhHgbDlePaVRCaHvqoO91DweijHOZq1X1BwnSrzgDapADDC+P +4uhDwjHpb62H5Y29TiyJS1HmnExUdsASgVOb7KD8LJzaGJVuHjgmQid4YAjff20y +6NjAbx/rJnWfk/x7G/41kNxTowemP4NVCitOYoIlzmYwXSzg+RkbdbmdmFamgyd6 +0Y+NWZP8P3PXLrQsldiL98l+x/ydrHIEH9LMF/TtNGCbnkqXBP7dcg5XVFEGcE3v +qhykguAzx/Q= +-----END CERTIFICATE----- +# Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority +# Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC +ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w +ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk +aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 +YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg +c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 +d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG +CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF +wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS +Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst +0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc +pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl +CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF +P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK +1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm +KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ +8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm +fyWl8kgAwKQB2j8= +-----END CERTIFICATE----- |