Nous démontons et visualisons les certificats qualifiés à l'aide de Python / Tkinter

Les certificats qualifiés sont rapidement devenus partie intégrante de la vie quotidienne. Et de plus en plus de gens veulent voir cette "bête" de l'intérieur. C'est d'une part. D'un autre côté, de plus en plus d'applications sont développées dans lesquelles les informations de ces certificats sont utilisées. Et ce ne sont pas seulement des attributs du TIN ou du BIN du propriétaire ou de l'éditeur du certificat. Il peut s'agir d'informations sur le fournisseur de chiffrement utilisé par le titulaire du certificat (attribut subjectSignTool) pour générer une clé privée ou sur la base de laquelle certifié signifie qu'un centre de certification (CA) a été créé qui a émis un certificat. Et si vous écrivez un programme qui analysera les certificats émis, vous pouvez collecter des statistiques intéressantes sur les outils de protection des informations cryptographiques utilisés par les détenteurs de certificats et sur la base des fonds certifiés (ou non certifiés) (mais moins intéressants) des autorités de certification déployées (attribut issuerSignTools):



Dans les espaces ouverts de Habr, une tentative réussie a déjà été faite pour démonter un certificat qualifié. Malheureusement, l'analyse n'a concerné que l'obtention des attributs TIN, PSRN et SNILS, qui font partie du nom distinctif DN (Distinguished Name). Mais pourquoi malheureusement? L'auteur avait un problème spécifique et il a été résolu. Nous voulons accéder aux attributs d'un certificat qualifié via Python et fournir un utilitaire graphique pour les visualiser.

Pour accéder aux attributs du certificat, nous utiliserons le package fsb795 . Le package est disponible pour Pytho2 et Python3, pour Linux et pour Windows. Pour l'installer, il suffit d'exécuter la commande traditionnelle:

# python -m pip install fsb795 Collecting fsb795 Requirement already satisfied: pyasn1-modules>=0.2.2 in /usr/lib/python2.7/site-packages (from fsb795) (0.2.2) Collecting pyasn1>=0.4.4 (from fsb795) Using cached https://files.pythonhosted.org/packages/d1/a1/7790cc85db38daa874f6a2e6308131b9953feb1367f2ae2d1123bb93a9f5/pyasn1-0.4.4-py2.py3-none-any.whl Requirement already satisfied: six in /usr/lib/python2.7/site-packages (from fsb795) (1.11.0) Installing collected packages: pyasn1, fsb795 Successfully installed fsb795-1.5.2 pyasn1-0.4.4 [root@localhost GCryptGOST]# 

Le package fsb795 nécessite les packages pyasn1 et pyasn1-modules. Par conséquent, s'ils ne sont pas installés, une tentative sera effectuée pour les installer.

Pour python3, cette commande ressemble à ceci:

 # python -m pip install fsb795 ... # 

Vous pouvez également télécharger les packages d' installation python3 et python2 et les installer localement.
Le nom du package, par analogie avec les modules du package pyasn1-modules, par exemple, rfc2459, etc., indique qu'il est conçu pour fonctionner avec des certificats qui répondent aux exigences de l'Ordre du Service fédéral de sécurité de la Fédération de Russie du 27 décembre 2011 n ° 795 «Sur approbation exigences relatives à la forme d'un certificat qualifié ... ".

