Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ssl/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用pyOpenSSL-Python处理SNI_Python_Ssl_Https_Sni - Fatal编程技术网

使用pyOpenSSL-Python处理SNI

使用pyOpenSSL-Python处理SNI,python,ssl,https,sni,Python,Ssl,Https,Sni,我最近正在使用pyOpenSSL,但是我遇到了一些使用SNI为同一IP地址提供多个证书的URL。这是我的密码: from OpenSSL import SSL from socket import socket from sys import argv, stdout import re from urlparse import urlparse def callback(conn, cert, errno, depth, result): if depth == 0 and (err

我最近正在使用
pyOpenSSL
,但是我遇到了一些使用
SNI
为同一IP地址提供多个证书的URL。这是我的密码:

from OpenSSL import SSL
from socket import socket
from sys import argv, stdout
import re
from urlparse import urlparse

def callback(conn, cert, errno, depth, result):
    if depth == 0 and (errno == 9 or errno == 10):
        return False # or raise Exception("Certificate not yet valid or expired")
    return True

def main():
    if len(argv) < 2:
            print 'Usage: %s <hostname>' % (argv[0],)
            return 1

    o = urlparse(argv[1])
    host_name = o.netloc
    context = SSL.Context(SSL.TLSv1_METHOD) # Use TLS Method
    context.set_options(SSL.OP_NO_SSLv2) # Don't accept SSLv2
    context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
                       callback)
    # context.load_verify_locations(ca_file, ca_path)

    sock = socket()
    ssl_sock = SSL.Connection(context, sock)
    ssl_sock.connect((host_name, 443))
    ssl_sock.do_handshake()

    cert = ssl_sock.get_peer_certificate()
    common_name = cert.get_subject().commonName.decode()
    print "Common Name: ", common_name
    print "Cert number: ", cert.get_serial_number()
    regex = common_name.replace('.', r'\.').replace('*',r'.*') + '$'
    if re.match(regex, host_name):
        print "matches"
    else:
        print "invalid"

if __name__ == "__main__":
    main()
当我得到以下输出时:

python sni.py https://example.com/
Common Name:  *.example.com
Cert number:  63694395280496902491340707875731768741
invalid
对于
https://another.example.com

python sni.py https://another.example.com/
Common Name:  *.example.com
Cert number:  63694395280496902491340707875731768741
matches

但是,假设
https://another.example.com
已过期,连接将被接受,因为它使用的是有效的
*.example.com
证书。但是我希望能够使用
https://another.example.com/
如果无效,则直接拒绝连接。我怎样才能做到这一点

您需要使用
设置主机名
。发件人:


除此之外,主机名验证是错误的,因为它只与CN进行比较,而不与主题替代名称进行比较。它还允许在任何地方使用通配符,这违反了只允许在最左边的标签上使用通配符的规则:
*。example.com
很好,而
www.*.com
甚至
*。*.
是不允许的,但您的代码会接受。

您能详细说明主机名验证吗?@philippe:多合适主机名验证的完成实际上超出了原始问题的范围。但它本质上可以归结为首先检查主题备选名称(SAN),如果SAN中没有给出主机名,则只使用CN。并且通配符只能位于最左边的标签中。有关更多详细信息,请参阅或查看ssl.py中的
match_hostname
函数的实现。
python sni.py https://another.example.com/
Common Name:  *.example.com
Cert number:  63694395280496902491340707875731768741
matches
Connection.set_tlsext_host_name(name)
  Specify the byte string to send as the server name in the client hello message.
  New in version 0.13.