Repository: twisted/twisted

Issue:
t.i.ssl.Certificate __repr__ assumes certificate will have a common name.
|<img alt="mmcc's avatar" src="https://avatars.githubusercontent.com/u/0?s=50" width="50" height="50">| mmcc reported|
|-|-|
|Trac ID|trac#5851|
|Type|defect|
|Created|2012-08-06 22:29:24Z|

It raises AttributeError when a cert has no common name.

```python
>>> from twisted.internet import ssl
>>>pemfn = 'UbuntuOne-Go_Daddy_CA.pem'
>>> pemd = open(pemfn, 'r').read()
>>> ssl.Certificate.loadPEM(pemd)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "Twisted-11.1.0-py2.7-macosx-10.7-x86_64.egg/twisted/internet/_sslverify.py", line 166, in __repr__
    self.getIssuer().commonName)
  File "Twisted-11.1.0-py2.7-macosx-10.7-x86_64.egg/twisted/internet/_sslverify.py", line 85, in __getattr__
    raise AttributeError(attr)
AttributeError: commonName
>>> 
```


<details><summary>Searchable metadata</summary>

```
trac-id__5851 5851
type__defect defect
reporter__mmcc mmcc
priority__normal normal
milestone__ 
branch__ 
branch_author__ 
status__new new
resolution__None None
component__core core
keywords__ 
time__1344292164000000 1344292164000000
changetime__1344339159000000 1344339159000000
version__None None
owner__ 

```
</details>



Discussion:

Resolving patch:
diff --git a/src/twisted/internet/_sslverify.py b/src/twisted/internet/_sslverify.py
--- a/src/twisted/internet/_sslverify.py
+++ b/src/twisted/internet/_sslverify.py
@@ -431,8 +431,8 @@ class Certificate(CertBase):
     def __repr__(self) -> str:
         return "<{} Subject={} Issuer={}>".format(
             self.__class__.__name__,
-            self.getSubject().commonName,
-            self.getIssuer().commonName,
+            self.getSubject().get("commonName", ""),
+            self.getIssuer().get("commonName", ""),
         )
 
     def __eq__(self, other: object) -> bool:


Test patch:
diff --git a/src/twisted/test/test_sslverify.py b/src/twisted/test/test_sslverify.py
--- a/src/twisted/test/test_sslverify.py
+++ b/src/twisted/test/test_sslverify.py
@@ -6,10 +6,10 @@
 Tests for L{twisted.internet._sslverify}.
 """
 
-
 import datetime
 import itertools
 import sys
+import textwrap
 from unittest import skipIf
 
 from zope.interface import implementer
@@ -1479,6 +1479,60 @@ def test_inspectCertificate(self):
             ],
         )
 
+    def test_representationOfCertificate(self):
+        """
+        The repr of L{sslverify.Certificate} returns
+        a human-readable string containing the subject of the certificate itself, and the subject of the signing CA.
+        """
+        c = sslverify.Certificate.loadPEM(A_HOST_CERTIFICATE_PEM)
+        self.assertEqual(
+            repr(c),
+            "<Certificate Subject=b'example.twistedmatrix.com' Issuer=b'example.twistedmatrix.com'>",
+        )
+
+    def test_representationOfCertificateNoCN(self):
+        """
+        Repr of a L{sslverify.Certificate} for which both the certificate and the signing CA have no subject, contains the empty string as the subject.
+        """
+        ubuntuOneGoDaddyPem = textwrap.dedent(
+            """\
+            -----BEGIN CERTIFICATE-----
+            MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMx
+            ITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
+            RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYw
+            MTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMH
+            QXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5j
+            b20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j
+            b20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmlj
+            YXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcN
+            AQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3H
+            KrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQm
+            VZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpR
+            SgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRT
+            cDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ
+            6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEu
+            MB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDS
+            kdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEB
+            BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0f
+            BD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv
+            c2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUH
+            AgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAO
+            BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IG
+            OgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMU
+            A2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o
+            0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTX
+            RE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuH
+            qDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWV
+            U+4=
+            -----END CERTIFICATE-----
+            """
+        )
+        c = sslverify.Certificate.loadPEM(ubuntuOneGoDaddyPem)
+        self.assertEqual(
+            repr(c),
+            "<Certificate Subject=b'Go Daddy Secure Certification Authority' Issuer=>",
+        )
+
     def test_publicKeyMatching(self):
         """
         L{PublicKey.matches} returns L{True} for keys from certificates with