使用LWP和SSL以及客户端证书

10

我正在将一个应用程序从PHP/cURL转移到Perl和LWP::UserAgent。我需要向Web服务器发送POST请求,并提供客户端证书和密钥文件。 我想复制的PHP代码是:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 
curl_setopt($ch, CURLOPT_SSLCERT, "/path/to/certificate.pem"); 
curl_setopt($ch, CURLOPT_SSLKEY, "/path/to/private.key"); 
curl_setopt($ch, CURLOPT_SSLKEYPASSWD, "secretpassword");

这是我的Perl代码:

my $ua = LWP::UserAgent->new();
$ua->ssl_opts(
   SSL_verify_mode => 0,
   SSL_cert_file   => '/path/to/certificate.pem',
   SSL_key_file    => "/path/to/private.key",
   SSL_passwd_cb   => sub { return "secretpassword"; }  
);

这段 PHP 代码成功连接到服务器,但 Perl 代码出现以下错误:

SSL 读取错误 error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure

我无法确定我漏掉了什么。


1
private.key(PHP)和private.pem(Perl)是打字错误还是移植的一部分? - amon
2
将多个参数传递给 ssl_opts 没有文档记录。为了安全起见,可以多次调用 ssl_opts 或将其传递到构造函数中。我认为它偶尔会起作用,但最好还是保险一些。您也可以报告它作为一个 bug/缺失功能 - Schwern
那只是我在模糊化代码时的打字错误。应该对于两者都是 private.key。谢谢您注意到了,但那不是问题所在。仍需要帮助! :) - kent
我更新了我的代码,多次调用ssl_opts。但仍然没有成功。感谢您的建议。 - kent
3
你尝试过使用 use Net::SSL (); 吗?那会强制 LWP 使用 Net::SSL(来自 Crypt::SSLeay)而不是 IO::Socket::SSL - emazep
3个回答

2

以上emazep的答案解决了我的问题。我正在使用UPS的示例Perl代码通过XML连接到他们的费率服务。从我的测试来看,只要没有直接可控制的参数调用LWP :: UserAgent,这将在任何时候起作用,这使得它非常方便,如果您正在使用一些其他模块为您调用LWP。只需使用Net :: SSL(除了已经使用LWP的任何软件包之外),并设置一些环境变量:

...
use Net::SSL;
$ENV{HTTPS_VERSION} = 3;
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
my $browser = LWP::UserAgent->new();
...

就是这样!您甚至不需要使用 $ENV{PERL_LWP_SSL_CA_FILE} 指定到您服务器根证书的路径。


对于使用 $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0 的建议,我很抱歉。这个设置会禁用确保您正在通信的服务器实际上是所需端点的逻辑;因此,尽管 URL 仍将以 "https" 开头,但连接实际上并没有 HTTPS 的安全属性。(有时候会有合法的使用情况,但除非有充分的理由,否则不应该推荐它,即使有理由也需要警告遇到的问题。) - ruakh

2
sub send_command(){
        my $command = shift;
        my $parser = XML::LibXML->new('1.0','utf-8');

        print color ("on_yellow"), "SEND: ", $command, color ("reset"), "\n";

        # Create a request
        my $req = HTTP::Request->new( GET => $Gateway.$command );

        # Pass request to the user agent and get a response back
        my $res;
        eval {
                 my $ua;
                 local $SIG{'__DIE__'};
                 $ua = LWP::UserAgent->new(); #  или 
                 $ua->ssl_opts( #$key => $value 
                    SSL_version         => 'SSLv3',
                    SSL_ca_file         => '/ca.pem',
                    #SSL_passwd_cb       => sub { return "xxxxx\n"; },
                    SSL_cert_file       => '/test_test_cert.pem',
                    SSL_key_file        => '/test_privkey_nopassword.pem',
                ); # ssl_opts => { verify_hostname => 0 }
                 $ua->agent("xxxxxx xxxx_tester.pl/0.1 ");
                 $res = $ua->request($req);

        };  
        warn $@ if $@;
        # Check the outcome of the response
        if ( $res->is_success ) {
                open  xxxLOG, ">> $dir/XXXX_tester.log";
                my $without_lf = $res->content;
                $without_lf =~ s/(\r|\n)//gm;
                print PAYLOG $without_lf,"\n";
                close PAYLOG;
        }
        else {
                 return $res->status_line;
        }       
        print  color ("on_blue"), "RESPONSE: ", color ("reset"), respcode_color($res->content), color ("reset"),"\n\n";
        return $res->content;
} 

1

实际上,这是一个混乱的部分。根据您的设置,LWP::UserAgent 可能会使用至少两个 SSL 模块中的一个来处理 SSL 连接。

  • IO::Socket::SSL
  • Net::SSL

较新版本的 LWP::UserAgent 应该默认使用第一个模块。您可以通过在终端中运行每个模块的标准命令来测试已安装哪些模块:

perl -e 'use <module>;'

IO::socket::SSL需要使用ssl_opts进行SSL配置,就像您的示例中一样。

Net::SSL需要在环境变量中进行SSL配置,就像goddogsrunnings答案中所示。

个人而言,我属于第二种情况,并从Crypt::SSLeay页面获得了良好的灵感。特别是名为“CLIENT CERTIFICATE SUPPORT”的部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接