python ssl(等同于openssl s_client -showcerts)如何从服务器获取客户端证书的CA列表 [英] python ssl (eqivalent of openssl s_client -showcerts ) How to get list of CAs for client certs from server

查看:352
本文介绍了python ssl(等同于openssl s_client -showcerts)如何从服务器获取客户端证书的CA列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组服务器,它们接受客户端证书.如果我使用Web浏览器,则Web浏览器似乎会收到客户端证书的有效CA列表.浏览器仅显示由这些CA之一签名的客户端证书.

I have a group of servers, that accept client certificates. If I use a web browser, then the web browser seems to receive a list of valid CAs for client certs. The browser shows only client certs signed by one of these CAs.

以下openssl命令为我提供了CA证书列表:

Following openssl command gives me a list of CA certs:

openssl s_client -showcerts -servername myserver.com -connect myserver.com:443 </dev/null

我感兴趣的行如下所示:

The lines I am interested in look following way:

---
Acceptable client certificate CA names
/C=XX/O=XX XXXX
/C=YY/O=Y/OU=YY YYYYYL
...
Client Certificate Types: RSA sign, DSA sign, ECDSA sign

如何使用python获取相同信息:

How can I get the same information with python:

我确实具有以下代码段,该代码段允许获取服务器的证书,但是此代码不会返回客户端证书的CA列表.

I do have following code snippet, that allows to obtain a server's certificate, but this code does not return the list of CAs for client certs.

import ssl

def get_server_cert(hostname, port):
    conn = ssl.create_connection((hostname, port))
    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    sock = context.wrap_socket(conn, server_hostname=hostname)
    cert = sock.getpeercert(True)
    cert = ssl.DER_cert_to_PEM_cert(cert)
    return cerft

我希望找到与 getpeercert()等效的功能,类似于 getpeercas(),但什么也没找到.

I expected to find a functional equivalent of getpeercert(), something like getpeercas() but didn't find anything.

当前解决方法:

import os
import subprocess


def get_client_cert_cas(hostname, port):
    """
    returns a list of CAs, for which client certs are accepted
    """

    cmd = [
        "openssl",
        "s_client",
        "-showcerts",
        "-servername",  hostname,
        "-connect",  hostname + ":" + str(port),
        ]

    stdin = open(os.devnull, "r")
    stderr = open(os.devnull, "w")

    output = subprocess.check_output(cmd, stdin=stdin, stderr=stderr)
    ca_signatures = []
    state = 0
    for line in output.decode().split("\n"):
        print(state, line)
        if state == 0:
            if line == "Acceptable client certificate CA names":
                state = 1
        elif state == 1:
            if line.startswith("Client Certificate Types:"):
                break
            ca_signatures.append(line)
    return ca_signatures

更新:使用pyopenssl解决方案(感谢Steffen Ullrich)

@Steffen Ulrich建议使用pyopenssl,该方法具有 get_client_ca_list()方法,这有助于我编写一个小的代码段.

@Steffen Ulrich suggested to use pyopenssl, which has a method get_client_ca_list() and this helped me to write a small code snippet.

以下代码似乎有效.不确定是否可以改进或是否有坑坑洼洼.

Below code seems to work. Not sure if it can be improved or whether there are any pit falls.

如果在接下来的几天内没有人回答,我会将其发布为潜在答案.

If nobody is answering within the next days I will post this as a potential answer.

import socket
from OpenSSL import SSL

def get_client_cert_cas(hostname, port):
    ctx = SSL.Context(SSL.SSLv23_METHOD)
    # If we don't force to NOT use TLSv1.3 get_client_ca_list() returns
    # an empty result
    ctx.set_options(SSL.OP_NO_TLSv1_3)
    sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
    # next line for SNI
    sock.set_tlsext_host_name(hostname.encode("utf-8"))
    sock.connect((hostname, port))
    # without handshake get_client_ca_list will be empty
    sock.do_handshake()  
    return sock.get_client_ca_list()

更新:2021-03-31

上面建议的使用 pyopenssl 的解决方案在大多数情况下都有效.但是,在执行 sock.connect((hostname,port))后,不能立即调用 sock.get_client_ca_list()).在这两个命令之间似乎需要采取一些措施.

Above suggested solution using pyopenssl works in most cases. However sock.get_client_ca_list()) cannot be called immediately after performing a sock.connect((hostname, port)) Some actions seem to be required in between these two commands.

最初,我使用的是 sock.send(b"G"),但是现在我使用的是 sock.do_handshake()`,看起来更干净了.

Initially I used sock.send(b"G"), but now I use sock.do_handshake()`, which seems a little cleaner.

即使是陌生人,该解决方案也不适用于TLSv1.3

Even stranger, the solution doesn't work with TLSv1.3

推荐答案

在@Steffen Ullrich的帮助下,我找到了以下解决方案,适用于大多数服务器.

With @Steffen Ullrich's help I found following solution, which works for most servers.

但是我遇到解决方案不起作用的情况因此,我删除了正确"的内容.回答检查,直到该解决方案适用于所有https服务器

However I encountered situations, where the solution is not working Thus I remove the "correct" answer check until the solution works with all https servers

它需要安装一个外部软件包

It requires to install an external package

pip install pyopenssl

然后以下工作将起作用:

Then following work will work:

import socket
from OpenSSL import SSL

def get_client_cert_cas(hostname, port):
    ctx = SSL.Context(SSL.SSLv23_METHOD)
    # If we don't force to NOT use TLSv1.3 get_client_ca_list() returns
    # an empty result
    ctx.set_options(SSL.OP_NO_TLSv1_3)
    sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
    # next line for SNI
    sock.set_tlsext_host_name(hostname.encode("utf-8"))
    sock.connect((hostname, port))

    # without handshake get_client_ca_list will be empty
    sock.do_handshake()  
    return sock.get_client_ca_list()

需要行 sock.do_handshake()来触发足够的SSL协议.否则,似乎不会填充client_ca_list信息.

The line sock.do_handshake() is required to trigger enough of the SSL protocol. Otherwise client_ca_list information doesn't seem to be populated.

至少对于我测试过的服务器,必须确保未使用TLSv1.3.我不知道这是错误,功能还是使用TLSv1.3,在调用 get_client_ca_list()

At least for the servers, that I tested I had to make sure TLSv1.3 is not used. I don't know whether this is a bug, a feature or whether with TLSv1.3 another function has to be called prior to calling get_client_ca_list()

我不是pyopenssl专家,但可以想象,有一种更优雅/更明确的方式来获得相同的行为.

I am no pyopenssl expert, but could imagine, that there is a more elegant / more explicit way to get the same behavior.

但是到目前为止,这对我遇到的所有服务器都有效.

but so far this works for me for all the servers, that I encountered.

这篇关于python ssl(等同于openssl s_client -showcerts)如何从服务器获取客户端证书的CA列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