L'accès au certificat dans le package fsb795 est implémenté via la classe Certificate:

 # -*- coding: utf-8 -*- import os, sys import pyasn1 import binascii import six from pyasn1_modules import rfc2459, pem from pyasn1.codec.der import decoder from datetime import datetime, timedelta class Certificate: #  cert_full = '' cert = '' pyver = '' formatCert = '' def __init__ (self,fileorstr): #     if not os.path.exists(fileorstr): #  ,  ,     #    PEM- strcert = fileorstr.strip('\n') if (strcert[0:27] != '-----BEGIN CERTIFICATE-----'): return idx, substrate = pem.readPemBlocksFromFile(six.StringIO( strcert), ('-----BEGIN CERTIFICATE-----', '-----END CERTIFICATE-----') ) self.pyver = sys.version[0] try: self.cert_full, rest = decoder.decode(substrate, asn1Spec=rfc2459.Certificate()) self.cert = self.cert_full["tbsCertificate"] self.formatCert = 'PEM' except: self.pyver = '' self.formatCert = '' return #   #  self.pyver   python self.pyver = sys.version[0] filename = fileorstr if (self.pyver == '2'): if sys.platform != "win32": filename = filename.encode("UTF-8") else: filename = filename.encode("CP1251") #  DER file1 = open(filename, "rb") substrate = file1.read() if (self.pyver == '2'): b0 = ord(substrate[0]) b1 = ord(substrate[1]) else: b0 = substrate[0] b1 = substrate[1] #  PEM/DER,   0x30,       127  if (b0 == 48 and b1 > 128) : self.formatCert = 'DER' else: self.formatCert = 'PEM' file1 = open(filename, "r") idx, substrate = pem.readPemBlocksFromFile( file1, ('-----BEGIN CERTIFICATE-----', '-----END CERTIFICATE-----') ) file1.close() try: self.cert_full, rest = decoder.decode(substrate, asn1Spec=rfc2459.Certificate()) self.cert = self.cert_full["tbsCertificate"] except: self.pyver = '' self.formatCert = '' #       def subjectSignTool(self): . . . #,     if __name__ == "__main__": . . . 

