Desmontamos y vemos certificados calificados usando Python / Tkinter

Los certificados calificados se convirtieron rápidamente en una parte integral de la vida cotidiana. Y cada vez más personas quieren ver a esta "bestia" desde adentro. Esto es por un lado. Por otro lado, se están desarrollando más y más aplicaciones en las que se utiliza la información de estos certificados. Y estos no son solo atributos del TIN o BIN del propietario o editor del certificado. Esta puede ser información sobre qué proveedor criptográfico fue utilizado por el titular del certificado (atributo subjectSignTool) para generar una clave privada o sobre la base de qué certificado significa que se creó un centro de certificación (CA) que emitió un certificado. Y si escribe un programa que analizará los certificados emitidos, puede recopilar estadísticas interesantes sobre qué herramientas de protección de la información criptográfica son utilizadas por los titulares de certificados y sobre la base de qué fondos (aunque menos interesantes) certificados (o no certificados) CA se implementan (atributo issuerSignTools):



En los espacios abiertos de Habr, ya se hizo un intento exitoso de desarmar un certificado calificado. Desafortunadamente, el análisis solo se refería a la recepción de los atributos TIN, PSRN y SNILS que forman parte del DN de nombre distinguido. Aunque, ¿por qué lamentablemente? El autor tenía un problema específico y se resolvió. Queremos acceder a los atributos de un certificado calificado a través de Python y ofrecer una utilidad gráfica para verlos.

Para acceder a los atributos del certificado, utilizaremos el paquete fsb795 . El paquete está disponible tanto para Pytho2 como para Python3, tanto para Linux como para Windows. Para instalarlo, simplemente ejecute el comando tradicional:

# 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]# 

El paquete fsb795 requiere los paquetes pyasn1 y pyasn1-modules. Por lo tanto, si no están instalados, se intentará instalarlos.

Para python3, este comando se ve así:

 # python -m pip install fsb795 ... # 

También puede descargar los paquetes de instalación python3 y python2 e instalarlos localmente.
El nombre del paquete, por analogía con los módulos del paquete pyasn1-modules, por ejemplo, rfc2459, etc., indica que está diseñado para trabajar con certificados que cumplan con los requisitos de la Orden del Servicio Federal de Seguridad de la Federación de Rusia del 27 de diciembre de 2011 No. 795 “En aprobación requisitos para la forma de un certificado calificado ... ".

El acceso al certificado en el paquete fsb795 se implementa a través de la clase Certificado:

 # -*- 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__": . . . 

Para crear una instancia de un objeto para un certificado específico, es suficiente ejecutar la siguiente declaración:

 $ 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(</  >) >> 

Como parámetro, al crear una instancia de la clase, se especifica un certificado, que puede estar en un archivo PEM o DER o ser una cadena en formato PEM.

Después de la creación, cada instancia tiene cuatro atributos: pyver, formatCert, cert_full y cert.
Usando el atributo pyver, puede verificar cómo fue el análisis del certificado. Si pyver es igual a una cadena vacía, entonces el archivo o cadena no contiene un certificado. De lo contrario, el atributo pyver contiene la versión del lenguaje 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 >>> 

El atributo formatCert, al crear con éxito una instancia de la clase Certificado, contiene el tipo de formato de archivo / cadena del certificado. Puede ser PEM o DER. Por qué se necesita este atributo se aclarará a continuación.

El paquete fsb795 se creó con el paquete pyasn1 . Entonces, dos atributos permanecieron sin examinar. El atributo cert almacena el certificado tbs, listo para usar con el paquete pyasn1. Otro atributo cert_full almacena el certificado descodificado completo con respecto a rfc2459. Mostramos cómo obtener el algoritmo de clave pública con el atributo cert y el paquete pyasn1 conectado:

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

Al final, será posible evaluar las capacidades del paquete fsb795 para obtener información sobre la clave pública de un certificado calificado.

Cuando se crea con éxito una instancia de la clase Certificado, tenemos a nuestra disposición métodos que facilitan la obtención de los datos necesarios del certificado. Toda la información sobre la clave pública se puede obtener de la siguiente manera:

 >>> 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 >>> 

