名称

IO::Socket::SSL-带IO::套接字接口的SSL套接字

简介

使用严格;使用IO::Socket::SSL;#简单客户端my$cl=IO::Socket::SSL->new('www.google.com:443');打印$cl“GET/HTTP/1.0\r\n\r\n”;打印<$cl>;#简单服务器my$srv=IO::Socket::SSL->new(LocalAddr=>“0.0.0.0:1234”,听=>10,SSL_cert_file=>“server-cert.pem”,SSL_key_file=>“server-key.pem”,);$srv->accept;

描述

IO::Socket::SSL通过将必要的功能包装到熟悉的IO::插座接口并尽可能提供安全默认值。通过这种方式,可以让现有的应用程序无需太多努力就可以实现SSL感知,至少如果您阻止I/O并且不使用选择或轮询。

但是,在幕后,SSL是一个复杂的怪兽。因此,如果默认行为不充分,有很多方法可以让它做您需要的事情。由于很容易无意中引入关键的安全性错误,或者只是很难调试问题,我建议仔细研究以下文档。

文档由以下部分组成:

其他文档可以在中找到

关于SSL/TLS的基本信息

SSL(安全套接字层)或其后续TLS(传输层安全性)是促进端到端安全的协议。这些协议用于访问网站(https)、发送或检索电子邮件以及许多其他用例。在以下文档中,我们将SSL和TLS称为“SSL”。

SSL通过提供两个基本功能实现端到端安全:

加密

这部分对数据进行加密,以便在通信双方之间进行传输,这样中间就没有人可以读取数据。它还提供了防篡改功能,因此中间没有人可以操作数据。

识别

这一部分确保你与正确的同伴交谈。如果身份识别不正确,很容易发动人与人之间的攻击,例如,如果爱丽丝想与鲍勃对话,马洛里可能会将自己置于中间,这样爱丽丝就可以与马洛里对话,而马洛里则可以与鲍勃对话。所有数据仍将被加密,但不是爱丽丝和鲍勃之间的端到端加密,而是只在爱丽丝和马洛里之间加密,然后在马洛里和鲍勃之间加密。这样,马洛里就能读取和修改爱丽丝和鲍勃之间的所有流量。

识别是最难理解、最容易出错的部分。

使用SSL时,通常使用证书在内部公钥基础设施(公钥基础设施)。这些证书可与身份证相比较,身份证包含身份证所有者的信息。这张卡不知怎的签署发行人卡片的加利福尼亚州(认证机构)。

要验证对等方的身份,必须在SSL内执行以下操作:

  • 从对等方获取证书。如果对等方没有提供证书,我们无法验证它。

  • 检查我们是否信任该证书,例如确保它不是伪造的。

    我们认为,如果我们已经知道证书,或者如果我们信任颁发者(CA),并可以验证证书上的颁发者签名。事实上,通常存在证书代理的层次结构,我们只直接信任该层次结构的根。在这种情况下,对等方不仅发送自己的证书,还发送所有中级证书。将通过构建信任路径从可信根到对等方证书,并在每个步骤中检查是否可以验证颁发者的签名。

    此步骤通常会导致问题,因为客户端不知道必需的受信任根证书。这些通常存储在依赖于系统的CA存储中,但浏览器通常有自己的CA存储。

  • 检查证书是否仍然有效。每个证书都有一个生存期,在这段时间之后不应该使用,因为它可能会被破坏,或者底层的加密在同一时间被破坏。

  • 检查证书的主题是否与对等方匹配。这就像是将身份证上的图片与代表身份证的人进行比较。

    当连接到服务器时,这通常是通过将用于连接的主机名与证书中表示的名称进行比较来完成的。证书可能包含多个名称或通配符,因此可以用于多个主机(例如*.example.com和*.examples.org)。

    虽然没有任何理智的人会接受图片与我们看到的人不匹配的身份证,但SSL的一个常见实现错误是忽略此检查或出错。

  • 检查证书是否已被颁发者吊销。如果证书以某种方式被泄露,现在其他人可能会使用它来声明错误的身份,则可能会出现这种情况。这种撤销在令人心碎的袭击之后发生了很多次。

    对于SSL,有两种验证吊销的方法,CRL和OCSP。CA使用CRL(证书吊销列表)为吊销的证书提供序列号列表。客户必须以某种方式下载列表(可能很大)并使其保持最新。使用OCSP(在线证书状态协议),客户端可以通过询问颁发者直接检查单个证书。

    撤销是验证中最困难的部分,目前没有一个浏览器能够完全正确地进行验证。但是,它们仍然比大多数其他不实现吊销检查或将困难部分留给开发人员的实现要好。

当使用SSL访问网站或以安全的方式发送邮件时,通常只会以一种方式检查身份,例如,客户端希望确保它与正确的服务器对话,但服务器通常不关心它与哪个客户端对话。但是,有时服务器也想识别客户机,并将从客户机请求证书,服务器必须以类似的方式验证该证书。

基本SSL客户端

基本SSL客户端很简单:

my$client=IO::Socket::SSL->new('www.example.com:443')或死“error=$!,ssl_error=$ssl_error”;

这将使用OpenSSL默认CA存储作为受信任CA的存储。这通常在UNIX系统上有效。如果存储中没有证书,它将尝试使用Mozilla::CA它提供了Firefox的默认CA。

在默认设置中,IO::套接字::SSL将使用更安全的密码集和SSL版本,根据证书进行正确的主机名检查,并使用SNI(服务器名称指示)在SSL握手中发送主机名。这对于在同一IP地址后面具有不同证书的服务器来说是必要的。它还将使用OCSP检查证书的吊销情况,但目前仅当服务器提供OCSP装订时(有关更深入的检查,请参阅ocsp_求解器方法)。

可以使用许多选项来更改密码、SSL版本、CA位置等。有关详细信息,请参阅方法文档。

对于SMTP等协议,有必要将现有套接字升级为SSL。可以这样做:

my$client=IO::Socket::INET->new('mx.example.com:25')或die$!;# .. 从服务器读取问候语# .. 发送EHLO并读取响应# .. 发送STARTTLS命令并读取响应# .. 如果响应成功,我们现在可以将套接字升级为SSL:IO::套接字::SSL->start_SSL($client,#显式设置SNI应使用的主机名SSL_hostname=>“mx.example.com”)或死亡$SSL_ERROR;

简单HTTP客户端的更完整示例:

my$client=IO::Socket::SSL->new(#连接的位置PeerHost=>“www.example.com”,对等端口=>“https”,#证书验证-VERIFY_PEER为默认值SSL_verify_mode=>SSL_verify_PEER,#CA商店的位置#仅当不应使用默认存储时才需要给定SSL_ca_path=>“/etc/SSL/certs”,#Linux上的典型ca路径SSL_ca_file=>“/etc/SSL/cert.pem”,#BSD上的典型ca文件#或者只使用系统上的默认路径:IO::Socket::SSL::default_ca(),#显式#或者不提供SSL_ca_*#易于主机名验证#它将使用PeerHost作为验证的默认名称#scheme作为默认值,这对于大多数用途来说都足够安全。SSL_verifycn_name=>“foo.bar”,SSL_verifycn_scheme=>“http”,#SNI支持-默认为对等主机SSL_hostname=>“foo.bar”,)或死“连接或ssl握手失败:$!,$ssl_ERROR”;#通过SSL连接发送和接收打印$client“GET/HTTP/1.0\r\n\r\n”;打印<$client>;

并使用OCSP进行吊销检查(仅适用于OpenSSL 1.0.0或更高版本以及净值::SSLeay1.59或更高):

#默认情况下,将尝试OCSP装订并仅检查叶证书my$client=IO::Socket::SSL->new($dst);#更好的是:需要检查整个链条my$client=IO::Socket::SSL->new(对等地址=>$dst,SSL_ocsp_mode=>SSL_ocsp_FULL_CHAIN,);#更好:使OCSP错误致命#(由于OCSP设置不好,许多站点可能会失败)#也使用通用OCSP响应缓存my$ocsp_cache=IO::Socket::SSL::ocsp_cache->new;my$client=IO::套接字::SSL->新建(对等地址=>$dst,SSL_ocsp_mode=>SSL_ocsp_FULL_CHAIN|SSL_OP_FAIL_HARD,SSL_ocsp_cache=>$ocsp_cache,);#如果服务器出现问题,请禁用OCSP装订my$client=IO::Socket::SSL->new(对等地址=>$dst,SSL_ocsp_mode=>SSL_ocsp_NO_STAPLE,);#检查OCSP装订尚未检查的任何证书,或#我们已经缓存了结果。对于您自己的解决联合收割机#带有$ocsp->add_response(uri,响应)的$ocsp->请求。我的$ocsp=$client->ocsp_resolver();我的$errors=$ocsp->resolve_blocking();if($errors){警告“OCSP验证失败:$errors”;关闭($client);}

基本SSL服务器

基本的SSL服务器与其他服务器类似IO::插座服务器,但它还包含证书和密钥的设置:

#简单服务器my$server=IO::Socket::SSL->new(#在哪里听LocalAddr=>“127.0.0.1”,本地端口=>8080,听=>10,#提供哪种证书#有了SNI支持,每个主机名可以有不同的证书SSL_cert_file=>“cert.pem”,SSL_key_file=>“key.pem”,)否则就死“不听:$!”;#接受客户端my$client=$server->接受或终止“无法接受或ssl握手:$!,$ssl_ERROR”;

这将自动使用一组安全的密码和SSL版本,还支持使用(椭圆曲线)Diffie-Hellmann密钥交换进行前向保密。

如果您正在进行分叉或线程服务器,我们建议您在新进程/线程内进行SSL握手,以便主进程可以自由进行新连接。我们建议这样做,因为SSL握手不正确或速度较慢的客户端可能会使服务器在握手时阻塞,这对侦听套接字是不利的:

#inet服务器my$server=IO::Socket::INET->新(#在哪里听LocalAddr=>“127.0.0.1”,本地端口=>8080,听=>10,);#接受客户端my$client=$server->接受或终止;#SSL升级客户端(在新进程/线程中)IO::套接字::SSL->start_SSL($client,SSL_server=>1,SSL_cert_file=>“cert.pem”,SSL_key_file=>“key.pem”,)或死亡“ssl握手失败:$ssl_ERROR”;

与普通套接字一样,分叉服务器和线程服务器都无法很好地扩展。建议使用非阻塞套接字,请参阅“使用非阻塞套接字”

常见使用错误

这是使用时出现的典型错误列表IO::套接字::SSL:

  • 禁用验证SSL_验证模式.

    如中所述“SSL/TLS的基本信息”,正确识别对等方至关重要,未能进行验证会导致中间人攻击。

    然而,许多脚本甚至公共模块或应用程序都会禁用验证,因为这可能是最简单的工作方式,而且通常没有人注意到任何安全问题。

    如果使用默认设置验证失败,可以执行以下操作:

    • 确保所需的CA在商店中,可能使用SSL_ca_文件SSL_ca_路径指定不同的CA存储。

    • 如果验证失败,因为证书是自签名的,而这正是您所期望的,那么可以使用SSL_指纹通过证书或公钥指纹接受特定叶证书的选项。

    • 如果由于主机名不匹配而导致验证失败,并且您无法使用证书中给定的名称访问主机,则可以使用SSL_verifycn名称指定证书中需要的主机名。

    一个常见的错误模式是,如果发现没有CA存储,则禁用验证(不同的模块查看不同的“默认”位置)。因为IO::套接字::SSL现在能够在大多数平台(UNIX、Mac OSX和Windows)上提供可用的CA存储,最好使用IO::套接字::SSL。如有必要,可使用默认ca方法。

  • SSL套接字的轮询(例如选择、轮询和其他事件循环)。

    如果在普通套接字上sysread一个字节,则会导致syscall读取一个字节。因此,如果套接字上有多个字节可用,它将保存在操作系统的网络堆栈中,下一个select或poll调用将返回可读的套接字。但是,使用SSL,您不能提供单个字节。多个数据字节被打包并加密在一个SSL帧中。解密只能在整个帧上进行,因此一个字节的sysread实际上从套接字读取整个SSL帧,对其进行解密并返回第一个解密的字节。进一步的sysreads将从同一帧返回更多字节,直到返回所有字节,并从套接字读取下一个SSL帧。

    因此,为了决定是否可以读取更多数据(例如,如果sysread将阻塞),必须通过调用悬而未决的如果没有挂起的数据,可以使用select或poll检查底层套接字。另一种方法可能是,如果您一直尝试sysread至少16kByte。16kByte是SSL帧的最大大小,因为sysread只从单个SSL帧返回数据,所以可以保证没有挂起的数据。

    此外,与普通套接字相反,套接字上交付的数据不一定是应用程序负载。它可能是TLS握手,也可能只是TLS记录的开始,也可能是在TLS 1.3中的TLS握手之后发送的TLS会话票证。在这种情况下,select将返回可供读取的数据,因为它只查看普通套接字。IO::Socket::SSL套接字上的sysread不会返回任何数据,因为它是一个只返回应用程序数据的抽象。这会导致sysread在套接字阻塞时挂起,或者在非阻塞套接字上返回EAGAIN错误。因此,使用select或类似方法的应用程序应将套接字设置为非阻塞,并预计sysread可能会因EAGAIN而暂时失败。

    另请参见“使用非阻塞套接字”.

  • 期望与普通套接字的行为完全相同。

    IO::Socket::SSL尝试尽可能好地模拟通常的套接字行为,但无法进行完全模拟。具体来说,对SSL套接字的读取也可能导致对TCP套接字进行写入,或者对SSL套接字进行写入也可能导致读取TCP套接符。阿尔索接受关闭SSL套接字将导致向TCP套接字写入和读取数据。

    尤其是,如果对等方已关闭底层TCP套接字,则隐藏的写入可能会导致连接重置。除非应用程序显式处理信号PIPE,否则通常会导致应用程序崩溃。因此,建议显式IGNORE signal PIPE,以便错误以EPIPE的形式传播,而不是导致应用程序崩溃。

  • 将“SSL_version”或“SSL_cipher_list”设置为“better”值。

    IO::套接字::SSL试图将这些价值观设定为与世界其他地方兼容的合理、安全的价值观。但是,有一些脚本或模块试图变得更智能,并获得更安全或兼容的设置。不幸的是,他们几年前就这样做了,从未更新过这些值,所以他们仍然被迫只更新“TLSv1”(而不是同时使用TLSv12或TLSv11)。或者,他们将“HIGH”设置为密码列表,并认为它们是安全的,但没有注意到“HIGH”包含匿名密码,例如,没有识别对等方。

    因此,建议将设置保留为安全默认值IO::套接字::SSL设置和更新,以更好地适应现实世界。

  • 使SSL设置不可被用户访问,以及错误的内置设置。

    一些模块使用IO::套接字::SSL,但不要将SSL设置提供给用户。这通常与错误的内置设置或默认设置(如关闭验证)相结合。

    因此,用户需要使用设置args_filter_hack或类似情况。

  • 将常量用作字符串。

    常量如SSL_VERIFY_PEER(验证)SSL_WANT_读取应该用作常量,而不是放在引号内,因为它们代表数值。

  • 分叉并处理父级和子级中的套接字。

    A类的进程将复制套接字的内部用户空间SSL状态。如果主套接字和子套接字都使用各自的SSL状态与套接字交互,则会出现奇怪的错误消息。这种交互包括显性的或隐性的关闭SSL套接字的。为了避免这种情况,套接字应该用显式关闭SSL_无关闭.

  • 分叉并执行新进程。

    由于SSL状态存储在用户空间中,因此它将由但这样做时会丢失执行官这意味着不可能通过复制相关文件句柄将新进程的stdin和stdout重定向到SSL套接字。相反,需要在子进程和SSL套接字之间显式交换普通数据。

SSL常见问题

SSL是一个具有多个实现的复杂协议,每个实现都有自己的怪癖。虽然大多数实现都可以协同工作,但对于旧版本、负载平衡器中的最小版本或完全错误的设置,通常会出现问题。

不幸的是,这些问题很难调试。了解SSL内部机制、wireshark和使用IO::套接字::SSL净值::SSLeay,这两者都可以用设置$IO::套接字::SSL::调试。定义了以下调试级别,但使用方式不一致:

也,分析-ssl.pl从位于的ssl-tools存储库https://github.com/noxxi/p5-ssl-tools在调试SSL问题时可能是一个有用的工具开放ssl命令行工具和使用不同SSL实现的检查(例如web浏览器)。

以下问题并不罕见:

  • 服务器设置错误:缺少中间证书。

    管理员无法将所有必要的证书包括在服务器设置中是一个常见问题,例如,从受信任的根构建信任链所需的一切。如果他们用浏览器检查设置,一切看起来都正常,因为浏览器通过缓存任何中间证书来解决这些问题,并在证书丢失时将其应用于新连接。

    但是,从未见过这些中间产品的新浏览器配置文件无法填写缺失的证书,也无法验证;同样的道理也适用于IO::套接字::SSL.

  • 旧版本的服务器或负载平衡器不理解特定的TLS版本或在特定数据上发出嘎嘎声。

    有时会遇到SSL对等方,它会在SSL握手中关闭连接。这通常可以通过降级SSL版本来解决,例如通过设置SSL_版本现代浏览器通常通过自动降级SSL版本并重复连接尝试直到成功来处理此类服务器。

    更糟糕的服务器不会关闭底层TCP连接,而只是丢弃相关的数据包。这很难检测,因为它看起来像一个暂停的连接。但是,降级SSL版本在这里也经常有效。

    此类问题的原因通常是负载平衡器或安全设备,它们具有硬件加速功能,并且只有最小(且不太健壮)的SSL堆栈。它们通常可以被检测到,因为它们支持的密码比其他实现少得多。

  • 错误或旧的OpenSSL版本。

    IO::套接字::SSL在的帮助下使用OpenSSL净值::SSLeay库。建议使用此库的最新版本,因为它具有更多功能,通常已知的错误更少。

  • 客户端证书验证失败。

    确保证书的用途允许用作ssl客户端(请使用openssl x509-用途,必须的根证书位于指定的路径中SSL_ca公司*(或默认路径),并且构建信任链所需的任何中间证书都由客户端发送。

  • 即使使用提供了自签名证书,也无法验证该证书SSL_ca公司*参数。

    这个SSL_ca公司*参数不为任意证书提供通用信任存储,而只为CA证书指定存储,然后可以使用CA证书验证其他证书。这尤其意味着,不是CA的证书会被简单地忽略,尤其是没有设置CA标志的自签名证书。

    OpenSSL的这种行为不同于在浏览器中可以找到的更通用的信任存储概念,在浏览器中,可以简单地将任意证书(CA或非CA)添加为可信证书。

使用非阻塞套接字

如果您有一个非阻塞套接字,那么在读取、写入、接受或连接时的预期行为是设置$!如果操作无法立即完成,则返回到EWOULDBLOCK。请注意,EWOULDBLOCK在UNIX系统上与EAGAIN相同,但在Windows上不同。

使用SSL,握手可能随时发生,即使在已建立的连接内也是如此。在这些情况下,必须先完成握手,然后才能读取或写入数据。这可能会导致这样的情况:你想阅读,但必须首先完成握手的写入;或者你想写作,但必须先完成阅读。在这些情况下$!与预期一样设置为EAGAIN,此外$SSL_ERROR(错误)设置为SSL_WANT_READ或SSL_VANT_WRITE。因此,如果在SSL套接字上获得EWOULDBLOCK,则必须检查$SSL_ERROR(错误)并相应调整事件掩码。

在非阻塞套接字上使用readline没有多大意义,我建议不要使用它IO::插座类,它将尝试模拟在那里看到的行为,例如返回接收到的数据而不是阻塞,即使行没有完成。如果发生不可恢复的错误,它将不返回任何内容,即使它已经收到一些数据。

此外,我建议不要使用接受使用非阻塞SSL对象,因为它可能会阻塞,而这并不是大多数人所期望的。原因是接受在非阻塞TCP套接字上(例如。IO::套接字::IP,IO::插座::INET..)会生成一个新的TCP套接字,该套接字不会继承主套接字的非阻塞行为。因此,内部新套接字上的初始SSL握手IO::套接字::SSL::接受将以阻塞方式完成。为了解决这个问题,您可以通过执行TCP接受并在以后以非阻塞方式升级TCP套接字启动_SSL接受_SSL.

my$cl=IO::Socket::SSL->new($dst);$cl->阻塞(0);my$sel=IO::选择->新建($cl);而(1){#使用SSL调用读取n个字节不会导致读取n个#字节,但它必须至少读取一个完整的SSL#框架。如果套接字没有新字节,但有未处理的数据#从SSL框架can_read将阻塞!#等待套接字上的数据$sel->can_read();#套接字或eof上的新数据阅读:#这不仅从套接字中读取1个字节,而且读取完整的SSL#帧,然后只返回一个字节。在随后的调用中,它比#返回相同SSL帧的更多字节,直到需要读取#下一帧。my$n=系统读取($cl,my$buf,1);if(!定义$n){死$!如果不是$!{EWOULDBLOCK};如果$SSL_ERROR==SSL_WANT_READ;如果($SSL_ERROR==SSL_WANT_WRITE){#需要在重新协商时写入数据$sel->can_write;下一步;}die“出现问题:$SSL_ERROR”;}elsif(!$n){最后;#电动势}其他{#读取下一个字节#我们可能在当前SSL框架中仍有数据#因此,首先要处理这些数据,而不是等待底层数据#套接字对象如果$cl->pending;#,则转到READ转到sysread下一个;#转到$sel->can_read}}

此外,在使用select、poll、kqueue或类似技术以获得数据可用时的通知时,与普通套接字也有不同。在所有情况下,仅仅依靠这些调用是不够的,因为未读数据可能会在SSL堆栈中进行内部缓冲。检测这种缓冲挂起()需要使用。或者,可以通过使用系统读取具有SSL帧的最大大小。请参阅“常见用法错误”了解详细信息。

高级使用

SNI支持

较新的SSL扩展可以使用服务器名称指示(SNI)区分同一IP地址上的多个主机名。

OpenSSL 0.9.8系列中增加了对客户端SNI的支持,但在1.0版本中,当服务器无法确定其主机名时,修复了一个错误。因此,客户端SNI仅支持OpenSSL 1.0或更高版本IO::套接字::SSL。在支持的版本中,如果SNI可以根据对等地址对等主机(它们是底层IO::Socket::类中的同义词,因此不应同时或至少不应设置为不同的值)。在不受支持的OpenSSL版本上,它将不使用SNI。主机名也可以用显式给定SSL_主机名,但在这种情况下,如果不支持SNI,它将抛出错误。要检查您可能会致电的支持IO::套接字::SSL->can_client_sni().

在服务器端,支持早期版本的OpenSSL,但只能与净值::SSLeay版本>=1.50。要检查您可能会致电的支持IO::套接字::SSL->can_server_sni()。如果支持服务器端SNI,则可以使用为每个主机指定不同的证书SSL_证书*SSL_键*,并使用检查请求的名称获取服务器名称.

使用相同套接字的普通对话和SSL

通常需要先交换一些普通数据,然后在执行某种STARTTLS命令后将套接字升级到SSL。FTPS等协议甚至需要一种方法将套接字再次降级为普通套接字。

通常的方法是创建一个普通的套接字并使用启动_SSL升级并停止_SSL以降级:

my$sock=IO::Socket::INET->new(…)或die$!;…在$sock上交换普通数据,直到starttls命令。。。IO::Socket::SSL->start_SSL($sock,%sslargs)或die$SSL_ERROR;…现在$sock是一个IO::Socket::SSL对象。。。…在$sock上与SSL交换数据,直到stoptls命令。。。$sock->stop_SSL或die$SSL_ERROR;…现在$sock又是一个IO::Socket::INET对象。。。

但是,许多模块直接从IO::插座::INET。虽然此基类可以替换为IO::套接字::SSL,这些模块无法轻松支持SSL和普通数据的不同基类,也无法通过starttls命令在这些类之间切换。

为了在这种情况下提供帮助,IO::套接字::SSL可以在启动时简化为普通套接字,并且connect_SSL/accept_SSL/start_SSL可以用于启用SSL和停止_SSL再说一遍:

my$sock=IO::Socket::SSL->new(对等地址=>。。。SSL_startHandshake=>0,%sslargs(SSL参数))或者死$!;…在$sock上交换普通数据,直到starttls命令。。。$sock->connect_SSL或die$SSL_ERROR;…现在$sock是一个IO::Socket::SSL对象。。。…在$sock上与SSL交换数据,直到stoptls命令。。。$sock->stop_SSL或die$SSL_ERROR;…$sock仍然是IO::Socket::SSL对象。。。…但数据再次以普通方式交换。。。

集成到自己的模块中

IO::套接字::SSL行为与其他类似IO::插座模块,因此可以以相同的方式进行集成,但在使用非阻塞I/O(如处理超时)或使用select或poll时必须特别小心。请研究如何处理这些差异的文档。

此外,建议不要设置或触摸SSL协议_*选项,以便保留其安全默认值。还建议允许用户覆盖这些特定于SSL的设置,而无需进行全局设置或黑客攻击,如设置args_filter_back.

值得注意的例外是SSL_验证方案。这应该设置为模块或协议所需的主机名验证方案。

方法说明

IO::套接字::SSL从他人继承IO::插座模块。超级类的选择取决于安装的模块:

请注意,对于支持IPv6的超类,它将首先查找给定主机名的IPv6地址。如果解析程序提供IPv6地址,但IPv6无法访问主机,则不会自动回退到IPv4。为了避免这些问题,您可以使用家庭值为AF_INET的选项,如中所述IO::套接字::IP。或者,您可以通过加载全局IPv4IO::套接字::SSL使用选项“inet4”,在这种情况下,它将使用仅IPv4类IO::插座::INET作为超级市场。

IO::套接字::SSL将提供其超类的所有方法,但有时它会重写这些方法,以匹配SSL预期的行为或提供其他参数。

下面描述了新的或更改的方法,但也请阅读有关SSL特定错误处理的部分。

错误处理

如果发生SSL特定错误,则全局变量$SSL_ERROR(错误)将被设置。如果错误发生在现有SSL套接字上,则方法错误字符串将允许访问最新的套接字特定错误。两者都有$SSL_ERROR($SSL_ERROR)错误str方法给出一个类似于$!例如,在数字上下文中提供错误号,或在字符串上下文中提供一个错误描述。

新建(…)

创建新的IO::套接字::SSL对象。你可以使用超级类附带的所有友好选项(例如。IO::套接字::IP,IO::插座::INET, ...) 加上(可选地)下面描述的那些。如果您没有指定任何与SSL相关的选项,它将尽最大努力使用安全默认值,例如选择好的密码、启用正确的验证等。

SSL_服务器

如果套接字应用作服务器,请将此选项设置为真值。如果未显式设置,则假定参数是在创建套接字时给定的。

SSL_主机名

可以指定SNI使用的主机名,如果在同一IP地址上有多个SSL主机名,则需要指定该主机名。如果没有给出,它将尝试从中确定主机名对等地址,如果只给定了一个IP,或者如果在启动_SSL.

如果要禁用SNI,请将此参数设置为“”。

目前仅支持客户端,服务器端将忽略。

有关SNI支持的详细信息,请参阅“SNI支持”一节。

SSL_start握手

如果此选项设置为false(默认为true),则还不会启动SSL握手。这必须稍后完成接受_SSL连接_SSL。在握手开始之前,可以使用读/写等来交换纯数据。

SSL_keepSocketOn错误

如果此选项设置为true(默认为false),则在出现错误时不会关闭底层TCP套接字。在大多数情况下,这种行为并没有实际用途,因为TCP连接的两端可能对连接的当前状态有不同的看法。

SSL_ca | SSL_ca_file | SSL_ca_path

通常,您希望验证对等证书是否已由受信任的证书颁发机构签名。在这种情况下,您应该使用此选项指定文件(SSL_ca_文件)或目录(SSL_ca_路径)包含受信任的证书颁发机构的证书。

SSL_ca_路径也可以是包含多个路径的数组或字符串,其中路径由特定于平台的分隔符分隔。此分离器是;在DOS、Windows、Netware、,,VMS和:用于所有其他系统。如果给定了多条路径,则其中至少一条必须是可访问的。

您还可以提供X509*证书句柄的列表(如从净值::SSLeayIO::套接字::SSL::实用程序::PEM_xxx2cert)带有SSL_ca公司。这些将在路径和文件之前添加到CA存储中,因此具有优先权。如果未设置SSL_ca、SSL_ca_file或SSL_ce_path,则将使用默认ca()以确定用户设置或系统默认值。如果确实不想设置CA,请将SSL_CA_file或SSL_ce_path设置为\未定义或者SSL_ca到空列表。(不幸的是''由某些模块使用IO::套接字::SSL未明确给出CA时)。

SSL_client_ca|SSL_crient_ca_file

如果服务器端的verify_mode为verify_PEER,则可以使用这些选项为客户端设置可接受CA的列表。这样,客户端可以从证书列表中选择所需的证书。这些选项的值类似于SSL_ca公司SSL_ca_文件.

SSL_指纹

有时,您有一个自签名证书或由未知CA颁发的证书,您确实想接受它,但根本不想禁用验证。在这种情况下,您可以将证书的指纹指定为'算法$hex_fingerprint'.阿尔戈是OpenSSL支持的指纹算法,例如“sha1”、“sha256”。。。六边形指纹是二进制指纹的十六进制表示。十六进制字符串中的任何冒号都将被忽略。

如果要使用证书中公钥的指纹而不是证书,请使用以下语法'算法$pub$hex_fingerprint'相反。要获取已建立连接的指纹,可以使用获取指纹.

也可以跳过阿尔戈$,即仅指定指纹。在这种情况下,将根据摘要字符串的长度自动检测可能的算法。

如果您有几个可接受的证书,可以指定指纹列表。如果指纹与最上面的(即叶)证书匹配,则没有其他验证会导致验证失败。

SSL_cert_file|SSL_cert |SSL_key_file|SSL_key

如果创建服务器,通常需要指定一个应由客户端验证的服务器证书。客户端证书也是如此,应该由服务器验证。证书可以作为带有SSL_cert_file的文件或X509*对象的内部表示形式(如净值::SSLeayIO::套接字::SSL::实用程序::PEM_xxx2cert)使用SSL_cert。如果作为文件提供,它将自动检测格式。支持的文件格式为PEM、DER和PKCS#12,其中PEM和PKCS#12可以包含要使用的证书和链,而DER只能包含单个证书。

如果以X509*列表的形式给出,请注意,所有链证书(例如,除第一个证书外的所有证书)都将被openssl“使用”,并且在SSL上下文被破坏时将被释放,因此您永远不要自己释放它们。但是服务器证书(例如第一个)不会被openssl使用,因此必须由应用程序释放。

对于每个证书,都需要一个密钥,它可以作为带有SSL_key_file的文件,也可以作为带有SSL_key的EVP_PKEY*对象的内部表示形式(如网络::SSLeayIO::套接字::SSL::实用程序::PEM_xxx2key). 如果SSL_cert_file指定的PKCS#12文件中已给定密钥,则会忽略任何SSL_key或SSL_key_file。如果没有给定SSL_key或SSL_key_file,它将尝试再次使用SSL_cert_file给定的PEM文件,可能它也包含密钥。

如果您的SSL服务器应该能够在同一IP地址上使用不同的证书,这取决于SNI给定的名称,那么您可以使用哈希引用,而不是具有<主机名=cert_file>>。

如果您的SSL服务器应该能够对同一域/IP同时使用RSA和ECDSA证书,则会提供类似于SNI的哈希引用。用于指定附加证书的域名应为hostname%任意,即。主机名%ecc或类似的。这至少需要OpenSSL 1.0.2。让服务器根据客户端密码首选项选择证书SSL_主机_密码_订单应设置为false。

如果需要证书和密钥,但没有给定,则可能会返回到内置默认值,请参阅“证书、密钥和CA的默认值”。

示例:

SSL_cert_file=>“mycert.pem”,SSL_key_file=>“mykey.pem”,SSL_cert_file=>{“foo.example.org”=>“foo-cert.pem”,“foo.example.org%ecc”=>“foo-ecc-cert.pem”,“bar.example.org”=>“bar-cert.pem”,#在没有匹配项或客户端不支持SNI时使用“”=>“default-cert.pem”,“%ecc”=>“default-ecc-cert.pem”,},SSL_key_file=>{“foo.example.org”=>“foo-key.pem”,“foo.example.org%ecc”=>“foo-ecc-key.pem”,“bar.example.org”=>“bar-key.pem”,#在没有匹配项或客户端不支持SNI时使用“”=>“default-key.pem”,“%ecc”=>“default-ecc-key.pem”,}
SSL_密码_cb

如果您的私钥已加密,您可能不希望使用Net::SSLeay的默认密码提示。此选项引用一个子例程,该子例程应返回解密私钥所需的密码。

SSL_使用_插入

如果这是真的,它将强制IO::Socket::SSL使用证书和密钥,即使您正在设置SSL客户端。如果将其设置为0(默认值),则只需要在设置服务器时使用证书和密钥。

如果设置了SSL_server,则会隐式设置SSL_use_cert。为了方便起见,如果未提供证书,但提供了证书以供使用(SSL_cert_file或类似文件),也会设置它。

SSL_版本

设置用于传输数据的SSL协议的版本。'SSLv23使用与SSL2.0、SSL3.0和TLS1.x兼容的握手,而“SSLv2”、“SSLv 3”、“TLSv1”、“TSSv1_1”、”TLSv1_“或”TLSv 1_3“将握手和协议限制为指定版本。所有值都区分大小写。除了“TLSv1_1”、“TLSv1_2”和“TLSv1_3”之外,还可以使用“TLSv 11”、“TLSv12”和“tlSv 13”。实际支持的协议版本取决于所安装的OpenSSL和Net::SSLeay的版本,但像TLS 1.3这样的现代协议多年来一直受到这些版本的支持。

独立于握手格式,您可以通过添加限制为一组可接受的SSL版本!版本由“:”分隔。

默认SSL_version为'SSLv23:!TLSv1:!TLSv1_1:!SSLv3:!SSLv2’。这意味着,握手格式与SSL2.0和更高版本兼容,但成功的握手仅限于TLS1.2和更高级别,即没有SSL2.0、SSL3.0、TLS 1.0或TLS 1.1,因为这些版本存在严重的安全问题,不应再使用。

你也可以使用!TLSv1_1和!TLSv1_2禁用TLS版本1.1和1.2,同时仍允许TLS版本1.0。将版本设置为“TLSv1”可能会中断与非常旧或损坏的客户端的交互,这些客户端需要与SSL2.0兼容的握手。另一方面,一些断开的客户端在收到TLS 1.1版本的请求时会关闭连接。在这种情况下,将版本设置为'SSLv23:!SSLv2:!SSLv3:!TLSv1_1:!“TLSv1_2”可能会有所帮助。

SSL_密码列表

如果设置了此选项,则连接的密码列表将设置为给定值,例如“ALL:!”!低:!经验:!为空。这只会影响TLS 1.2及更低版本的密码。请参阅OpenSSL文档(https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html#CIPHER-字符串)了解更多详细信息。

除非由于没有共享密码而无法联系您的对等方,否则建议将此选项保留为默认设置,即使用系统默认值,但禁用一些在旧系统上仍可能启用的不安全密码。

如果不同的SNI主机需要不同的密码列表,可以使用主机作为密钥,密码集作为值来给出散列,类似于SSL_证书*.

SSL_密码套件

如果设置了此选项,则连接的TLS 1.3密码套件将设置为给定值。这与SSL_cipher_list类似,但仅适用于TLS 1.3密码。参见参数-密码套件在OpenSSL文档中(https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html)了解详细信息。

除非您由于没有共享密码而无法联系您的同伴,否则建议将此选项保留为默认设置,即使用系统默认设置。

如果不同的SNI主机需要不同的密码列表,可以使用主机作为密钥,密码集作为值来给出散列,类似于SSL_证书*.

SSL_主机_密码_订单

如果此选项为true,则使用服务器指定的密码顺序,而不是客户端建议的顺序。此选项默认为true以使用我们的安全密码列表设置。

SSL_dh_文件

要创建提供前向保密性的服务器,您需要提供DH参数或(更好的是,因为更快)ECDH曲线。此设置关心DH参数。

为了支持非椭圆Diffie-Hellman密钥交换,需要在此处提供一个合适的文件,或者SSL_dh应该与适当的值一起使用。有关更多信息,请参阅openssl中的dhparam命令。

如果两者都没有SSL_dh_文件也不是SSL_dh公司默认使用长度为2048位的内置DH参数提供DH密钥交换。如果您不想这样做(例如禁用DH密钥交换),请显式设置此项或SSL_dh公司参数设置为undef。

SSL_dh公司

与SSL_dh_file类似,但您不使用预加载或生成的dh*来提供文件。

SSL_ecdh_曲线

要创建提供前向保密性的服务器,您需要提供DH参数或(更好的是,因为更快)ECDH曲线。此设置关注ECDH曲线。

为了支持椭圆曲线Diffie-Hellmann密钥交换,这里需要提供至少一条合适曲线的OID或NID。

对于OpenSSL 1.1.0+,此参数默认为汽车,这意味着它允许OpenSSL选择最佳设置。如果在Net::SSLeay中实现了对CTX_set_ecdh_auto的支持(至少需要1.86版),则它将使用该支持来实现相同的默认值。否则,它将默认为素数256v1(OpenSSL的内置),以便在默认情况下提供ECDH密钥交换。

如果Net::SSLeay支持设置组或曲线(至少需要1.86版),则可以按首选顺序在此处给出多条曲线,即。P-521:P-384:P-256。当在客户端使用时,这将包括支持的曲线作为TLS握手的扩展。

如果您不想进行ECDH密钥交换,可以将其设置为undef或setSSL_密码排除所有这些密码。

您可以通过致电检查ECDH支持是否可用IO::套接字::SSL->can_ecdh.

SSL_验证模式

此选项设置对等证书的验证模式。您可以组合SSL_VERIFY_PEER(VERIFY_PEER)、SSL_VERIFY_FAIL_IF_NO_PEER_CERT(如果不存在对等证书,则验证失败;对于客户端,将忽略此项)和SSL_VERIFY_CLIENT_ONCE(只验证一次客户端;对于客户端将忽略此选项)。有关更多信息,请参阅SSL_CTX_set_verify的OpenSSL手册页。

默认值为服务器的SSL_VERIFY_NONE(例如,不检查客户端证书)和客户端的SSL_EVERIFY_PEER(检查服务器证书)。

SSL_验证回拨

如果您想自己验证证书,可以传递一个子引用和此参数。调用回调时,它将被传递:

1.一个true/false值,指示OpenSSL对证书的看法,
2.证书存储的C型内存地址,
3.包含证书颁发者属性和所有者属性的字符串,以及
4.包含遇到的任何错误的字符串(如果没有错误,则为0)。
5.对等方自己的证书的C风格内存地址(可通过Net::SSLeay::PEM_get_string_X509()转换为PEM形式)。
6.证书在链中的深度。深度0是叶证书。

函数应该返回1或0,具体取决于它认为证书有效还是无效。默认设置是让OpenSSL完成所有繁忙的工作。

将为证书链中的每个元素调用回调。

有关更多信息,请参阅SSL_CTX_set_verify的OpenSSL文档。

SSL_验证方案

该方案用于通过使用对等方的主机名来正确验证证书内部的身份。请参阅中有关验证方案的信息验证主机名.

如果您没有指定方案,它将使用“default”,但只会在名称验证失败时大声抱怨,而不会让整个证书验证失败。这将发生变化,例如,如果主机名与证书不匹配,则将来证书验证将失败!!!!要覆盖验证中使用的名称,请使用SSL_verifycn名称.

方案“default”是常用方案的超集,它将接受通用名称和subjectAltName中的主机名,并允许在任何地方使用通配符。虽然使用此方案比无名称验证更安全,但您最好使用特定于应用程序协议的方案,例如“http”、“ftp”。。。

如果您确实确定不想使用主机名验证身份,那么可以使用“none”作为方案。在这种情况下,你最好有其他形式的验证,比如证书指纹,或者稍后通过调用验证主机名你自己。

SSL_verifycn_publicsuffix

此选项用于指定检查公共后缀的通配符证书时的行为,例如,不应接受*.com或*.co.uk的通配器证书,而*.example.com或*.exampel.co.uk可以。

如果未指定,则只使用内置默认值IO::套接字::SSL::PublicSuffix,可以使用此模块的fromstring或fromfile创建另一个对象。

要禁用验证公共后缀,请将此选项设置为''.

SSL_verifycn名称

设置主机名验证中使用的名称。如果设置了SSL_verifycn_scheme,但未给定SSL_verifycn_name,则会尝试使用SSL_hostname或PeerHost和PeerAddr设置,如果无法确定名称,则会失败。如果未设置SSL_verifycn_scheme,它将使用默认方案,并在无法确定主机名时发出警告,但不会失败。

只有直接使用创建连接时,才能使用PeerHost或PeerAddrIO::套接字::SSL->新,如果使用升级了IO::Socket::INET对象启动_SSL姓名必须填写SSL_verifycn名称SSL_主机名.

SSL_检查_循环

如果要验证对等证书是否已被签名机构吊销,请将此值设置为true。OpenSSL将在SSL_ca_path中搜索CRL,或使用SSL_CRL_file指定的文件。有关更多详细信息,请参阅Net::SSLeay文档。请注意,此功能在OpenSSL<v0.9.7b时似乎已被破坏,因此在较低版本中使用它将导致错误。

SSL_crl_文件

如果要指定要使用的CRL文件,请将此值设置为要使用的路径名。除了设置SSL_check_crl之外,还必须使用此选项。

SSL_ocsp_模式

定义如何使用OCSP(联机状态吊销协议)进行证书吊销。默认情况下,向服务器发送OCSP装订请求,如果服务器返回OCSP响应,将使用结果。

任何其他OCSP检查都需要使用手动完成ocsp_求解器.

以下标志可以与组合|:

SSL_OCSP_NO_分接头

不要要求OCSP装订。如果SSL_verify_mode为verify_NONE,则此为默认值。

SSL_OCSP_TRY_阶梯

尝试OCSP装订,但如果没有收到装订响应,不要抱怨。如果SSL_verify_mode为verify_PEER(默认值),则此为默认值。

SSL_OCSP_MUST_STAPLE程序

如果服务器没有发送回已装订的OCSP响应,则认为这是一个硬错误。大多数服务器目前都没有发送回固定的OCSP响应。

SSL_OCSP_FAIL_HARD(SSL_OP_FAIL_硬件)

响应错误硬失败,默认情况下是像浏览器一样软失败。软错误意味着OCSP响应不可用,例如没有响应、错误响应、没有有效签名等。在任何情况下,验证响应中的证书撤销都被视为硬错误。

装订响应中的软错误永远不会被视为硬错误,例如,在这种情况下,预计OCSP请求将发送给负责的OCSP响应程序。

SSL_OCSP_FULL_链

这将设置ocsp_求解器这样将检查对等链中的所有证书,否则只检查叶证书是否被吊销。

SSL_ocsp_staple_callback

如果定义了此回调,则将使用SSL对象和从对等方获取的OCSP响应句柄调用它,例如。<$cb-($ssl,$resp)>>。如果对等端未提供固定的OCSP响应,则将使用调用函数$resp=未定义。由于OCSP响应句柄在离开此函数后不再有效,因此不应复制或释放它。如果离开此函数后需要访问响应,则可以使用序列化网络::SSLeay::i2d_OCSP_RESPONSE.

如果没有提供这样的回调,它将使用默认的回调,该回调验证响应并使用它来检查连接的证书是否被吊销。

SSL_ocsp_缓存

通过此选项,可以为缓存OCSP响应提供缓存,这些响应可以在不同的SSL上下文之间共享。如果没有给定,将只使用特定于SSL上下文的缓存。

您可以使用创建新缓存IO::套接字::SSL::OCSP_Cache->新([大小])或者实现自己的缓存,这需要有方法put($key,\%entry)获取($key)(返回\%条目)其中entry是OCSP响应的哈希表示,其中包含以下字段nextUpdate(下一次更新)。缓存的默认实现将认为响应有效,只要nextUpdate(下一次更新)小于当前时间。

SSL_重用_ctx

如果您已经为以前的IO::Socket::SSL实例设置了上述选项,那么可以通过将其作为SSL_reuse_ctx参数的值传递来重用该实例的SSL上下文。您还可以创建IO::Socket::SSL::SSL_Context类的新实例,使用您需要的任何上下文选项,而不指定连接选项,并将其传递到此处。

如果使用此选项,则在对new()的同一调用中传递的所有其他与上下文相关的选项都将被忽略,除非提供的上下文无效。注意,与v0.90以下的IO::Socket::SSL版本相反,除非使用set_default_context()函数,否则不会隐式使用全局SSL上下文。

SSL_create_ctx回拨

使用此回调,您可以在创建上下文并完成默认设置后对其进行单独设置。将使用Net::SSLeay中的CTX对象作为单个参数调用回调。

限制服务器会话缓存大小的示例:

SSL_create_ctx_callback=>子{my$ctx=班次;净值::SSLeay::CTX_sess_set_cache_size($CTX,128);}
SSL会话缓存大小

如果重复连接到同一主机/端口,并且SSL重新协商时间有问题,则可以通过指定正缓存大小来使用此选项打开客户端会话缓存。对于连续的连接,将SSL_reuse_ctx选项传递给new()调用(或使用set_default_context())以使用缓存的会话。会话缓存大小是指一次可以存储的唯一主机/端口对的数量;如果添加新会话,缓存中最旧的会话将被删除。

此选项不会影响服务器为其客户端提供的会话缓存,例如,它不会影响设置了SSL_server的SSL对象。

请注意,使用TLS 1.3的会话缓存至少需要Net::SSLeay 1.86。

SSL会话缓存

指定应使用而不是创建新的会话缓存对象。覆盖SSL_session_cache_size。如果您想重用缓存,但不想重用上下文的其余部分,则此选项非常有用。

可以使用创建会话缓存对象IO::Socket::SSL::Session_Cache->new(缓存大小).

使用set_default_session_cache()设置全局缓存对象。

SSL_session_key(会话密钥)

指定用于查找和插入到客户端会话缓存中的密钥。将使用每个默认的ip:port of destination,但有时您希望通过同一服务器上的多个端口共享同一会话(与FTPS类似)。

SSL_session_id_context(会话_上下文)

这将为服务器会话缓存提供一个id。如果您希望客户端使用客户端证书进行连接,这是必要的。如果未给定,但SSL_verify_mode指定需要客户端证书,则将选择上下文唯一id。

SSL错误陷阱

在使用accept()或connect()方法时,可能会出现实际套接字连接有效但SSL协商失败的情况,就像HTTP客户端连接到HTTPS服务器的情况一样。通过传递附加到此参数的子例程ref,可以获得对孤立套接字的控制,而不是强制关闭它。如果调用子例程,将传递两个参数:对SSL协商失败的套接字的引用和错误消息的全文。

SSL_npn_协议

如果在服务器端使用,它将SSL服务器公布的协议列表指定为数组引用,例如['spdy/2','http1.1']。在客户端,它将客户端为NPN提供的协议指定为数组引用。另请参阅方法下一个原型.

下一个协议协商(NPN)可用于Net::SSLeay 1.46+和openssl-1.0.1+。NPN在TLSv1.3协议中不可用。要检查您可能会致电的支持IO::套接字::SSL->can_npn()。如果将此选项用于不受支持的Net::SSLeay/OpenSSL,则会引发错误。

SSL_alpn_协议

如果在服务器端使用,它将SSL服务器支持的协议列表指定为数组引用,例如['http://2.0'、'spdy/3.1'、'http://1.1']。在客户端,它将客户端公布的ALPN协议指定为数组引用。另请参阅方法alpn选定.

应用层协议协商(ALPN)可用于Net::SSLeay 1.56+和openssl-1.0.2+。有关扩展的更多详细信息,请参阅RFC7301。要检查您可能会致电的支持IO::套接字::SSL->can_alpn()。如果将此选项用于不受支持的Net::SSLeay/OpenSSL,则会引发错误。

请注意,如果同时指定了NPN和ALPN,则某些客户端实现可能会遇到问题。由于ALPN旨在替代NPN,请尝试提供ALPN协议,如果失败,则返回NPN。

SSL_ticket_keycb=>[$sub,$data]|$sub

这是用于无状态会话重用的回调(会话票证,RFC 5077)。

此回调将被调用为$sub->($data,[$key_name])哪里$数据是给定给SSL_tickt_keycb(或undef)的参数$密钥名称取决于模式:

加密票据

如果票证需要加密,则调用回调时不会$密钥名称。在这种情况下,它应该返回($current_key,$current_key_name)其中$current_key(当前密钥)是当前密钥(32字节随机数据)$current_key_name(当前密钥名称)与此密钥相关联的名称(精确到16字节)。这个$current_key_name(当前密钥名称)将被纳入票据。

解密票据

如果票证需要解密,将使用调用回调$密钥名称如票上所示。它应该会回来($key,$current_key_name)其中$键是与给定$密钥名称$current_key_name(当前密钥名称)与当前活动密钥关联的名称。如果$current_key_name(当前密钥名称)与给定的不同$密钥名称将再次调用回调以使用当前活动密钥重新加密票据。

如果找不到与给定匹配的密钥$密钥名称那么这个函数应该不返回任何内容(空列表)。

应该使用此机制来限制对票据进行加密的每个密钥的生存时间。票证加密密钥泄露可能导致SSL会话解密,SSL会话使用受此密钥保护的会话票证。

例子:

Net::SSLeay::RAND_bytes(我的$oldkey,32);Net::SSLeay::RAND_bytes(我的$newkey,32);my$oldkey_name=包(“a16”,“oldsecret”);my$newkey_name=pack(“a16”,'newsecret');我的@keys=([$newkey_name,$newkey],#当前活动密钥[$oldkey_name,$oldkey],#已过期);my$keycb=[sub{my($mykeys,$name)=@_;#如果未指定名称,则返回(current_key,current_ key_name)return($mykeys->[0][1],$mykeys->[0][0])if$名称;#如果找到匹配的键,则返回(matching_key,current_key_name)#给定的名称对于(我的$i=0;$i<@$mykeys;$i++){下一个条件是$name ne$mykeys->[$i][0];返回($mykeys->[$i][1],$mykeys->[0][0]);}#找不到匹配的密钥回报;},\@keys];my$srv=IO::Socket::SSL->new(…,SSL_ticket_keycb=>$keycb);
SSL_mode_release_buffers 1 |0

这将启用或禁用SSL对象上的SSL_MODE_RELEASE_BUFFERS选项。使用此选项,读取缓冲区将在每次SSL_read后释放,但需要为每个新的SSL_read重新分配。根据SSL_CTX_set_mode(3ssl)中的文档,如果担心内存使用,那么这可能会在同一时间内节省大量内存,每个空闲SSL连接大约34k。

接受

这与底层套接字类的accept函数的行为类似,但还包括初始SSL握手。但是,由于底层套接字类即使在非阻塞套接字上调用accept时也会返回阻塞文件句柄,因此新文件对象上的SSL握手将以阻塞方式完成。有关详细信息,请参阅有关非阻塞I/O的部分。如果你不喜欢这种行为,你应该接受TCP套接字,然后用升级启动_SSL稍后。

连接(…)

这与connect函数的行为类似,但也执行SSL握手。因为您不能为此函数提供特定于SSL的参数,所以最好使用新的创建连接SSL套接字或启动_SSL将已建立的TCP套接字升级为SSL。

关闭(…)

与简单INET套接字的关闭相反,SSL中的关闭还要求正确关闭SSL部分。这是通过两个对等方发送关闭通知消息来完成的。

因此,一个简单的实现将一直等到它收到来自对等方的close notify消息——这与close不会阻塞的通常预期语义相冲突。因此,默认行为是只发送关闭通知,而不等待对等方的关闭通知。如果需要的话SSL_快速_停机需要显式设置为false。

还有一些情况下根本不应该关闭SSL。例如,当分叉以让子进程处理套接字并在父进程中关闭套接字时,情况就是这样。天真的露骨关闭或者在销毁父进程中的套接字时隐式关闭会向对等进程发送关闭通知,这会使客户端进程中的SSL套接字不可用。在这种情况下,显式关闭具有SSL_no_关闭设置为true应该在父进程中完成。

有关更多详细信息和其他参数,请参见停止_SSL从以下位置呼叫关闭关闭套接字的SSL状态。

系统读取(BUF,LEN,[OFFSET])

此函数从外部的行为与系统读取在其他IO::插座对象,例如,它最多返回LEN字节的数据。但实际上,它不仅从底层套接字读取LEN字节,而且在单个SSL帧上读取。然后,它返回从这个SSL帧解密的最多LEN字节。如果帧包含的数据多于请求的数据,它将只返回LEN数据,缓冲其余数据,并在进一步读取调用时返回。这意味着,即使底层套接字不可读,也可能读取数据,因此使用poll或select可能不够。

sysread将只返回单个SSL帧的数据,例如,来自已缓冲帧的挂起数据,或者它将从底层套接字读取帧并返回解密的数据。它不会在一次调用中返回跨越多个SSL帧的数据。

此外,对sysread的调用可能会失败,因为它必须首先完成SSL握手。

如果您编写的应用程序使用事件循环和/或非阻塞套接字,那么理解这些行为至关重要。请阅读本文档中的特定部分。

系统写入(BUF,[LEN,[OFFSET]])

此函数从外部的行为与系统写入在其他IO::插座对象,例如,它最多会将LEN字节写入套接字,但不能保证所有LEN字节都已写入。它将返回写入的字节数。因为它基本上只是从OpenSSL调用SSL_write,syswrite最多只能写入一个SSL帧。这意味着,一次写入的字节数不超过16.384字节,这是SSL帧的最大大小。

对于非阻塞套接字,适用SSL特定行为。Pease阅读本文档中的特定部分。

预览(BUF、LEN、[OFFSET])

此函数的语法与系统读取,并执行几乎相同的任务,但不会提前读取位置,以便使用相同参数连续调用peek()将返回相同的结果。此函数需要OpenSSL 0.9.6a或更高版本才能工作。

挂起()

此函数为您提供无需从底层套接字对象读取的可用字节数。如果您使用事件循环,则此函数至关重要,请参阅有关轮询SSL套接字的部分。

get_fingerprint([algo,certificate,pubkey])

此方法返回表单中给定证书的指纹algo$digest_hex算法,其中阿尔戈是使用的算法,默认为“sha256”。如果没有提供证书,则使用连接的对等证书。如果公共密钥为true时,它不会返回证书的指纹,而是返回证书中公钥的指纹algo$pub$digest_hex算法.

get_fingerpint_bin([algo,certificate,pubkey])

此方法使用算法返回给定证书的二进制指纹阿尔戈,默认为“sha256”。如果没有提供证书,则使用连接的对等证书。如果公共密钥为true时,它不会返回证书的指纹,而是返回证书中公钥的指纹。

获取密码(_cipher)

返回IO::Socket::SSL对象正在使用的密码的字符串形式。

获取_sslversion()

返回已建立连接的SSL版本的字符串表示形式。

获取_sslversion_int()

返回已建立连接的SSL版本的整数表示形式。

获取会话引用()

如果会话被重用,则返回true,否则返回false。请注意,对于重用的会话,在握手过程中不会发送证书,也不会提供密码,因此依赖于此的功能可能无法工作。

dump_peer_certificate()

返回对等SSL证书中包含选择字段的可分析字符串。此方法直接返回Net::SSLeay的dump_peer_certificate()方法的结果。

对等证书($field;[$refresh])

如果存在对等证书,此函数可以从中检索值。如果没有给定字段,则返回Net::SSLeay中证书的内部表示。如果刷新为true,则不会使用缓存版本,但请再次检查,以防连接证书因重新协商而更改。

可以查询以下字段:

授权机构(别名发行人)

签署证书的证书颁发机构。

所有者(别名主题)

证书的所有者。

commonName(alias cn)-仅适用于Net::SSLeay版本>=1.30

通用名称,通常是SSL证书的服务器名称。

subjectAltNames-仅适用于Net::SSLeay版本>=1.33

主题的可选名称,通常是同一服务器的不同名称,如example.org、example.com、*.example.com。

它返回一个(typ,value)列表,其中包含typ GEN_DNS、GEN_IPADD等(这些常量从IO::Socket::SSL导出)。请参阅Net::SSLeay::X509_get_subjectAltNames。

sock_certificate($field)

这类似于对等证书(_C)但将返回站点自己的证书。相同的参数$字段可以使用。如果没有$字段将返回底层OpenSSL的证书句柄。这个句柄只有在SSL连接存在的情况下才有效,如果以后使用它,可能会导致应用程序发生奇怪的崩溃。

同行证书

这将返回对等方发送的所有证书,例如,首先对等方拥有证书,然后是链的其余部分。你可以使用CERT_as散列IO::套接字::SSL::实用程序检查每个证书。

此函数取决于Net::SSLeay的版本>=1.58。

获取服务器名称

如果使用了服务器名称指示(SNI),则会提供客户端请求的名称。

验证主机名($hostname、$scheme、$publicsuffix)

这将使用给定的方案根据对等证书验证给定的主机名。主机名通常是您在PeerAddr中指定的。请参阅SSL_verifycn_publicsuffix参数用于解释后缀检查和可能的值。

根据证书验证主机名在不同的应用程序和RFC之间是不同的。有些方案允许主机名使用通配符,有些仅在subjectAltNames中使用,甚至可以使用不同的通配符方案。RFC 6125提供了一个很好的概述。

为了便于验证,预定义了以下方案(可以使用协议名称和rfcXXXX名称):

rfc2818、xmpp(rfc3920)、ftp(rfc 4217)

subjectAltNames和通用名称中的扩展通配符是可能的,例如*.example.org或甚至www*.examplet.org。只有在subjectAltNames中没有提供DNS名称时,才会检查通用名称。

http(别名www)

虽然rfc2818中定义了名称检查,但只要没有定义subjectAltNames,当前浏览器通常也会接受通用名称中的IP地址(不带通配符)。因此,这是rfc2818扩展了此功能。

smtp(rfc2595)、imap、pop3、acap(rfc4642)、netconf(rfc5538)、syslog(rfc 5425)、snmp(rfc5953)

subjectAltNames中可以使用简单的通配符,例如*.example.org匹配www.exampleorg,但不匹配lala.www.exampel.org。如果subjectAltNames中没有匹配的内容,则会检查通用名称,其中也允许使用通配符匹配最左侧的完整标签。

ldap(rfc4513)

subjectAltNames中允许使用简单通配符,但公共名称中不允许使用。即使subjectAltNames存在,也会检查公用名。

sip(rfc5922)

不允许使用通配符,即使subjectAltNames存在,也会检查通用名称。

要点(rfc5971)

subjectAltNames和公用名中允许使用简单通配符,但只有当subjectAltNames中没有DNS名称时,才会检查公用名。

违约

这是所有规则的超集,如果没有给定方案,但已知主机名(而不是IP),则会自动使用。subjectAltNames中允许使用扩展通配符,并始终选中公用名和公用名。

没有人

不会进行任何验证。实际上,在这种情况下调用verify_hostname没有任何意义。

可以通过指定上述预定义方案之一的名称或使用具有以下键和值的散列来指定方案:

check_cn:0|“总是”|“仅当”

确定是否选中公用名称。如果“always”总是被选中(如ldap中),如果“when_only”只有在subjectAltNames(如http中)中没有给定名称时才被选中,对于任何其他值,公共名称都不会被选中。

通配符_in_alt:0|'full_label'|'anywhere'

确定subjectAltNames中的通配符是否可用以及在何处可用。如果“full_label”只适用于*.example.org这样的情况(如ldap中的情况),那么“anywhere”也适用于www*.examplet.org(如http),则不允许出现www.*.org甚至“*”这样的危险情况。为了与旧版本兼容,可以使用“最左边”代替“full_label”。

通配符_in_cn:0|'full_label'|'nywhere'

与通配符_in_alt类似,但检查通用名称。没有预定义的方案允许在通用名称中使用通配符。

ip_in_cn:0|1|4|6

确定公用名称中是否允许使用IP地址(不允许使用通配符)。如果设置为4或6,则只允许IPv4或IPv6地址,任何其他真值都允许这两个地址。

回调:\&coderef

如果为验证提供子例程,则将使用参数($hostname、$commonName、@subjectAltNames)调用该子例程,其中hostname是为验证提供的名称,commonName是peer_certificate('cn')的结果,subjectAltNames是peer_certificate的结果('subjectArtNames')。

在这种情况下,验证方案的所有其他参数都将被忽略。

下一个协议协商()

此方法返回协商协议的名称,例如“http/1.1”。它适用于SSL连接的客户端和服务器端。

Net::SSLeay 1.46+和openssl-1.0.1+支持NPN。要检查您可能会致电的支持IO::套接字:SSL->can_npn().

alpn_selected()

以字符串形式返回通过ALPN协商的协议,例如“http://1.1”、“http://2.0”或“spdy/3.1”。

Net::SSLeay 1.56+和openssl-1.0.2+支持ALPN。要检查支持,请使用IO::套接字::SSL->can_alpn().

errstr()

返回发生的最后一个错误(字符串形式)。如果没有可执行此方法的实际对象,请改为调用IO::Socket::SSL::errstr()。

对于非阻塞套接字上的读写错误,此方法可能包括字符串SSL需要先读取!SSL需要先写!这意味着另一方希望读取或写入套接字,并希望在您做任何事情之前得到满足。但对于0.98版本,最好将全局导出变量$SSL_ERROR与导出符号SSL_WANT_READ和SSL_VANT_WRITE进行比较。

打开()

如果无法打开套接字,则返回false;如果套接字可以打开并且SSL握手成功完成,则返回1;如果底层IO::Handle已打开,但SSL握手失败,则返回-1。

IO::套接字::SSL->start_SSL($Socket,…)

这将转换您提供给IO::socket::SSL对象的全局引用或套接字。您也可以通过参数来指定上下文或连接选项,就像调用new()一样。如果在接受的套接字上使用此函数,则必须将参数“SSL_server”设置为1,即IO::socket::SSL->start_SSL($socket,SSL_ser=>1)。如果您有一个继承自IO::Socket::SSL的类,并且您希望$Socket被加到您自己的类中,那么可以使用MyClass->start_SSL($sockets)来实现所需的效果。

注意,如果start_SSL()在SSL协商中失败,$socket将保留在其原始类中。对于非阻塞套接字,您最好将套接字升级为IO::socket::SSL,然后调用accept_SSL或connect_SSL和升级的对象。只需升级套接字集SSL_start握手显式设置为0。如果在不使用此参数的情况下调用start_SSL,它将恢复为accept_SSL和connect_SSL的阻塞行为。

如果给定参数“Timeout”,则在超时后未建立SSL连接时将停止。此参数仅用于阻塞套接字,如果未给定基础IO::Socket的默认超时,则将使用该参数。

stop_SSL(…)

这与start_SSL()、connect_SSL()和accept_SSL。它获得与close()相同的参数,实际上close()调用stop_SSL()(但不降级类)。

如果成功则返回true,如果失败则返回undef。这可能是非阻塞套接字的情况。在这种情况下$!设置为EWOULDBLOCK,ssl错误设置为ssl_WANT_READ或ssl_VANT_WRITE。在这种情况下,一旦套接字就绪,应该使用相同的参数再次重试调用。

用于从呼叫停止_SSL SSL_fast_关机默认为false,例如等待对等方的closenotify。如果您想降级套接字并继续将其用作普通套接字,那么这是必要的。

在stop_SSL之后,套接字可以再次用于交换纯数据。

连接_SSL,接受_SSL

如果套接字是用创建的,那么应该使用这些函数进行相关的握手新的或升级为启动_SSLSSL_start握手设置为false。在握手成功或抛出错误之前,它们将返回undef。只要函数返回undef和$!设置为EWOULDBLOCK时,可以在套接字可读(SSL_WANT_READ)或可写(SSL_FANT_WRITE)后重试调用。

设置msg_callback

这将在内部使用openssl SSL_set_msg_callback API为每条消息添加/删除用户定义的回调。要确保在握手开始之前回调处于活动状态,请将其与SSL_start握手=>0在前面的SSL对象设置中。要删除回调,请使用空回调函数显式调用它。

例子:

$sock=IO::Socket::SSL->new(….,SSL_startHandshake=>0);#设置回调$sock->set_msg_callback(\&cb,$cbarg1,$cbar2);$sock->connect_SSL();子cb{我的($sock,#有关以下参数,请参阅SSL_set_msg_callback$direction、$ssl_ver、$content_type、$buf、$len、$ssl、,$cbarg1、$cbarg2)= @_;...if(no_longer_need_cb){#禁用回调$sock->set_msg_callback(undef);}}
ocsp_求解器

这将创建一个OCSP解析器对象,可用于创建对SSL连接证书的OCSP请求。验证哪些证书取决于SSL_ocsp_模式:默认情况下,只检查叶证书,但使用SSLOCSP_FULL_CHAIN时,将检查所有链证书。

由于要创建OCSP请求,需要知道证书及其颁发者证书,因此在信任链不完整或证书是自签名的情况下,无法检查证书。

OCSP解析器是通过调用$ssl->ocsp_resolver并提供了以下方法:

硬错误(_E)

这将在检查OCSP响应时返回硬错误。硬错误是证书吊销。使用SSL_ocs_mode(SSL_ocs_mode)SSL_OCSP_FAIL_HARD的任何软错误(例如,未能获取有关证书的签名信息)也将被视为硬错误。

OCSP解析将在第一个硬错误时停止。

只要没有出现硬错误,并且仍有需要解决的请求,该方法就会返回undef。如果所有请求都得到了解决,并且没有出现硬错误,则该方法将返回''.

软错误(_E)

这将返回询问OCSP响应者时发生的软错误。

请求

这将返回一个包含(url,请求)-元组,例如,其中包含OCSP请求字符串和它应该发送到的URL。发送此类请求的通常方式是HTTP POST请求,其内容类型为应用程序/ocsp-request或者将带有base64和url-encoded请求的GET请求添加到url的路径。

处理完所有这些请求并用添加响应(_R)您最好再次调用此方法,以确保不再有未完成的请求。IO::Socket::SSL将在单个请求中组合同一服务器的多个OCSP请求,但某些服务器不会对所有这些请求作出响应,因此必须使用剩余的请求再次进行请求。

添加响应($uri,$response)

此方法获取在向发送OCSP请求时收到的响应的HTTP正文$uri(美元)。如果未收到响应或发生错误,则应重试或考虑$响应为空将触发软错误。

该方法返回的当前值为硬错误(_E)例如,无需执行更多请求时的定义值。

resolve_blocking(%args)

这结合了请求添加响应(_R)哪一个HTTP::微小以阻塞方式执行所有必要的请求。%参数将提供给HTTP::微小这样您可以在此处放置代理设置等。HTTP::微小将调用验证SSL因为OCSP响应有自己的签名,所以不需要额外的SSL验证。

如果不想使用阻塞请求,则需要使用自己的用户代理请求添加响应(_R).

IO::套接字::SSL->new_from_fd($fd,[mode],%sslargs)

这将把通过文件描述符标识的套接字转换为SSL套接字。注意,参数列表不包括“MODE”参数;如果您提供一个,它将被忽略(为了与IO::Socket::INET兼容)。相反,假定为“+<”模式,并且传递的文件描述符必须能够处理此类I/O,因为初始SSL握手需要双向通信。

在内部,给定的$fd将使用新的from_fd超类的方法(IO::插座::INET或类似),然后启动_SSL将使用给定的%sslargs(SSL参数).如果美元fd已经是一个IO::Socket对象,您最好调用启动_SSL直接。

IO::套接字::SSL::default_ca([path|dir|SSL_ca_file=…,SSL_ca_path=>…])>

确定或设置默认CA路径。如果给定了现有路径或目录或哈希,它会将默认CA路径设置为该值,并且永远不会尝试自动检测它。如果未定义它将忘记任何存储的默认值,并继续检测系统默认值。如果没有给定参数,它将开始检测系统默认值,除非它已经存储了用户设置或以前检测到的值。

系统默认值的检测与OpenSSL类似,例如,它将检查环境变量SSL_CERT_DIR中指定的目录或路径OPENSSLDIR/certs(SSLCERTS:在VMS上),以及环境变量SSR_CERT_file中指定的文件或路径OPENSSLDIR/CERT.pem(SSLCRTS:在VMS上的CERT.pem)。与OpenSSL相反,它将检查SSL_ca_path是否包含以散列作为文件名的PEM文件,以及SSL_ce_file是否与PEM类似。如果找不到可用的系统默认值,它将尝试加载并使用Mozilla::CA如果不可用,则放弃检测。检测结果将被保存,以加快未来的通话速度。

函数将保存的默认CA作为散列返回,其中包含SSL_CA\ufile和SSL_CA\upath。

IO::套接字::SSL::set_default_context(…)

您可以使用它使IO::Socket::SSL自动重用给定的上下文(除非在调用new()时被特别覆盖)。它接受一个参数,该参数应该是IO::Socket::SSL对象或IO::套接字::SSL::SSL_Context对象。有关更多详细信息,请参阅new()的SSL_reuse_ctx选项。请注意,这将全局设置默认上下文,因此请谨慎使用(尤其是在mod_perl脚本中)。

IO::套接字::SSL::set_default_session_cache(…)

您可以使用它使IO::Socket::SSL自动重用给定的会话缓存(除非在调用new()时被特别覆盖)。它接受一个参数,该参数应该是IO::Socket::SSL::Session_Cache对象或类似对象(例如,像IO::Socket::SSL::Session_Cache那样实现get_Session、add_Session和del_Session的对象)。有关更多详细信息,请参阅new()的SSL_session_cache选项。请注意,这将全局设置默认缓存,因此请谨慎使用。

IO::套接字::SSL::set_defaults(%args)

使用此函数,可以设置用于创建上下文的所有SSL_*参数的默认值,如SSL_verify*参数。可以提供任何SSL_*参数或以下简短版本:

模式-SSL_verify_mode
回调-SSL_verify_callback
方案-SSL_verifycn_scheme
名称-SSL_verifycn_name
IO::套接字::SSL::set_client_defaults(%args)

类似集合_错误,但仅设置客户端模式的默认值。

IO::套接字::SSL::set_server_defaults(%args)

类似集合_错误,但仅设置服务器模式的默认值。

IO::套接字::SSL::set_args_filter_hack(\&code |'use_defaults')

有时必须使用对SSL使用不需要或无效参数的代码,通常是禁用SSL验证或设置错误的密码或SSL版本。使用此黑客可以覆盖这些设置并恢复正常。例子:

IO::套接字::SSL::set_args_filter_hack(sub{my($is_server,$args)=@_;if(!$is_server){#客户端设置-使用默认CA启用验证#以及备用主机名验证等删除@{$args}{qw(SSL_验证模式SSL_ca_文件SSL_ca_路径SSL_验证方案SSL_版本)};#并为签名的已知证书添加一些指纹#未知CA或自签名$args->{SSL_fingerprint}=。。。}});

使用短设置set_args_filter_hack('use_defaults')在所有情况下,它都会首选默认设置。可以使用修改这些默认设置集合_错误,设置客户端默认值设置服务器错误.

以下方法不受支持(更不用说无效!),如果您愚蠢地使用了IO::Socket::SSL,它会发出一个很大的CROAK():

截断
斯达
ungetc公司
塞布夫
setvbuf(设置vbuf)
fdopen(打开)
发送/接收

注意,send()和recv()不能被绑定的文件句柄(例如IO::Socket::SSL使用的文件句柄)可靠捕获,因此可能会通过套接字发送未加密的数据。对这些函数的面向对象调用将失败,告诉您改用print/printf/syswrite和read/sysread系列。

折旧

以下函数已弃用,仅为了兼容性而保留:

上下文_信息()

如果要重用上下文,请使用SSL_reuse_ctx选项

socketToSSL()和socket_to_SSL()

改用IO::Socket::SSL->start_SSL()

kill_socket()

改用close()

获取证书()

请改用peer_certificate()函数。用于使用subject_name和issuer_name方法返回X509_Certificate。现在只返回具有这些方法的$self(尽管已弃用)。

发行人名称()

改用peer_certificate(“颁发者”)

subject_name()

改用peer_certificate(“subject”)

示例

请参阅“example”目录、“t”中的测试以及“util”中的工具。

漏洞

如果您将IO::Socket::SSL与线程一起使用,则应在创建使用它的任何其他线程之前,在主线程中加载它(例如,使用或要求)。这样会更快,因为它只会初始化一次。还有报道称,它可能会以另一种方式崩溃。

在一个线程中创建IO::Socket::SSL对象并在另一个线程中关闭它将不起作用。

IO::Socket::SSL无法与Storable::fd_retrieve/fd_store一起使用。有关更多信息以及如何解决该问题,请参阅BUGS文件。

Win32不支持非阻塞和超时(基于非阻塞),因为基础IO::Socket::INET不支持此平台上的非阻塞。

如果您有一台服务器,并且看起来有内存泄漏,则可以检查会话缓存的大小。Net::SSLeay的默认值似乎是20480,请参阅SSL_create_ctx_callback的示例以了解如何限制它。

关于会话重用的TLS 1.3支持不完整。

另请参阅

IO::插座::INET,IO::插槽::INET6,IO:,插座::IP,网络::SSLeay。

谢谢

非常感谢所有添加补丁或报告错误或以另一种方式帮助IO::Socket::SSL的人。请不断报告错误并帮助修补程序,即使它们只是修复了文档。

特别感谢Net::SSLeay团队的良好合作。

作者

Steffen Ullrich是目前的维护者。

彼得·贝罗齐(Peter Behroozi),<behrooz at fas.harvard.edu>

Marko Asplund是《IO::Socket::SSL》的原作者,他是kronodoc.fi>的<Marko.Asplund。

由不同人员合并的修补程序,请参阅文件更改。

版权

本模块的原始版本版权所有(C)1999-2002 Marko Asplund。

本模块的改写版本为版权所有(C)2002-2005 Peter Behroozi。

0.98及更新版本版权所有(C)2006-2014 Steffen Ullrich。

本模块为免费软件;您可以重新发布它和/或使用与Perl本身相同的条款对其进行修改。