Desmontamos e visualizamos certificados qualificados usando Python / Tkinter

Os certificados qualificados rapidamente se tornaram parte integrante da vida cotidiana. E mais e mais pessoas querem ver esse "animal" por dentro. Isso está por um lado. Por outro lado, mais e mais aplicativos estão sendo desenvolvidos nos quais as informações desses certificados são usadas. E esses não são apenas atributos do TIN ou BIN do proprietário ou editor do certificado. Podem ser informações sobre qual provedor de criptografia foi usado pelo detentor do certificado (atributo subjectSignTool) para gerar uma chave privada ou com base em que certificado significa que foi criado um centro de certificação (CA) que emitiu um certificado. E se você escrever um programa que analisará os certificados emitidos, poderá coletar estatísticas interessantes sobre quais ferramentas de proteção de informações criptográficas são usadas pelos detentores de certificados e com base em quais CAs (embora menos interessantes) certificadas (ou não certificadas) de fundos CA são implantadas (atributo issuerSignTools):



Nos espaços abertos da Habr, já foi feita uma tentativa bem- sucedida de desmontar um certificado qualificado. Infelizmente, a análise dizia respeito apenas à obtenção dos atributos TIN, PSRN e SNILS, que fazem parte do nome distinto DN (Nome Distinto). Embora, por que, infelizmente? O autor teve um problema específico e foi resolvido. Queremos acessar os atributos de um certificado qualificado através do Python e fornecer um utilitário gráfico para visualizá-los.

Para acessar os atributos do certificado, usaremos o pacote fsb795 . O pacote está disponível para Pytho2 e Python3, tanto para Linux quanto para Windows. Para instalá-lo, basta executar o 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]# 

O pacote fsb795 requer os pacotes pyasn1 e pyasn1-modules. Portanto, se eles não estiverem instalados, será feita uma tentativa de instalá-los.

Para python3, este comando se parece com o seguinte:

 # python -m pip install fsb795 ... # 

Você também pode baixar os pacotes de instalação python3 e python2 e instalá- los localmente.
O nome do pacote, por analogia com os módulos do pacote pyasn1-modules, por exemplo, rfc2459, etc., indica que ele foi projetado para funcionar com certificados que atendem aos requisitos da Ordem do Serviço de Segurança Federal da Federação Russa de 27 de dezembro de 2011 no 795 “Aprovação requisitos para a forma de um certificado qualificado ... ".

O acesso ao certificado no pacote fsb795 é implementado através da 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__": . . . 

Para criar uma instância de um objeto para um certificado específico, basta executar a seguinte instrução:

 $ 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, ao criar uma instância da classe, é especificado um certificado, que pode estar em um arquivo PEM ou DER ou uma sequência no formato PEM.

Após a criação, cada instância possui quatro atributos: pyver, formatCert, cert_full e cert.
Usando o atributo pyver, você pode verificar como foi a análise do certificado. Se pyver for igual a uma cadeia vazia, o arquivo ou cadeia não conterá um certificado. Caso contrário, o atributo pyver contém a versão da linguagem 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 >>> 

O atributo formatCert, ao criar com êxito uma instância da classe Certificate, contém o tipo de formato de arquivo / string do certificado. Pode ser PEM ou DER. Por que esse atributo é necessário ficará claro abaixo.

O pacote fsb795 foi criado usando o pacote pyasn1 . Portanto, dois atributos permaneceram sem exame. O atributo cert armazena o certificado tbs, pronto para uso com o pacote pyasn1. Outro atributo cert_full armazena todo o certificado decodificado em relação ao rfc2459. Mostramos como obter o algoritmo de chave pública com o atributo cert e o pacote pyasn1 conectado:

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

No final, será possível avaliar os recursos do pacote fsb795 para obter informações sobre a chave pública de um certificado qualificado.

Quando uma instância da classe Certificate é criada com sucesso, temos à nossa disposição métodos que facilitam a obtenção dos dados necessários a partir do certificado. Todas as informações sobre a chave pública podem ser obtidas da seguinte maneira:

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

