Perl LWP:为什么IO::Socket::SSL使用TLS 1.0,而Net::SSL使用TLS 1.2?

Perl LWP:为什么IO::Socket::SSL使用TLS 1.0,而Net::SSL使用TLS 1.2?,perl,ssl,lwp,io-socket-ssl,Perl,Ssl,Lwp,Io Socket Ssl,当我运行以下代码时: use strict; use warnings; use IO::Socket::SSL; use LWP::UserAgent; my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0, }); my $res = $ua->get('https://internal.foo.bar.baz:20002'); print $res->as_string;

当我运行以下代码时:

use strict;
use warnings;

use IO::Socket::SSL;
use LWP::UserAgent;

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
});

my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;
我从LWP得到一个内部错误:

500 Can't connect to internal.foo.bar.baz:20002 (Bad file descriptor)
Content-Type: text/plain
Client-Date: Fri, 29 Jun 2018 21:23:13 GMT
Client-Warning: Internal response

Can't connect to internal.foo.bar.baz:20002 (Bad file descriptor)

Bad file descriptor at D:/strawberry/perl/site/lib/LWP/Protocol/http.pm line 50.
网络通信表明,这是一个“客户机你好”,然后服务器立即重置,协议是TLSv1。服务器只允许TLS1.2连接,因此这是有意义的

当我更改代码以指定客户端只应使用TLS1.2时,我得到了相同的响应

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
    SSL_version => 'TLSv1_2',
});
事实上,网络流量看起来是一样的:

当我显式使用Net::SSL而不是IO::Socket::SSL时:

use strict;
use warnings;

use Net::SSL;
use LWP::UserAgent;

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
});

my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;
-- internal.foo.bar.baz port 20002
 ! using certificate verification (default) -> SSL connect attempt failed error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
 * maximum SSL version  : TLSv1_2 (SSLv23)
 * supported SSL versions with handshake used and preferred cipher(s):
   * handshake protocols ciphers
   * SSLv23    TLSv1_2   ECDHE-RSA-AES256-SHA384
   * TLSv1_2   TLSv1_2   ECDHE-RSA-AES256-SHA384
   * TLSv1_1   FAILED: SSL connect attempt failed
   * TLSv1     FAILED: SSL connect attempt failed
 * cipher order by      : unknown
 * SNI supported        : certificate verify fails without SNI
 * certificate verified : FAIL: SSL connect attempt failed error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
 * chain on ***.**.**.209
   * [0/0] bits=2048, ocsp_uri=, *******************************************************************************************************
   * [1/1] bits=2048, ocsp_uri=, *******************************************************
   * [2/2] bits=2048, ocsp_uri=, ****************************************************************
它的工作原理是:

HTTP/1.1 401 Unauthorized
Date: Fri, 29 Jun 2018 21:33:35 GMT
Server: Kestrel
Client-Date: Fri, 29 Jun 2018 21:33:37 GMT
Client-Peer: ***.**.**.209:20002
Client-Response-Num: 1
Client-SSL-Cert-Issuer: *******************************************************
Client-SSL-Cert-Subject: **************************************************************************
Client-SSL-Cipher: ECDHE-RSA-AES256-SHA384
Client-SSL-Socket-Class: Net::SSL
Client-SSL-Warning: Peer certificate not verified
Client-Transfer-Encoding: chunked
Client-Warning: Missing Authenticate header
Strict-Transport-Security: max-age=2592000
X-Powered-By: ASP.NET
协议正确设置为TLSv1.2:

奇怪的是,TLS1.2与IO::Socket::SSL协商:

use strict;
use warnings;

use Net::SSL;
use LWP::UserAgent;

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
});

my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;
-- internal.foo.bar.baz port 20002
 ! using certificate verification (default) -> SSL connect attempt failed error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
 * maximum SSL version  : TLSv1_2 (SSLv23)
 * supported SSL versions with handshake used and preferred cipher(s):
   * handshake protocols ciphers
   * SSLv23    TLSv1_2   ECDHE-RSA-AES256-SHA384
   * TLSv1_2   TLSv1_2   ECDHE-RSA-AES256-SHA384
   * TLSv1_1   FAILED: SSL connect attempt failed
   * TLSv1     FAILED: SSL connect attempt failed
 * cipher order by      : unknown
 * SNI supported        : certificate verify fails without SNI
 * certificate verified : FAIL: SSL connect attempt failed error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
 * chain on ***.**.**.209
   * [0/0] bits=2048, ocsp_uri=, *******************************************************************************************************
   * [1/1] bits=2048, ocsp_uri=, *******************************************************
   * [2/2] bits=2048, ocsp_uri=, ****************************************************************
如何防止IO::Socket::SSL尝试从LWP进行TLS 1.0连接


  • Perl 5.26.2 MSWin32-x64-多线程(草莓色)
  • OpenSSL 1.1.0h 2018年3月27日
  • LWP 6.34
  • Net::HTTPS 6.18
  • IO::Socket::SSL 2.056
  • Net::SSL 2.86

Steffen Ullrich脚本的输出:

openssl version compiled=0x1010008f linked=0x1010008f -- OpenSSL 1.1.0h  27 Mar 2018
IO::Socket::SSL version=2.056
LWP::UserAgent version=6.34
LWP::Protocol::https version=6.07
Net::HTTPS version=6.18

它不应该以您描述的方式运行,事实上,我无法在Win10上使用新安装的最新草莓Perl重现您的问题,即使用您使用的相同Perl版本。您的第一个代码与目的地分开,并使用
openssl s_服务器-www..
作为目标。它与TLS1.2完美连接,数据包捕获也清楚地显示了TLS1.2

我的猜测是您的设置出了问题:可能是系统上仍有一些较旧的Perl安装或类似的东西。这种混乱的设置可能与您运行脚本的方式有关,因为运行
analyze.pl
不会出现此类问题。因此,我建议您在脚本中实际检查实际使用的内容,即

use strict;
use warnings;
use IO::Socket::SSL;
use LWP::UserAgent;
use LWP::Protocol::https;

printf("openssl version compiled=0x%0x linked=0x%0x -- %s\n",
    Net::SSLeay::OPENSSL_VERSION_NUMBER(),
    Net::SSLeay::SSLeay(),
    Net::SSLeay::SSLeay_version(0));
printf("IO::Socket::SSL version=%s\n",$IO::Socket::SSL::VERSION);
printf("LWP::UserAgent version=%s\n",$LWP::UserAgent::VERSION);
printf("LWP::Protocol::https version=%s\n",$LWP::Protocol::https::VERSION);
printf("Net::HTTPS version=%s\n",$Net::HTTPS::VERSION);

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
});

my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;

对于我来说,在新的设置中,LWP(6.33 vs.6.34)和Net::HTTPS(6.17 vs.6.18)的版本略有不同,但其余的都适合您的版本。但重要的部分可能是代码实际加载的OpenSSL版本。我的猜测是,在您的特定脚本设置中,它使用的不是您期望的OpenSSL 1.1.0,而是一些旧的OpenSSL 1.0.0或更旧的版本,它们不支持TLS 1.2。

由于analyze-ssl.pl工作,而我的测试脚本在指向同一服务器时没有工作,我开始比较它们以找出差异。其中一个主要区别是analyze-ssl.pl尝试与
ssl\u cipher\u list=>'
建立连接,事实证明这就是问题所在

更改我的LWP::UserAgent实例化解决了以下问题:

my $ua = LWP::UserAgent->new(ssl_opts => {
    verify_hostname => 0,
    SSL_cipher_list => '',
});

您的更改本质上使IO::Socket::SSL提供了范围广泛的密码,而不是仅提供一小部分密码,这些密码是仿效浏览器所做的,并且是为了解决一些无法处理较大密码集的坏服务器。看起来您的服务器只支持非常有限的密码。由于这一点,您不太可能在该服务器与其他客户端之间也会遇到问题。@SteffenUllrich,因为它只用于内部系统之间的API调用,我同意这一点。相同的请求在cURL和SoapUI中默认工作,因此它们也必须提供更多的密码。这样做对吗?不,但正如我所说,这只是内部使用。@SteffenUllrich发现,服务器上的组策略修改了密码套件顺序,真的把事情搞得一团糟(这是我最初问题的基础)。恢复到Server 2016默认设置解决了根本问题,并使所有内容都符合HTTP/2和TLS 1.2。万岁,企业号!◔_◔再想一想:除了你所表现出来的改变之外,肯定还有别的改变。密码列表上的更改不会神奇地改变客户端提供的TLS版本。因此,您以前必须使用不同的代码运行,这些代码以某种方式使用支持TLS1.2的OpenSSL强制执行TLS1.0。或者使用旧版本的OpenSSL库运行代码,该库最多只支持TLS 1.0。