Fix ca_file containing multiple CAs (#6666)

#### Description

Unfortunately 0b5e310881 broke mitmproxy's
ability to issue leaf certificates if `ca_file` contains multiple CAs.
This PR restores that capability.

The issue lies in `mitmproxy/certs.py` - specifically, in the
`from_files` method of the `CertStore` class. Before
0b5e310881, the issuing CA was identified
like this:

``` python
raw = ca_file.read_bytes()
key = load_pem_private_key(raw, passphrase)
…
certs = re.split(rb"(?=-----BEGIN CERTIFICATE-----)", raw)
ca = Cert.from_pem(certs[1])
```

This worked even when `ca_file` contained multiple CAs. For example,
consider this example:

```
-----BEGIN PRIVATE KEY-----
REDACTED
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIICmTCCAfqgAwIBAgIRAIqaBJ6aU5wXT+5JAn8Uj1IwCgYIKoZIzj0EAwQwMDEu
MCwGA1UEAxMlbWFuc2VsbWkgSW50ZXJtZWRpYXRlIENBICgyMDIzLTA0LTExKTAe
Fw0yNDAyMTQxNzEzMDBaFw0yNDAyMTUxNzE0MDBaMB8xHTAbBgNVBAMTFG1pdG1w
cm94eSBJc3N1aW5nIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEeKTLpy6KeiWN
89S9UJoTM9w6HyrEiVByf/ie2wUX3vR/3nHVMTi5XZQD6/8h3A9yUWwtRtVDj55D
STbPKjN6G8t/Qh1dGz2vanNjmWNlceM3DOIlT30vmR5+GrUOwj+7o4HoMIHlMA4G
A1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwEgYD
VR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUkf9kgx5DbP0B66Ir6oLUDV/CIU4w
HwYDVR0jBBgwFoAUqBYQLb8rkIidwq5dYuiDFdKhY48wYAYMKwYBBAGCpGTGKEAB
BFAwTgIBAQQcbWlrZUBtYW5zZWxtaS5jb206aXNzdWluZy1jYQQrYmZhODBJaGMz
VXNUcU0yQlp6ZG1rYW1OLW1XNGtpWTJrM1g5X19kd3Q0bzAKBggqhkjOPQQDBAOB
jAAwgYgCQgF/QLZ0cThJKeoeIk4dY4+Z2MdIey7kvvy1jwlWrsOqOY+UXUWHXRlj
oXZN92imAKqgLU5T859MMqWrjA1LjN7w6QJCAY+2xN7ovry3WSMqwnu86NuPL8vh
fjftZeIW5GWuLzOsEFt7SfWMhkmFc/jcFpZ/qlolPhvr6E170zNhHUSMKvjb
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICcjCCAdSgAwIBAgIQeK+VyMjx85py1kqGdjn5nTAKBggqhkjOPQQDBDAoMSYw
JAYDVQQDEx1tYW5zZWxtaSBSb290IENBICgyMDIzLTA0LTExKTAeFw0yMzA0MTEw
MjIzMzdaFw0yODA0MDkwMjIzMzdaMDAxLjAsBgNVBAMTJW1hbnNlbG1pIEludGVy
bWVkaWF0ZSBDQSAoMjAyMy0wNC0xMSkwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYA
BADhbjtnME8IDpBab8MogM0HchH0V9B8Qx7nRXK+tbGyyHlgLjIBYF/erJGBP0md
RMAxU5x/nPlHPODRBkJhB0wbcwGC9zjQQyxfhG75806QzkaZSsC0kFZ9d3JG4fjn
uenY1S2VZ5qE0gDc8Qmfyjcx7KqxQKwS9RwtG1ymAmWgvmGIJ6OBlDCBkTAOBgNV
HQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUqBYQLb8r
kIidwq5dYuiDFdKhY48wHwYDVR0jBBgwFoAU6+JptrrPiOuWZyKvwg0EJz5TkW8w
KwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2NhLm1hbnNlbG1pLmNvbS9jcmwwCgYI
KoZIzj0EAwQDgYsAMIGHAkEw/A2kiL4Kt9/8PND89vlXEehqeymFgats62a3UMU2
g3zATES/WI2E1IgqIsmB6ILRgWMslzpH+NGP+JJkFCz9cwJCAJaRlnOc3xafHZta
0Xwr5YUVAAfmVOj9a2XAsNlNuMy2t8cmeeGmQjrZwCuivlzLOp19Ge3Mi0dnzeV+
5L2rBOCL
-----END CERTIFICATE-----
```

`certs` would have three elements: the private key, the issuing CA and
the intermediate CA. As a result, `ca = Cert.from_pem(certs[1])` would
select the first CA (the issuing CA).

From 0b5e310881 onward, we instead have

``` python
raw = ca_file.read_bytes()
key = load_pem_private_key(raw, passphrase)
…
certs = x509.load_pem_x509_certificates(raw)
ca = Cert(certs[-1])
```

Now, `certs` would have only two elements: the issuing CA and the
intermediate CA. (`x509.load_pem_x509_certificates` discards the private
key.) As a result, `ca = Cert(certs[-1])` must instead be `ca =
Cert(certs[0])`, otherwise the `ca` and `key` won't correspond to each
other and we'll eventually see an error like this when mitmproxy tries
to generate a leaf certificate:

```
Addon error: [('x509 certificate routines', '', 'key values mismatch')]
Traceback (most recent call last):
  File "/Users/manselmi/repos/mitmproxy/mitmproxy/addons/tlsconfig.py", line 208, in tls_start_client
    tls_start.ssl_conn.use_privatekey(
  File "/Users/manselmi/virtualenv/mitmproxy-py312/lib/python3.12/site-packages/OpenSSL/SSL.py", line 1949, in use_privatekey
    self._context._raise_passphrase_exception()
  File "/Users/manselmi/virtualenv/mitmproxy-py312/lib/python3.12/site-packages/OpenSSL/SSL.py", line 1123, in _raise_passphrase_exception
    _raise_current_error()
  File "/Users/manselmi/virtualenv/mitmproxy-py312/lib/python3.12/site-packages/OpenSSL/_util.py", line 57, in exception_from_error_queue
    raise exception_type(errors)
OpenSSL.SSL.Error: [('x509 certificate routines', '', 'key values mismatch')]
```
This commit is contained in:
Mike Anselmi 2024-02-16 14:37:24 -05:00 committed by GitHub
parent fbc373bc9d
commit 3ba4b65ecd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 3 additions and 1 deletions

View file

@ -17,6 +17,8 @@
([#6648](https://github.com/mitmproxymitmproxy/pull/6648), @sujaldev)
* Fix bug where wireguard config is generated with incorrect endpoint when two or more NICs are active.
([#6659](https://github.com/mitmproxy/mitmproxy/pull/6659), @basedBaba)
* Fix a regression when leaf cert creation would fail with intermediate CAs in `ca_file`.
([#6666](https://github.com/mitmproxy/mitmproxy/pull/6666), @manselmi)
## 21 January 2024: mitmproxy 10.2.2

View file

@ -415,7 +415,7 @@ class CertStore:
key = load_pem_private_key(raw, passphrase)
dh = cls.load_dhparam(dhparam_file)
certs = x509.load_pem_x509_certificates(raw)
ca = Cert(certs[-1])
ca = Cert(certs[0])
if len(certs) > 1:
chain_file: Path | None = ca_file
else: