admin管理员组

文章数量:1387420

I've generated a private key and a self-signed certificate 3 different ways: using command-line openssl, pyopenssl and pyca/cryptography. Then I've used them to create ssl context for a simple flask app. The flask app itself runs fine, but when I try to send a file to it using requests.post(url, files={"file": my_file}, verify=my_cert), I get an ssl error:

SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1007)'))

Trying to mess around with certificate extensions hasn't solved the issue so far. Using verify=False would be equal to giving up. The surprising part (to me) is that all the methods of self-signed cert generation lead to the same error: the cert being recognised as self-signed.

I can add further details if needed (python version, pyopenssl version, system ssl version and so on).

Any pointers'd be appreciated.

Here's a function that creates the self-signed certificates:

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import datetime
import ipaddress

def create_self_signed_cert(cert_file, key_file, ip_address):
    # Generate private key - same as OpenSSL's -newkey rsa:4096
    key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
    
    # Simple subject/issuer like OpenSSL
    subject = issuer = x509.Name([
        x509.NameAttribute(NameOID.COMMON_NAME, ip_address),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Some Name"),
    ])
    
    # Build certificate - match OpenSSL's basic self-signed cert
    cert_builder = x509.CertificateBuilder().subject_name(
        subject
    ).issuer_name(
        issuer
    ).public_key(
        key.public_key()
    ).serial_number(
        x509.random_serial_number()
    ).not_valid_before(
        datetime.datetime.utcnow()
    ).not_valid_after(
        datetime.datetime.utcnow() + datetime.timedelta(days=365)
    )

    # Add the exact extensions that OpenSSL adds for server certs
    cert_builder = cert_builder.add_extension(
        x509.SubjectAlternativeName([
            x509.DNSName(ip_address),
            x509.IPAddress(ipaddress.ip_address(ip_address))
        ]),
        critical=False
    )
    
    # Basic Constraints marked as critical - this is a CA cert
    cert_builder = cert_builder.add_extension(
        x509.BasicConstraints(ca=True, path_length=None),
        critical=True
    )

    # Key Usage extension
    cert_builder = cert_builder.add_extension(
        x509.KeyUsage(
            digital_signature=True,
            content_commitment=False,
            key_encipherment=True,
            data_encipherment=False,
            key_agreement=False,
            key_cert_sign=True,
            crl_sign=True,
            encipher_only=False,
            decipher_only=False
        ),
        critical=True
    )

    # Extended Key Usage
    cert_builder = cert_builder.add_extension(
        x509.ExtendedKeyUsage([
            x509.oid.ExtendedKeyUsageOID.SERVER_AUTH,
            x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH
        ]),
        critical=False
    )
    
    # Subject Key Identifier
    cert_builder = cert_builder.add_extension(
        x509.SubjectKeyIdentifier.from_public_key(key.public_key()),
        critical=False
    )

    # Authority Key Identifier
    cert_builder = cert_builder.add_extension(
        x509.AuthorityKeyIdentifier.from_issuer_public_key(key.public_key()),
        critical=False
    )
    
    # Sign with SHA512 
    cert = cert_builder.sign(private_key=key, algorithm=hashes.SHA512())
    
    # Write certificate and key in PEM format
    with open(cert_file, "wb") as f:
        f.write(cert.public_bytes(serialization.Encoding.PEM))
    
    with open(key_file, "wb") as f:
        f.write(key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption()
        ))

create_self_signed_cert("selfsigned.crt", "private.key", "192.168.1.172")

(the whole experiment is setup in a local network)

The flask app is pretty basic:

from flask import Flask, request
from pathlib import Path

from crypto_common import verify_descriptor
from utilsworking_utils import get_ip_address_2


app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

@app.route("/tor", methods=["GET", "POST"])
def accept_descriptor():
    if request.method == "GET":
        return "<p>You're supposed to send descriptors here</p>"
    if request.method == "POST":
        # print(f"we've got this POSTed to us: {request.data}")
        print(f"the files we've got are {request.files}")
        print("Saving our file...")
        file = request.files["file"]
        our_folder = Path("received_descriptors/")
        our_filepath = our_folder / file.filename
        our_folder.mkdir(exist_ok=True, parents=True)
        descriptor_content = file.read().decode("utf-8")
        if verify_descriptor(descriptor_content) == 0:
            print("Saving a valid descriptor to file...")
            with our_filepath.open("w", encoding="utf-8") as f:
                f.write(descriptor_content)
            return "OK"
        else:
            print("Descriptor verification failed;\nnot saving.")
            return "Not OK"    



app.debug = False


def run_flask_app(dir_port=10330, 
                  cert="selfsigned.crt", 
                  key="private.key"):
    #interface = "wlp4s0"
    ip = get_ip_address_2()[1]

    app.run(host=ip, port=dir_port, ssl_context=(cert, key))

And then setup a request:

import requests

files = {"file": b"example"}

requests.post("192.168.1.172", files=file, verify=selfsigned.crt)

And there you go.

The line using openssl is pretty standard:

openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365

本文标签: python requestsSelfsigned ssl certs verificationStack Overflow