Repository: elastic/apm-agent-python

Issue:
read_pem_file doesn't support certificate chains
Something like this should fix the issue:
```diff
 def read_pem_file(file_obj):
     cert = b""
     for line in file_obj:
         if line.startswith(b"-----BEGIN CERTIFICATE-----"):
             break
     for line in file_obj:
-        if not line.startswith(b"-----END CERTIFICATE-----"):
-            cert += line.strip()
+        if line.startswith(b"-----END CERTIFICATE-----"):
+            break
+        cert += line.strip()
     return base64.b64decode(cert)
```
read_pem_file doesn't support certificate chains
Something like this should fix the issue:
```diff
 def read_pem_file(file_obj):
     cert = b""
     for line in file_obj:
         if line.startswith(b"-----BEGIN CERTIFICATE-----"):
             break
     for line in file_obj:
-        if not line.startswith(b"-----END CERTIFICATE-----"):
-            cert += line.strip()
+        if line.startswith(b"-----END CERTIFICATE-----"):
+            break
+        cert += line.strip()
     return base64.b64decode(cert)
```


Discussion:

Resolving patch:
diff --git a/elasticapm/utils/__init__.py b/elasticapm/utils/__init__.py
--- a/elasticapm/utils/__init__.py
+++ b/elasticapm/utils/__init__.py
@@ -165,9 +165,11 @@ def read_pem_file(file_obj):
     for line in file_obj:
         if line.startswith(b"-----BEGIN CERTIFICATE-----"):
             break
+    # scan until we find the first END CERTIFICATE marker
     for line in file_obj:
-        if not line.startswith(b"-----END CERTIFICATE-----"):
-            cert += line.strip()
+        if line.startswith(b"-----END CERTIFICATE-----"):
+            break
+        cert += line.strip()
     return base64.b64decode(cert)
 
 