Pour créer une instance d'un objet pour un certificat spécifique, il suffit d'exécuter l'instruction suivante:

 $ python Python 2.7.15 (default, May 23 2018, 14:20:56) [GCC 5.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>import fsb795 >>tek_cert = fsb795.Certificate(</  >) >> 

En tant que paramètre, lors de la création d'une instance de la classe, un certificat est spécifié, qui peut être dans un fichier PEM ou DER ou être une chaîne au format PEM.

Après la création, chaque instance a quatre attributs: pyver, formatCert, cert_full et cert.
À l'aide de l'attribut pyver, vous pouvez vérifier le déroulement de l'analyse du certificat. Si pyver est égal à une chaîne vide, le fichier ou la chaîne ne contient pas de certificat. Sinon, l'attribut pyver contient la version du langage python:

 >>> c1=fsb795.Certificate('     ') >>> if (c1.pyver == ''): ... print ('   ') ...     >>> c2 = fsb795.Certificate('/home/a513/cert_nss.der') >>> if (c2.pyver != ""): ... print(c2.pyver) ... 2 >>> print(c2.formatCert) DER >>> 

L'attribut formatCert, lors de la création réussie d'une instance de la classe Certificate, contient le type de format de fichier / chaîne du certificat. Il peut s'agir de PEM ou DER. La raison pour laquelle cet attribut est nécessaire apparaîtra clairement ci-dessous.

Le package fsb795 a été créé à l'aide du package pyasn1 . Ainsi, deux attributs n'ont pas été examinés. L'attribut cert stocke le certificat tbs, prêt à être utilisé avec le package pyasn1. Un autre attribut cert_full stocke l'intégralité du certificat décodé par rapport à rfc2459. Nous montrons comment obtenir l'algorithme de clé publique avec l'attribut cert et le package pyasn1 connecté:

 >>> pubkey = c2.cert['subjectPublicKeyInfo'] >>> ff = pubkey['algorithm'] >>> ff1 = ff['algorithm'] >>> print (ff1) 1.2.643.2.2.19 >>> 

Au final, il sera possible d'évaluer les capacités du package fsb795 pour obtenir des informations sur la clé publique d'un certificat qualifié.

Lorsqu'une instance de la classe Certificate est créée avec succès, nous avons à notre disposition des méthodes qui facilitent l'obtention des données nécessaires à partir du certificat. Toutes les informations sur la clé publique peuvent être obtenues comme suit:

 >>> c3 = fsb795.Certificate('cert.der') >>> key_info=c3.publicKey() >>> for opt in key_info.keys(): ... val = str(key_info[opt]) ... print (opt + '=' + val) ... curve=1.2.643.2.2.36.0 hash=1.2.643.2.2.30.1 valuepk=5b785f86f0dd5316ba37c8440e398e83f2ec0c34478f90da9c0c8046d341ff66f9044cd00a0e25530 acefd51e6be852dbecacbaabc55e807be8e1f861658bd58 algo=1.2.643.2.2.19 >>> 

Actuellement, la classe Certificate contient les méthodes suivantes:

  • subjectSignTool () - retourne une chaîne avec le nom du certificat du titulaire du certificat cryptographique
  • issuerSignTool () - retourne une liste de quatre éléments avec les outils cryptographiques d'informations de l'émetteur du certificat;
  • classUser () - retourne une chaîne avec les oids des classes de sécurité du certificat de protection des informations cryptographiques du titulaire du certificat, séparés par les caractères ";;";
  • issuerCert () - renvoie un dictionnaire avec des champs et des valeurs du nom distinctif DN de l'émetteur du certificat et un numéro qui identifie le certificat (2 - entité juridique);
  • subjectCert () - renvoie un dictionnaire avec les champs et les valeurs du nom distinctif DN du titulaire du certificat et un numéro qui identifie le certificat (2 - entité juridique);
  • publicKey () - retourne un dictionnaire contenant la valeur de clé ('valuepk') et les paramètres de clé ('curve' et 'hash');
  • signatureCert - renvoie deux valeurs: l'algorithme de signature et la valeur de signature;
  • validCert - retourne un dictionnaire avec deux clés 'not_after' et 'not_before';
  • keyUsage () - renvoie une liste des portées de clés;
  • serialNumber () - renvoie le numéro de série du certificat sous forme décimale;
  • prettyPrint () - renvoie une chaîne avec une 'impression' du certificat en termes de pyasn1 (self.cert_full.prettyPrint ()).

Le spoiler contient un exemple de test qui montre clairement le travail de ces méthodes.

Testez test795.py pour tester le package fsb795
 import fsb795 certpem = """ -----BEGIN CERTIFICATE----- MIIG3DCCBougAwIBAgIKE8/KkAAAAAAC4zAIBgYqhQMCAgMwggFKMR4wHAYJKoZI hvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRwwGgYDVQQIDBM3 NyDQsy4g0JzQvtGB0LrQstCwMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAxPzA9BgNV BAkMNjEyNTM3NSDQsy4g0JzQvtGB0LrQstCwLCDRg9C7LiDQotCy0LXRgNGB0LrQ sNGPLCDQtC4gNzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+ 0YHRgdC40LgxGDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEB EgwwMDc3MTA0NzQzNzUxQTA/BgNVBAMMONCT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+ 0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMB4XDTE4MDcwOTE1MjYy NFoXDTI3MDcwOTE1MjYyNFowggFVMR4wHAYJKoZIhvcNAQkBFg9jb250YWN0QGVr ZXkucnUxITAfBgNVBAMMGNCe0J7QniDCq9CV0LrQtdC5INCj0KbCuzEwMC4GA1UE Cwwn0KPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMSEwHwYD VQQKDBjQntCe0J4gwqvQldC60LXQuSDQo9CmwrsxCzAJBgNVBAYTAlJVMRgwFgYD VQQIDA83NyDQnNC+0YHQutCy0LAxRDBCBgNVBAkMO9Cj0JvQmNCm0JAg0JjQm9Cs 0JjQndCa0JAsINCULjQsINCQ0J3QotCgIDMg0K3Qojsg0J/QntCcLjk0MRgwFgYD VQQHDA/Qsy7QnNC+0YHQutCy0LAxGDAWBgUqhQNkARINMTE0Nzc0NjcxNDYzMTEa MBgGCCqFAwOBAwEBEgwwMDc3MTA5NjQzNDgwYzAcBgYqhQMCAhMwEgYHKoUDAgIk AAYHKoUDAgIeAQNDAARAW3hfhvDdUxa6N8hEDjmOg/LsDDRHj5DanAyARtNB/2b5 BEzQCg4lUwrO/VHmvoUtvsrLqrxV6Ae+jh+GFli9WKOCA0AwggM8MBIGA1UdEwEB /wQIMAYBAf8CAQAwHQYDVR0OBBYEFMQYnG5GfYRnj2ehEQ5tv8Fso/qBMAsGA1Ud DwQEAwIBRjAdBgNVHSAEFjAUMAgGBiqFA2RxATAIBgYqhQNkcQIwKAYFKoUDZG8E Hwwd0KHQmtCX0JggwqvQm9CY0KDQodCh0JstQ1NQwrswggGLBgNVHSMEggGCMIIB foAUi5g7iRhR6O+cAni46sjUILJVyV2hggFSpIIBTjCCAUoxHjAcBgkqhkiG9w0B CQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxHDAaBgNVBAgMEzc3INCz LiDQnNC+0YHQutCy0LAxFTATBgNVBAcMDNCc0L7RgdC60LLQsDE/MD0GA1UECQw2 MTI1Mzc1INCzLiDQnNC+0YHQutCy0LAsINGD0LsuINCi0LLQtdGA0YHQutCw0Y8s INC0LiA3MSwwKgYDVQQKDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB 0LjQuDEYMBYGBSqFA2QBEg0xMDQ3NzAyMDI2NzAxMRowGAYIKoUDA4EDAQESDDAw NzcxMDQ3NDM3NTFBMD8GA1UEAww40JPQvtC70L7QstC90L7QuSDRg9C00L7RgdGC 0L7QstC10YDRj9GO0YnQuNC5INGG0LXQvdGC0YCCEDRoHkDLQe8zqaC3yHaSmikw WQYDVR0fBFIwUDAmoCSgIoYgaHR0cDovL3Jvc3RlbGVjb20ucnUvY2RwL2d1Yy5j cmwwJqAkoCKGIGh0dHA6Ly9yZWVzdHItcGtpLnJ1L2NkcC9ndWMuY3JsMIHGBgUq hQNkcASBvDCBuQwj0J/QkNCa0JwgwqvQmtGA0LjQv9GC0L7Qn9GA0L4gSFNNwrsM INCf0JDQmiDCq9CT0L7Qu9C+0LLQvdC+0Lkg0KPQpsK7DDbQl9Cw0LrQu9GO0YfQ tdC90LjQtSDihJYgMTQ5LzMvMi8yLTk5OSDQvtGCIDA1LjA3LjIwMTIMONCX0LDQ utC70Y7Rh9C10L3QuNC1IOKEliAxNDkvNy8xLzQvMi02MDMg0L7RgiAwNi4wNy4y MDEyMAgGBiqFAwICAwNBALvjFGhdFE9llvlvKeQmZmkI5J+yO2jFWTh8nXPjIpiL OutUew2hIZv15pJ1QM/VgRO3BTBGDOoIrq8LvgC+3kA= -----END CERTIFICATE----- """ #c1 = fsb795.Certificate('OOO_VOLGA.der') #c1 = fsb795.Certificate('cert.der') c1 = fsb795.Certificate(certpem) if (c1.pyver == ''): print('Context for certificate not create') exit(-1) print('=================formatCert================================') print(c1.formatCert) res = c1.subjectSignTool() print('=================subjectSignTool================================') print (res) print('=================issuerSignTool================================') res1 = c1.issuerSignTool() print (res1[0]) print (res1[1]) print (res1[2]) print (res1[3]) print('=================prettyPrint================================') res2 = c1.prettyPrint() #print(res2) print('=================classUser================================') res3 = c1.classUser() print (res3) print('=================issuerCert================================') iss, vlad_is = c1.issuerCert() print ('vlad_is=' + str(vlad_is)) for key in iss.keys(): print (key + '=' + iss[key]) print('=================subjectCert================================') sub, vlad_sub = c1.subjectCert() print ('vlad_sub=' + str(vlad_sub)) for key in sub.keys(): print (key + '=' + sub[key]) print('================publicKey=================================') key_info = c1.publicKey() print(key_info['curve']) print(key_info['hash']) print(key_info['valuepk']) print('================serialNumber=================================') print(c1.serialNumber()) print('================validityCert=================================') valid = c1.validityCert() print(valid['not_after']) print(valid['not_before']) print('================signatureCert=================================') algosign, value = c1.signatureCert() print(algosign) print(value) print('================KeyUsage=================================') ku = c1.KeyUsage() for key in ku: print (key) # print(ku) print('================END=================================') 


Pour exécuter un exemple de test, exécutez simplement la commande:

 $python test795.py 

Avec le package fsb795 à sa disposition, il était naturel d'écrire en python un utilitaire graphique autonome et indépendant de la plateforme pour visualiser les certificats qualifiés. Le package Tkinter a été utilisé comme support graphique:



L'utilitaire viewCertFL63 comporte trois onglets. L'onglet À propos du certificat, entre autres, affiche l'heure actuelle. Nous y reviendrons ci-dessous. Pour sélectionner un certificat, cliquez simplement sur le bouton "Sélectionner":



Faites attention au bouton (ceux qui travaillent sur Windows ne verront pas ce bouton), il vous permet de masquer les fichiers / répertoires soi-disant invisibles (cachés). Pour que ce bouton apparaisse, exécutez simplement les commandes suivantes:

 if sys.platform != "win32": root.tk.call('set', '::tk::dialog::file::showHiddenBtn', '1') root.tk.call('set', '::tk::dialog::file::showHiddenVar', '0') 

Un bouton très utile. Ainsi, après avoir choisi un certificat, l'onglet «À propos du certificat» prendra la forme:



Ce qui est remarquable ici, c'est que si le certificat expire lors de la visualisation du certificat, l'impression sur l'icône dans le coin supérieur gauche se divisera en deux moitiés. Tout le monde peut en être convaincu, en réorganisant l'horloge de l'ordinateur pendant un an à l'avance.
Dans l'onglet Détails, vous pouvez afficher en détail les caractéristiques de l'attribut sélectionné d'un certificat qualifié:



Et enfin, le troisième onglet est "Texte". Cet onglet affiche le contenu de l'ensemble du certificat:



Pour afficher le certificat, vous pouvez utiliser non seulement Python (le bouton "Python"), mais également les utilitaires openssl et pp des services de sécurité réseau (NSS). Si quelqu'un n'a pas ces utilitaires, le premier peut être obtenu en collectant openssl avec le support de la cryptographie russe. Lorsque vous utilisez l'utilitaire pp, la sortie du certificat ressemble à ceci:



Ci-dessus, nous avons mentionné l'attribut formatCert de la classe Certificate du package fsb795. Nous avons donc besoin de la valeur de cet attribut pour indiquer le format du fichier avec le certificat lors de l'exécution de tel ou tel utilitaire. Par exemple, invoquer l'utilitaire pp avec le format de fichier PEM ressemble à ceci:

 $pp –tc –u –a –i < > 

L'option -a indique le format du fichier PEM. Pour le format DER, il n'est pas spécifié.
Le paramètre "–inform" pour openssl est défini de la même manière.
Le bouton Utilitaire est utilisé pour indiquer le chemin d'accès aux utilitaires openssl ou pp.
Les distributions de l'utilitaire ViewCertFL63 se trouvent ici .
Les distributions ont été construites à l'aide du package pyinstaller:

 $python pyinstaller.py --noconsole -F viewCertFL63.py 

Source: https://habr.com/ru/post/fr421107/


All Articles