Repository: mitmproxy/mitmproxy

Issue:
incomplete certificate generated by mitmproxy
#### Problem Description
A clear and concise description of what the bug is.

When using mitm proxy to fetch the following resource:

> https://document-storage-production-dot-remarkable-production.appspot.com/document-storage/json/2/docs

I get the following exception:

```
Caused by: java.security.cert.CertificateParsingException: X.509 Certificate is incomplete: SubjectAlternativeName extension MUST be marked critical when subject field is empty
        at java.base/sun.security.x509.X509CertInfo.verifyCert(X509CertInfo.java:751)
```
 
I don't know why, but the Subject of the certificate generated by mitmproxy does not get set to anything in this case.

If I force the SubjectAlternativeName extension to be critical by changing the source code as follows, the problem disappears and the certificate is accepted by Java:

```diff
(venv) sebster@eeyore:~/Work/mitmproxy$ git diff
diff --git a/mitmproxy/certs.py b/mitmproxy/certs.py
index a37d29bc..cbedc1fd 100644
--- a/mitmproxy/certs.py
+++ b/mitmproxy/certs.py
@@ -114,7 +114,7 @@ def dummy_cert(privkey, cacert, commonname, sans, organization):
     if ss:
         cert.set_version(2)
         cert.add_extensions(
-            [OpenSSL.crypto.X509Extension(b"subjectAltName", False, ss)])
+            [OpenSSL.crypto.X509Extension(b"subjectAltName", True, ss)])
     cert.add_extensions([
         OpenSSL.crypto.X509Extension(
             b"extendedKeyUsage",
```

#### Steps to reproduce the behavior:
1. Attempt to fetch the above resource with mitmproxy from a Java client (OpenJDK 11.0.1) 

#### System Information
Mitmproxy: 6.0.0.dev (+44, commit 7fdcbb0)
Python:    3.6.9
OpenSSL:   OpenSSL 1.1.1g  21 Apr 2020
Platform:  Linux-5.3.0-51-generic-x86_64-with-Ubuntu-18.04-bionic

incomplete certificate generated by mitmproxy
#### Problem Description
A clear and concise description of what the bug is.

When using mitm proxy to fetch the following resource:

> https://document-storage-production-dot-remarkable-production.appspot.com/document-storage/json/2/docs

I get the following exception:

```
Caused by: java.security.cert.CertificateParsingException: X.509 Certificate is incomplete: SubjectAlternativeName extension MUST be marked critical when subject field is empty
        at java.base/sun.security.x509.X509CertInfo.verifyCert(X509CertInfo.java:751)
```
 
I don't know why, but the Subject of the certificate generated by mitmproxy does not get set to anything in this case.

If I force the SubjectAlternativeName extension to be critical by changing the source code as follows, the problem disappears and the certificate is accepted by Java:

```diff
(venv) sebster@eeyore:~/Work/mitmproxy$ git diff
diff --git a/mitmproxy/certs.py b/mitmproxy/certs.py
index a37d29bc..cbedc1fd 100644
--- a/mitmproxy/certs.py
+++ b/mitmproxy/certs.py
@@ -114,7 +114,7 @@ def dummy_cert(privkey, cacert, commonname, sans, organization):
     if ss:
         cert.set_version(2)
         cert.add_extensions(
-            [OpenSSL.crypto.X509Extension(b"subjectAltName", False, ss)])
+            [OpenSSL.crypto.X509Extension(b"subjectAltName", True, ss)])
     cert.add_extensions([
         OpenSSL.crypto.X509Extension(
             b"extendedKeyUsage",
```

#### Steps to reproduce the behavior:
1. Attempt to fetch the above resource with mitmproxy from a Java client (OpenJDK 11.0.1) 

#### System Information
Mitmproxy: 6.0.0.dev (+44, commit 7fdcbb0)
Python:    3.6.9
OpenSSL:   OpenSSL 1.1.1g  21 Apr 2020
Platform:  Linux-5.3.0-51-generic-x86_64-with-Ubuntu-18.04-bionic



Discussion:

Resolving patch:
diff --git a/mitmproxy/certs.py b/mitmproxy/certs.py
--- a/mitmproxy/certs.py
+++ b/mitmproxy/certs.py
@@ -106,7 +106,10 @@ def dummy_cert(privkey, cacert, commonname, sans, organization):
     cert.gmtime_adj_notBefore(-3600 * 48)
     cert.gmtime_adj_notAfter(DEFAULT_EXP_DUMMY_CERT)
     cert.set_issuer(cacert.get_subject())
-    if commonname is not None and len(commonname) < 64:
+    is_valid_commonname = (
+        commonname is not None and len(commonname) < 64
+    )
+    if is_valid_commonname:
         cert.get_subject().CN = commonname
     if organization is not None:
         cert.get_subject().O = organization
@@ -114,7 +117,13 @@ def dummy_cert(privkey, cacert, commonname, sans, organization):
     if ss:
         cert.set_version(2)
         cert.add_extensions(
-            [OpenSSL.crypto.X509Extension(b"subjectAltName", False, ss)])
+            [OpenSSL.crypto.X509Extension(
+                b"subjectAltName",
+                # RFC 5280 §4.2.1.6: subjectAltName is critical if subject is empty.
+                not is_valid_commonname,
+                ss
+            )]
+        )
     cert.add_extensions([
         OpenSSL.crypto.X509Extension(
             b"extendedKeyUsage",


Test patch: