使用Python验证SSL中的对等体

9 投票
4 回答
7679 浏览
提问于 2025-04-15 14:49

我在尝试了解如何在Python中验证一个自签名证书的服务器时,发现网上的信息不多。我还想确保服务器的URL是正确的。

提前感谢大家的帮助。

4 个回答

1

你问了“如何在Python中验证SSL的对等方”。

我通常会使用一个本地的“信任库”——这是一个存放着很多 .crt.pem.der 文件的文件夹。通过这个本地的证书目录来验证服务器是否可信:

#!/usr/bin/env python3
from socket import socket
import ssl

hostname = 'google.com'
port = 443
with socket() as sock:
    sock.setblocking(True)
    sock.connect((hostname, port))
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    context.verify_flags = 0x80000  # will verify if either the Root CA or Int CA is present for the server
    context.load_verify_locations(cafile=None,
                                  cadata=None,
                                  capath="ca_files/")
    with context.wrap_socket(sock=sock,
                             server_hostname=hostname,
                             do_handshake_on_connect=False) as ssl_sock:
        ssl_sock.do_handshake()
        print(hostname, ssl_sock.getpeername(), ssl_sock.version())
        print(ssl_sock)

正如 @abbott 所说——在你运行那个脚本之前,你需要设置 capath 的符号链接才能正常工作。如果你安装了 OpenSSL,这非常简单:

$ export CERTS=/path/to/ca_files
$ openssl/bin/c_rehash ${CERTS}

如果你不运行 c_rehash,它就不会工作。想了解更多信息,可以查看 man c_rehash

2

我猜你在使用某种OpenSSL的绑定库。我看到有两种方法可以解决你的问题。

  1. 你可以把你的证书放到openssl的目录里(运行 openssl version -d 可以查看你系统的目录)。这样做会影响你电脑上所有使用openssl的程序。
  2. 在运行时加载证书并添加它(下面的代码示例是针对PyOpenSSL的,但其他绑定库应该也差不多):

.

x509 = OpenSSL.crypto.load_certificate(...)
ctx = OpenSSL.SSL.Context(...)
store = ctx.get_cert_store()
store.add_cert(x509)
ctx.set_verify(VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, ...)
12

从我第一次回复的评论中,我发现大家对“验证证书”是什么意思有一些误解。我会在这里简单解释一下,以消除一些误解。

证书验证就是检查证书上的签名信息(比如证书的持有者、有效期、扩展信息等)是否和某个加密签名匹配。

如果你只有一个自签名证书来进行验证,你就无法区分它和另一个完全相同信息但使用不同密钥的自签名证书,除非你事先知道这个证书的密钥。而且不要忘了,你建立这个验证过程的目的就是为了不需要事先共享这些知识。通过常规的证书验证,你无法完全消除需要一些预先共享知识的要求,这些知识就是一组第三方证书,也称为“CA证书”。因为这些知识是事先共享的,所以这些证书可能是自签名的,但要记住,你获得这些证书有效性的信息并不是通过验证过程得到的,而是来自外部知识。

当你有一组可信的“CA证书”在各个节点之间分发时,你可以用这些证书来签署其他证书,并根据这些可信的CA的预共享知识来检查签名。

但是,如果你对一个自签名证书没有任何额外的知识,仅仅只有这个证书本身,你就无法对这个特定证书的可信度做出任何假设,因为它可能是某个恶意黑客发出的,也可能是你可信的服务器发出的。

在实施任何证书验证过程之前,请了解一下中间人攻击公钥基础设施公钥加密的相关知识。

请理解,盲目验证一个自签名证书并不能保护你免受即使是你自己网络中的聪明黑客的攻击,更不用说一般的互联网安全了。

编辑:问题的作者澄清说他实际上是在寻找如何使用M2Crypto绑定来验证一个verisign(或其他CA)签名的证书。这里有两个例子:

from M2Crypto import X509, SSL

# manual validation of a signature on a certificate using a given CA cert:
ca = X509.load_cert('/path/to/ca_cert.pem')
cert = X509.load_cert('certificate_to_validate.pem')
print "Verification results:", cert.verify(ca.get_pubkey())

# adding a given CA cert to the SSL Context for verification
ctx = SSL.Context()
# load a certificate from file
ctx.load_verify_locations(cafile='/path/to/ca_cert.pem') 
# or use all certificate in a CA directory
ctx.load_verify_locations(capath='/path/to/ca/dir') 
# or you can specify both options at the same time.

如果你打算使用一个包含许多CA证书的目录(这通常更方便),你必须将每个证书重命名为<hash>.0,其中<hash>是证书持有者的哈希值(可以通过openssl x509 -noout -hash -in cert.pem获得)。

撰写回答