Atualmente, a classe Certificate contém os seguintes métodos:

  • subjectSignTool () - retorna uma string com o nome do detentor do certificado de proteção de informações criptográficas;
  • issuerSignTool () - retorna uma lista de quatro elementos com ferramentas criptográficas de informações do emissor do certificado;
  • classUser () - retorna uma string com os oids das classes de segurança do proprietário do certificado de certificado de proteção de informações criptográficas, separados pelos caracteres ";;";
  • issuerCert () - retorna um dicionário com campos e valores do nome distinto DN do emissor do certificado e um número que identifica o certificado (2 - entidade legal);
  • subjectCert () - retorna um dicionário com os campos e valores do nome distinto DN do detentor do certificado e um número que identifica o certificado (2 - entidade legal);
  • publicKey () - retorna um dicionário que contém o valor da chave ('valuepk') e os parâmetros da chave ('curva' e 'hash');
  • signatureCert - retorna dois valores: o algoritmo de assinatura e o valor da assinatura;
  • validityCert - retorna um dicionário com duas chaves 'not_after' e 'not_before';
  • keyUsage () - retorna uma lista de escopos principais;
  • serialNumber () - retorna o número de série do certificado na forma decimal;
  • prettyPrint () - retorna uma string com uma 'impressão' do certificado em termos de pyasn1 (self.cert_full.prettyPrint ()).

O spoiler contém um exemplo de teste que demonstra claramente o trabalho desses métodos.

Teste test795.py para testar o pacote 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 executar um exemplo de teste, basta executar o comando:

 $python test795.py 

Com o pacote fsb795 à sua disposição, era natural escrever em python um utilitário gráfico independente e independente da plataforma para visualizar certificados qualificados. O pacote Tkinter foi usado como suporte gráfico:



O utilitário viewCertFL63 possui três guias. A guia Sobre certificado, entre outras coisas, exibe a hora atual. Voltaremos a ele abaixo. Para selecionar um certificado, basta clicar no botão "Selecionar":



Preste atenção ao botão (aqueles que trabalham no Windows não verão esse botão), ele permite ocultar os chamados arquivos / diretórios invisíveis (ocultos). Para que esse botão apareça, basta executar os seguintes comandos:

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

Um botão muito útil. Portanto, depois de escolher um certificado, a guia "Sobre certificado" assumirá o formato:



O que é digno de nota aqui é que, se o certificado expirar enquanto o exibe, a impressão no ícone no canto superior esquerdo será dividida em duas metades. Todos podem se convencer disso, reorganizando o relógio no computador por um ano de antecedência.
Na guia "Detalhes", é possível visualizar em detalhes as características do atributo selecionado de um certificado qualificado:



E, finalmente, a terceira guia é "Texto". Essa guia exibe o conteúdo de todo o certificado:



Para visualizar o certificado, você pode usar não apenas o Python (o botão "Python"), mas também os utilitários openssl e pp do Network Serurity Services (NSS). Se alguém não tiver esses utilitários, o primeiro poderá ser obtido coletando openssl com suporte para criptografia russa. Ao usar o utilitário pp, a saída do certificado fica assim:



Acima, mencionamos o atributo formatCert da classe Certificate do pacote fsb795. Portanto, precisamos do valor desse atributo para indicar o formato do arquivo com o certificado ao executar este ou aquele utilitário. Por exemplo, chamar o utilitário pp com o formato de arquivo PEM se parece com o seguinte:

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

A opção -a indica o formato do arquivo PEM. Para o formato DER, não está especificado.
O parâmetro "–inform" para o openssl é definido da mesma maneira.
O botão Utilitário é usado para indicar o caminho para os utilitários openssl ou pp.
As distribuições de utilidades ViewCertFL63 estão localizadas aqui .
As distribuições foram construídas usando o pacote pyinstaller:

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

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


All Articles