aio-libs / aiohttp

Asynchronous HTTP client/server framework for asyncio and Python
https://docs.aiohttp.org
Other
15.13k stars 2.02k forks source link

Peercert is None #7466

Open vasylborovets1 opened 1 year ago

vasylborovets1 commented 1 year ago

Describe the bug

Why request.transport.get_extra_info("peercert") return None if the connection is secured and the client has his own certificate Or how to get information about client certificate in "hello" function.

To Reproduce

Server example:

import os
import ssl

async def hello(request):
    cert = request.transport.get_extra_info("peercert") # return None

    # I want to get client certificate properties here

    return web.Response(text="Hello world")

def create_ssl_context_server():
    ssl_cntx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    localhost_pem = os.path.abspath(os.environ["CERT_PATH"])
    private_key = os.path.abspath(os.environ["KEY_PATH"])
    ssl_cntx.load_cert_chain(
        certfile=localhost_pem,
        keyfile=private_key,
    )
    ssl_cntx.ski = cert_file_ski_validate(localhost_pem)  # type: ignore
    ssl_cntx.self_signed = int(self_signed)  # type: ignore
    return ssl_cntx

def create_app():

    app = aiohttp.web.Application()
    app.router.add_route("GET", "/hello", hello)

    return app

if __name__ == "__main__":

    ssl_context_server = create_ssl_context_server()
    app = create_app()

    web.run_app(app, ssl_context=ssl_context_server)`

Client example: `

import ssl
import aiohttp
import asyncio

def client_session(url):
    ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    ssl_context.load_cert_chain(
        certfile="<path to client cert.pem>",
        keyfile="<path to client key.pem>"
    )

    session = aiohttp.ClientSession(
            base_url=f"https://{url}",
            connector=aiohttp.TCPConnector(ssl=ssl_context, force_close=True, enable_cleanup_closed=True),
        )
    return session

async def main():
    session = client_session("localhost:8000")

    async with session.get('/hello') as response:

        if response.status == 200:
            rtxt = await response.text()

            print(rtxt)

if __name__ == "__main__":
    asyncio.run(main())

Expected behavior

I expect to get client certificate by request.transport.get_extra_info("peercert")

Logs/tracebacks

I use Client session because in the real project I need a secured websocket.

Python Version

Python 3.10.12

aiohttp Version

Version: 3.8.4

multidict Version

Version: 6.0.4

yarl Version

Version: 1.8.2

OS

Ubuntu 20.04

Related component

Server, Client

Additional context

No response

Code of Conduct

Dreamsorcerer commented 1 year ago

No idea, but the method comes from asyncio: https://docs.python.org/3/library/asyncio-protocol.html#asyncio.BaseTransport.get_extra_info

If you have time to look into it and figure it out, then please open a PR.