Test patch:
diff --git a/tests/ca/chain.crt b/tests/ca/chain.crt
new file mode 100644
--- /dev/null
+++ b/tests/ca/chain.crt
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIFezCCA2OgAwIBAgIUYGPYOuce9WlPR+Xo5kVJq/JuWLUwDQYJKoZIhvcNAQEL
+BQAwTTELMAkGA1UEBhMCQ0gxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAoM
+CWxvY2FsaG9zdDEVMBMGA1UEAwwMbG9jYWxob3N0IENBMB4XDTE5MDcyMjExMjUz
+MloXDTE5MDgyMTExMjUzMlowTTELMAkGA1UEBhMCQ0gxEzARBgNVBAgMClNvbWUt
+U3RhdGUxEjAQBgNVBAoMCWxvY2FsaG9zdDEVMBMGA1UEAwwMbG9jYWxob3N0IENB
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2FmmlZbeOzySgT1YTH1h
+ulPSo5sJNCZM68s97ddjpJg3anX/S1Ldab4gwU0mctube1E+ZTJR+C8dyIYAq3Z3
+6evVmUd0haJeGlpBDI6wEHjkuBtDR/nhl/5DnklT3ZOQ+PldWc/SxQJg5vRYerf+
+lRyJ3Nq368TsqGJwAKSvpvS8UJMCgLCrre8UpzH94zo6furikn+z91YzCqGh4PdI
+bLNlcsPtnE0c7I/wFXBSNZ49W+dR2c4Np+INCl158KsitW85wy+zUPn+LJ44xLhX
+k3BfZan2CjYjXbjSg5PihahzlsnCJaHcLh3HwWchY/qc8+TkGnvkr7npOcgBWIQy
+UyUM0JxlhcIMrwV0TJPhS+gVv6IUhqynFEUtt+o+zIDtcjLIoAo26zcWGt8qR5oO
+5+2tbwUPZN6mpHrQxjWYSxCKcMgjYk2J79lZhAMvSgLcEn6JhAcaiGMU11/hK73P
+TlOJ8CmsMDvkW+uGAtAtsvAnb+Etbx+wULGjzMMoxGhw/WA/nse6sKs7qTLck77C
+dcol5GUvk0BtZbsrA6ajXbXEph6W4kzi966RvaisOl2QL1Xk+jESDWpmuTf1bnKA
+BEozSdv+5096V0cXfI+Gh8cBfi/6Rmba8ldUeOUMf1NkEgeoZqxTfnldTrPXV1ZD
++X2if6xXhdaVaBi+w78liyECAwEAAaNTMFEwHQYDVR0OBBYEFB4Mh9i0rkNEbii2
+3ZLOgk9MXzhzMB8GA1UdIwQYMBaAFB4Mh9i0rkNEbii23ZLOgk9MXzhzMA8GA1Ud
+EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAFSb+pNLIzA4vJw6ggvPwmYP
+SeHal/WW3DIoUChd/77q4QxatNZn0oe6Pj4mUZ9TBegctDxD7wYY34/crMrATq8+
+rBEW/ZpyZu3JO0xsBchFTHh8yLbnSXaqThUgAJL0e8NeWvs+rkvKFQf531TrbXiX
+JcW11WAURV3lZrJx+BrPHcgOgJr6HJgGMiFrFhVjCF8p3T8lArj1I1+Vk3q6enAj
+XewWuLudxsYDxRleCbJAaSRJR/kYZ4pAFtQfwjpx/eOAI0k4yZBqwli7ywxE9TEv
+IUx23Htx9vYTEDTbWHPin/AtmKcPZB+/2LQT2eP36t9M3WUPi7vK+IS0QR+odoD1
+2TyCnv1Hc2CR//QWKJvOkfstPv6RrgG0YLE66AkGgzKPWlRVEjsRz/OkWj3rXzyf
+jkR2rGBwhsx93ZN2k1gI/EIjbu9sqMO8GpDOPztJa501FlJVP+8w7u9wkfcM2z6O
+0VD8JNWyBzotF189Xz3p6WgPJvRRCr50NV19TfjW7a48ngQad/AQ/exzYcndWR9L
+cLNo2v20+zt8Lh7YTbGSeQ04QhoD19aCaR0Q0FhSLKhqy0A6rRrTkbuI1MBYbaXz
+KORqDWpeFSnjQDx4zpsJHSyfBZBxPeN7XdaXSmdw+r3bU7Y/hcDnCCpoNMEu8wnh
+2fxvKPt5zMsjZ7dnXsiu
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+Some other certificate
+-----END CERTIFICATE-----
diff --git a/tests/utils/tests.py b/tests/utils/tests.py
--- a/tests/utils/tests.py
+++ b/tests/utils/tests.py
@@ -28,12 +28,20 @@
 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+import os
 from functools import partial
 
 import pytest
 
 from elasticapm.conf import constants
-from elasticapm.utils import get_name_from_func, get_url_dict, sanitize_url, starmatch_to_regex, url_to_destination
+from elasticapm.utils import (
+    get_name_from_func,
+    get_url_dict,
+    read_pem_file,
+    sanitize_url,
+    starmatch_to_regex,
+    url_to_destination,
+)
 from elasticapm.utils.deprecation import deprecated
 
 try:
@@ -219,3 +227,15 @@ def test_url_to_destination(url, name, resource):
 def test_url_to_destination_bad_port():
     destination = url_to_destination("https://www.elastic.co:bad")
     assert destination["service"]["resource"] == "www.elastic.co:443"
+
+
+def test_read_pem_file():
+    with open(os.path.join(os.path.dirname(__file__), "..", "ca", "ca.crt"), mode="rb") as f:
+        result = read_pem_file(f)
+        assert result.startswith(b"0\x82\x05{0\x82\x03c\xa0\x03\x02\x01\x02\x02\x14")
+
+
+def test_read_pem_file_chain():
+    with open(os.path.join(os.path.dirname(__file__), "..", "ca", "chain.crt"), mode="rb") as f:
+        result = read_pem_file(f)
+        assert result.endswith(b"\xc8\xae")