Какова правильная цель сертификата для клиента SSL в python?

Я настраиваю проверку SSL-клиента в своем приложении Python. На данный момент мой код проверки концепции падает, просто устанавливая безопасное соединение.

Похоже, что сгенерированные мной сертификаты либо используют сертификат без необходимых разрешений (более вероятно, IMO), либо имеют разрешения, которые сервер не может понять или принять (чуть менее вероятно, IMO ).

Это должно быть относительно тривиально, но я не могу найти нужную документацию.

Я создал сертификат сервера и клиента через OpenSSL. Я делал это в прошлом для других приложений без каких-либо проблем. Но я гораздо меньше знаком с созданием клиентских сертификатов. OpenSSL сообщает, что сертификат клиента, который я использую, имеет расширения:

X509v3 extensions:
    X509v3 Subject Key Identifier: 
        AF:AB:9D:AA:88:96:F4:0C:F5:56:9A:2C:DB:B6:BA:D9:DD:11:69:45
    X509v3 Subject Alternative Name: 
        email:[email protected]
    X509v3 Basic Constraints: 
        CA:FALSE
    Netscape Cert Type: 
        SSL Client
    X509v3 Authority Key Identifier: 
        keyid:E1:35:7C:39:7F:39:A4:43:D2:F8:00:59:38:91:71:AF:B9:38:AD:3F

    X509v3 Key Usage: 
        Digital Signature, Key Encipherment
    X509v3 Extended Key Usage: 
        TLS Web Client Authentication

Тривиальный тестовый код сервера:

import ssl
import socket
import logging

_log = logging.getLogger(__name__)


def main():
    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_cert_chain("1B.pem", "key2.pem")
    context.verify_mode = ssl.CERT_REQUIRED
    context.load_verify_locations("my_ca.crt")

    raw_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
    try:
        # domain replaced for SO question
        raw_server_socket.bind(('neptune.example.com', 8812))
        raw_server_socket.listen(5)
        server_socket = context.wrap_socket(raw_server_socket, server_side=True)
    except Exception:
        raw_server_socket.close()
        raise

    with server_socket:
        while True:
            try:
                connection_to_client, address = server_socket.accept()
                with connection_to_client:
                    connection_to_client.write(b'Hello')
            except Exception as ex:
                print(ex)


if __name__ == "__main__":
    main()

Это дает ошибку:

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unsupported certificate purpose (_ssl.c:1076)

... Когда клиент подключился к этому:

import socket
import ssl

context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
context.load_cert_chain("1C.pem", "key.pem")

raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Domain changed for SO question
conn = context.wrap_socket(raw_socket, server_side=False, server_hostname="neptune.example.com")
conn.connect(("neptune.example.com", 8812))
conn.close()


person Philip Couling    schedule 15.03.2020    source источник
comment
Сертификат клиента с точно таким же использованием ключа, расширенным использованием ключа, типом сертификата netscape и базовыми ограничениями работает без проблем для меня. Обратите внимание, что эта ошибка также может исходить от сертификата в цепочке, поэтому было бы неплохо указать все задействованные сертификаты или точный способ их создания, чтобы воспроизвести вашу проблему. Или вы можете попробовать тестовые сертификаты на github.com/noxxi/ p5-io-socket-ssl/tree/master/certs, который я использовал (client-*.pem, server-*.pem, test-ca.pem)   -  person Steffen Ullrich    schedule 15.03.2020


Ответы (1)


Как намекнул Штеффен Ульрих, похоже, проблема была не в самом сертификате, а в сертификате ЦС, который я использовал для его подписи.

Сертификаты ЦС ограничены в правах, которые они могут авторизовать. Они представлены в тех же расширениях, что и сертификаты, которые они подписывают. Таким образом, чтобы ЦС мог подписать сертификат как клиентский SSL-сертификат, и, и клиентский сертификат, и сертификат ЦС должны иметь:

  • Использование ключа с «цифровой подписью».
  • расширение 1.3.6.1.5.5.7.3.2, также известное как TLS Web Client Authentication

То есть openssl должен сообщать следующее для как сертификата ЦС, так и подписанного сертификата клиента:

X509v3 Key Usage: 
    Digital Signature
X509v3 Extended Key Usage: 
    TLS Web Client Authentication

В моем случае ЦС не имел расширенного использования 1.3.6.1.5.5.7.3.2. Это было нормально для сертификатов сервера, но не могло подписывать клиентские сертификаты.

person Philip Couling    schedule 17.03.2020