简体   繁体   中英

What is the correct certificate purpose for SSL client in python?

I'm setting up SSL client verification in my python app. At the moment my proof-of-concept code is falling over just establishing a secure connection.

It looks like the certificates I've generated either have a certificate usage without the necessary permissions ( more likely IMO ) or they have permissions that the server cannot understand or accept ( a little less likely IMO ).

This should be relatively trivial, but I can't find the right documentation.

I've generated the server and client certificate through OpenSSL. I've done this in the past for other apps without any problem. But I'm much less familiar with creating client certificates. OpenSSL reports that the client certificate I'm using has extensions:

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:a@example.com
    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

The trivial server test code is:

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()

This gives the error:

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

... When the client connected with this:

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()

As hinted at by Steffen Ullrich, it looks like the problem was not with the certificate itself but the CA certificate I used to sign it.

CA certificates are limited in the rights they can authorize. These are represented in the same extensions as the certificates they sign. So for a CA to sign a certificate as a client SSL certificate both the client certificate and the CA certificate must have:

  • Key usage with "digital signature".
  • the 1.3.6.1.5.5.7.3.2 extension AKA TLS Web Client Authentication

That is, openssl should report the following for both the CA certificate and the signed client cetificate:

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

In my case, the CA didn't have the extended use 1.3.6.1.5.5.7.3.2 . It was fine for server certificates but couldn't sign client certificates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM