web-dev-qa-db-fra.com

Comment décoder un certificat SSL en utilisant python?

Comment décoder un certificat pem-encodé (base64) avec Python? Par exemple ceci ici de github.com:

-----BEGIN CERTIFICATE-----
MIIHKjCCBhKgAwIBAgIQDnd2il0H8OV5WcoqnVCCtTANBgkqhkiG9w0BAQUFADBp
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSgwJgYDVQQDEx9EaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBDQS0xMB4XDTExMDUyNzAwMDAwMFoXDTEzMDcyOTEyMDAwMFowgcoxHTAb
BgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYBBAGCNzwCAQMTAlVT
MRswGQYLKwYBBAGCNzwCAQITCkNhbGlmb3JuaWExETAPBgNVBAUTCEMzMjY4MTAy
MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2Fu
IEZyYW5jaXNjbzEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRMwEQYDVQQDEwpnaXRo
dWIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7dOJw11wcgnz
M08acnTZtlqVULtoYZ/3+x8Z4doEMa8VfBp/+XOvHeVDK1YJAEVpSujEW9/Cd1JR
GVvRK9k5ZTagMhkcQXP7MrI9n5jsglsLN2Q5LLcQg3LN8OokS/rZlC7DhRU5qTr2
iNr0J4mmlU+EojdOfCV4OsmDbQIXlXh9R6hVg+4TyBkaszzxX/47AuGF+xFmqwld
n0xD8MckXilyKM7UdWhPJHIprjko/N+NT02Dc3QMbxGbp91i3v/i6xfm/wy/wC0x
O9ZZovLdh0pIe20zERRNNJ8yOPbIGZ3xtj3FRu9RC4rGM+1IYcQdFxu9fLZn6TnP
pVKACvTqzQIDAQABo4IDajCCA2YwHwYDVR0jBBgwFoAUTFjLJfBBT1L0KMiBQ5um
qKDmkuUwHQYDVR0OBBYEFIfRjxlu5IdvU4x3kQdQ36O/VUcgMCUGA1UdEQQeMByC
CmdpdGh1Yi5jb22CDnd3dy5naXRodWIuY29tMIGBBggrBgEFBQcBAQR1MHMwJAYI
KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBLBggrBgEFBQcwAoY/
aHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ0FDZXJ0cy9EaWdpQ2VydEhpZ2hBc3N1
cmFuY2VFVkNBLTEuY3J0MAwGA1UdEwEB/wQCMAAwYQYDVR0fBFowWDAqoCigJoYk
aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL2V2MjAwOWEuY3JsMCqgKKAmhiRodHRw
Oi8vY3JsNC5kaWdpY2VydC5jb20vZXYyMDA5YS5jcmwwggHEBgNVHSAEggG7MIIB
tzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGln
aWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCC
AVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABp
AGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBw
AHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQ
AC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQBy
AHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0
ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwBy
AHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBl
AG4AYwBlAC4wHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBEGCWCGSAGG
+EIBAQQEAwIGwDAOBgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggEBABRS
cR+GnW01Poa7ZhqLhZi5AEzLQrVG/AbnRDnI6FLYERQjs3KW6RSUni8AKPfVBEVA
AMb0V0JC3gmJlxENFFxrvQv3GKNfZwLzCThjv8ESnTC6jqVUdFlTZ6EbUFsm2v0T
flkXv0nvlH5FpP06STLwav+JjalhqaqblkbIHOAYHOb7gvQKq1KmyuhUItnbKj1a
InuA6gcF1PnH8FNZX7t3ft6TcEFOI8t4eXnELurXZioY99HFfOISeIKNHeyCngGi
5QK+eKG5WVjFTG9PpTG0SVtemB4uOPYZxDmiSvt5BbjyWeUmEnCtwOh1Ix8Y0Qvg
n2Xkw9dJh1tybLEvrG8=
-----END CERTIFICATE-----

Selon ssl-shopper cela devrait ressembler à ceci:

Common Name: github.com
Subject Alternative Names: github.com, www.github.com
Organization: GitHub, Inc.
Locality: San Francisco
State: California
Country: US
Valid From: May 26, 2011
Valid To: July 29, 2013

Comment puis-je obtenir ce texte en clair en utilisant python?

21
puwei219

La bibliothèque standard de Python, même dans la version la plus récente, n’inclut rien qui puisse décoder les certificats X.509. Cependant, le complément cryptography package prend en charge cette opération. Citant un exemple tiré de la documentation :

>>> from cryptography import x509
>>> from cryptography.hazmat.backends import default_backend
>>> cert = x509.load_pem_x509_certificate(pem_data, default_backend())
>>> cert.serial_number
2

Un autre package add-on qui pourrait être une option est pyopenssl . C’est une mince pellicule autour de l’API C OpenSSL, ce qui signifie qu’il sera possible de faire ce que vous voulez, mais attendez-vous à passer quelques jours à vous déchirer les cheveux à la documentation.

Si vous ne pouvez pas installer les modules complémentaires Python, mais que vous avez l'utilitaire de ligne de commande openssl,

import subprocess
cert_txt = subprocess.check_output(["openssl", "x509", "-text", "-noout", 
                                    "-in", certificate])

devrait produire à peu près la même chose que vous avez obtenue de votre utilitaire Web dans cert_txt.

Incidemment, la raison de faire un décodage base64 direct vous donne un gobbledygook binaire, c'est qu'il y a deux couches d'encodage ici. Les certificats X.509 sont ASN.1 , structures de données sérialisées au format X.690 DER puis, puisque DER est un format binaire, blindé base64 pour faciliter le transfert de fichiers. (Beaucoup de normes dans ce domaine ont été écrites dans les années quatre-vingt-dix, alors que vous ne pouviez absolument pas envoyer de manière fiable autre que sept ASCII sept bits.)

32
zwol

Vous pouvez utiliser les packages pyasn1 et pyasn1_modules pour analyser ce type de données. Par exemple:

from pyasn1_modules import pem, rfc2459
from pyasn1.codec.der import decoder

substrate = pem.readPemFromFile(open('cert.pem'))
cert = decoder.decode(substrate, asn1Spec=rfc2459.Certificate())[0]
print(cert.prettyPrint())

Lisez la documentation de pyasn1 pour le reste.

26
andviro

Remarques:

En ce qui concerne le certificat (PEM) de la question:

  • Enregistré dans un fichier nommé q016899247.crt (dans le script (code.py) dir)
  • La balise de fin: ("----- END CERTIFICATE ----") manquait d'un trait d'union (-) à la fin; corrigé dans Question @VERSION # 4.)

code.py:

#!/usr/bin/env python3

import sys
import os
import ssl
from pprint import pprint as pp


def main():
    cert_file_name = os.path.join(os.path.dirname(__file__), "q016899247.crt")
    try:
        cert_dict = ssl._ssl._test_decode_cert(cert_file_name)
        pp(cert_dict)

    except Exception as e:
        print("Error decoding certificate: {:}".format(e))


if __== "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

Sortie:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q016899247]> "e:\Work\Dev\VEnvs\py_064_03.07.02_test0\Scripts\python.exe" code.py
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32

{'OCSP': ('http://ocsp.digicert.com',),
 'caIssuers': ('http://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt',),
 'crlDistributionPoints': ('http://crl3.digicert.com/ev2009a.crl',
                           'http://crl4.digicert.com/ev2009a.crl'),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', 'DigiCert Inc'),),
            (('organizationalUnitName', 'www.digicert.com'),),
            (('commonName', 'DigiCert High Assurance EV CA-1'),)),
 'notAfter': 'Jul 29 12:00:00 2013 GMT',
 'notBefore': 'May 27 00:00:00 2011 GMT',
 'serialNumber': '0E77768A5D07F0E57959CA2A9D5082B5',
 'subject': ((('businessCategory', 'Private Organization'),),
             (('jurisdictionCountryName', 'US'),),
             (('jurisdictionStateOrProvinceName', 'California'),),
             (('serialNumber', 'C3268102'),),
             (('countryName', 'US'),),
             (('stateOrProvinceName', 'California'),),
             (('localityName', 'San Francisco'),),
             (('organizationName', 'GitHub, Inc.'),),
             (('commonName', 'github.com'),)),
 'subjectAltName': (('DNS', 'github.com'), ('DNS', 'www.github.com')),
 'version': 3}
8
CristiFati

Ce code vide le contenu d'un fichier de certificat:

import OpenSSL.crypto

cert = OpenSSL.crypto.load_certificate(
      OpenSSL.crypto.FILETYPE_PEM,
      open('/path/to/cert/file.crt').read()
)

print OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT, cert)

Essayez.

0
Roberto Mier