我最近与pyOpenSSL
合作,但是我遇到了一些使用SNI
为同一IP地址提供多个证书的网址。这是我的代码:
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()
例如,我们说我有以下网址:
https://example.com
当我得到以下输出时:
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/
,如果它无效,请直接拒绝该连接。我怎么能做到这一点?
答案 0 :(得分:3)
您需要使用set_tlsext_host_name
。来自the documentation:
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.
除了您的主机名验证错误,因为它只与CN进行比较,而不是主题替代名称。此外,它允许任何地方的通配符违反规则,只允许在最左边的标签中使用通配符:*.example.com
没有问题,www.*.com
甚至*.*.*
是不允许的,但是代码。