Actualmente, la clase Certificado contiene los siguientes métodos:

  • subjectSignTool (): devuelve una cadena con el nombre del titular del certificado de protección de información criptográfica
  • issuerSignTool (): devuelve una lista de cuatro elementos con herramientas criptográficas de información del emisor del certificado;
  • classUser (): devuelve una cadena con los oids de las clases de seguridad del certificado de protección de información criptográfica del titular del certificado, separados por los caracteres ";;";
  • issuerCert () - devuelve un diccionario con campos y valores del nombre distintivo DN del emisor del certificado y un número que identifica el certificado (2 - entidad legal);
  • subjectCert () - devuelve un diccionario con los campos y valores del nombre distinguido DN del titular del certificado y un número que identifica el certificado (2 - entidad legal);
  • publicKey (): devuelve un diccionario que contiene el valor clave ('valuepk') y los parámetros clave ('curva' y 'hash');
  • signatureCert: devuelve dos valores: el algoritmo de firma y el valor de firma;
  • validityCert: devuelve un diccionario con dos claves 'not_after' y 'not_before';
  • keyUsage (): devuelve una lista de ámbitos clave;
  • serialNumber (): devuelve el número de serie del certificado en forma decimal;
  • prettyPrint (): devuelve una cadena con una 'impresión' del certificado en términos de pyasn1 (self.cert_full.prettyPrint ()).

El spoiler contiene un ejemplo de prueba que demuestra claramente el trabajo de estos métodos.

Prueba test795.py para probar el paquete 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=================================') 


Para ejecutar un ejemplo de prueba, simplemente ejecute el comando:

 $python test795.py 

Con el paquete fsb795 a su disposición, era natural escribir en python una utilidad gráfica autónoma e independiente de la plataforma para ver los certificados calificados. El paquete Tkinter se utilizó como soporte gráfico:



La utilidad viewCertFL63 tiene tres pestañas. La pestaña Acerca del certificado, entre otras cosas, muestra la hora actual. Volveremos a ello a continuación. Para seleccionar un certificado, simplemente haga clic en el botón "Seleccionar":



Preste atención al botón (aquellos que trabajan en Windows no verán este botón), le permite ocultar los llamados archivos / directorios invisibles (ocultos). Para que aparezca este botón, simplemente ejecute los siguientes comandos:

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

Un botón muy útil. Entonces, después de elegir un certificado, la pestaña "Acerca del certificado" tomará la forma:



Lo que es notable aquí es que si el certificado caduca mientras se ve el certificado, la impresión en el icono en la esquina superior izquierda se dividirá en dos mitades. Todos pueden estar convencidos de esto, reorganizando el reloj de la computadora con un año de anticipación.
En la pestaña "Detalles", puede ver en detalle las características del atributo seleccionado de un certificado calificado:



Y finalmente, la tercera pestaña es "Texto". Esta pestaña muestra el contenido de todo el certificado:



Para ver el certificado, puede usar no solo Python (el botón "Python"), sino también las utilidades openssl y pp de Network Serurity Services (NSS). Si alguien no tiene estas utilidades, entonces la primera puede obtenerse recolectando openssl con soporte para la criptografía rusa. Cuando se usa la utilidad pp, la salida del certificado se ve así:



Anteriormente, mencionamos el atributo formatCert de la clase Certificate del paquete fsb795. Por lo tanto, necesitamos el valor de este atributo para indicar el formato del archivo con el certificado cuando se ejecuta esta o aquella utilidad. Por ejemplo, invocar la utilidad pp con el formato de archivo PEM se ve así:

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

La opción -a indica el formato del archivo PEM. Para el formato DER, no se especifica.
El parámetro "–inform" para openssl se configura de la misma manera.
El botón Utilidad se usa para indicar la ruta a las utilidades openssl o pp.
Las distribuciones de utilidades ViewCertFL63 se encuentran aquí .
Las distribuciones se crearon utilizando el paquete pyinstaller:

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

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


All Articles