Квалифицированные сертификаты быстро стали неотъемлемой частью повседневной жизни. И все больше людей хотят увидеть этого «зверя» изнутри. Это с одной стороны. А с другой стороны разрабатывается все больше приложений, в которых задействуется информация иэ этих сертификатов. И это не только атрибуты ИНН или ОГРН владельца или издателя сертификата. Это может быть и информация о том какой криптопровайдер использован владельцем сертификата (атрибут subjectSignTool) для генерации закрытого ключа или на базе каких сертифицированных средств создан удостоверяющий центр (УЦ), выпустивший тот или иной сертификат. И если написать программку, которая будет анализировать выпускаемые сертификаты, то можно будут собрать интересную статистику по тому какие СКЗИ используют владельцы сертификатов и на базе каких (правда это менее интересно) сертифицированных (или несертифицированных) средств развернуты УЦ (атрибут issuerSignTools):
На просторах Хабра уже предпринималась успешная попытка разобрать квалифицированный сертификат. К сожалению, разбор коснулся только получения атрибутов ИНН, ОГРН и СНИЛС, входящие в состав отличительного имени DN (Distinguished Name). Хотя, почему к сожалению? Перед автором стояла конкретная задача и она была решена. Мы же хотим получить доступ к атрибутам квалифицированного сертификата через Python и дать графическую утилиту для их просмотра.
Для доступа к атрибутам сертификата будем использовать пакет fsb795. Пакет доступен как для Pytho2, так и для Python3, как для Linux, так и для Windows. Для его установки достаточно выполнить традиционную команду:
Пакет fsb795 в своей работе использует пакеты pyasn1 и pyasn1-modules. Поэтому если они не установлены, то будет предпринята попытка их установить.
Для python3 эта команда выглядит следующим образом:
Можно также скачать установочные пакеты python3 и python2 и локально их установить.
Название пакета, по аналогии с модулями из пакета pyasn1-modules, например, rfc2459 и т.д., указывает на то, что он предназначен для работы с сертификатами, соответствующими требованиям Приказа ФСБ РФ от 27 декабря 2011 г. № 795 «Об утверждении требований к форме квалифицированного сертификата...».
Доступ к сертификату в пакете fsb795 реализован через класс Certificate:
Для создании экземпляра объекта для конкретного сертификата достаточно выполнить следующий оператор:
В качестве параметра при создании экземпляра класса указывается сертификат, который может находится либо в файле формата PEM или DER или быть строкой в формате PEM.
После создания каждый экземпляр имеет четыре атрибута: pyver, formatCert, cert_full и cert.
По атрибуту pyver можно проверить как прошло распарсивание сертификата. Если pyver равен пустой строке, то файл или строка не содержит сертификата. В противном случае атрибут pyver содержит версию языка python:
Атрибут formatCert при успешном создании экземпляра класса Certificate содержит тип формата файла/строки с сертификатом. Это может быть PEM или DER. Зачем этот атрибут нужен станет ясно ниже.
Пакет fsb795 создавался с использованием пакета pyasn1. Итак, осталось нерассмотренными два атрибута. В атрибуте cert хранится tbs-сертификат, готовый к использованию с пакетом pyasn1. Другой атрибут cert_full хранит весь декодированный сертификат с учетом rfc2459. Покажем, как можно получить алгоритм публичного ключа, имея атрибут cert и подключенный пакет pyasn1:
В конце можно будет оценить возможности пакета fsb795 по получению информации о публичном ключе квалифицированного сертификата.
Когда экземпляр класса Certificate успешно создан, то в нашем распоряжении оказываются методы которые позволяют легко получить необходимые данные из сертификата. Всю информацию о публичном ключе мы можем получить следующим образом:
На данный момент класс Certificate содержит следующие методы:
В спойлере лежит тестовый пример, который наглядно демонстрирует работу этих методов.
Для запуска тестового примера достаточно выполнить команду:
Имея в своем распоряжении пакет fsb795 было естественным написать на языке python самодостаточную платформонезависимую графическую утилиту для просмотра квалифицированных сертификатов. В качестве графической поддержки использован пакет Tkinter:
Утилита viewCertFL63 имеет три вкладки. На вкладке «О сертификате » помимо прочего отображается текущее время. Мы еще вернемся к нему ниже. Для выбора сертификата достаточно нажать кнопку «Выбрать»:
Обратите внимание на кнопку (те кто работают на Windows этой кнопки не увидят), она позволяет скрывать так называемые невидимые файлы/каталоги (hidden). Для того чтобы эта кнопка появилась достаточно выполнить следующие команды:
Очень полезная кнопка. Итак, после выбора сертификата вкладка «О сертификате» примет вид:
Что здесь примечательного так это то, что если во время просмотра сертификата закончится срок его действия, то на иконке в левом верхнем углу печать разломается на две половинки. Каждый может убедится в этом, переставив на компьютере часы на один год вперед.
На вкладке «Детали» можно подробно просмотреть характеристики выбранного атрибута квалифицированного сертификата:
И, наконец, третья вкладка «Текст». В этой вкладке отображается содержимое всего сертификата:
Для просмотра сертификата можно использовать не только Python (кнопка «Python»), то и утилиты openssl и pp из состава Network Serurity Services (NSS). Если у кого-то не окажется этих утилит, то первую можно получить, собрав openssl с поддержкой российской криптографии. При использовании утилиты pp вывод сертификата выглядит так:
Выше мы упоминали про атрибут formatCert класса Certificate пакета fsb795. Так вот значение этого атрибута нам необходимо для указания формата файла с сертификатом при запуске той или утилиты. Например, вызов утилиты pp при формате файле PEM выглядит следующим образом:
Параметр «-a» и указывает на формат файла PEM. Для формата DER он не указывается.
Аналогичным образом задается и параметр "–inform " для openssl.
Кнопка «Утилита» служит для указания пути к утилитам openssl или pp.
Дистрибутивы утилиты viewCertFL63 находятся здесь.
Сборка дистрибутивов была сделана с использованием пакета pyinstaller:
На просторах Хабра уже предпринималась успешная попытка разобрать квалифицированный сертификат. К сожалению, разбор коснулся только получения атрибутов ИНН, ОГРН и СНИЛС, входящие в состав отличительного имени DN (Distinguished Name). Хотя, почему к сожалению? Перед автором стояла конкретная задача и она была решена. Мы же хотим получить доступ к атрибутам квалифицированного сертификата через Python и дать графическую утилиту для их просмотра.
Для доступа к атрибутам сертификата будем использовать пакет fsb795. Пакет доступен как для Pytho2, так и для Python3, как для Linux, так и для Windows. Для его установки достаточно выполнить традиционную команду:
# 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]#
Пакет fsb795 в своей работе использует пакеты pyasn1 и pyasn1-modules. Поэтому если они не установлены, то будет предпринята попытка их установить.
Для python3 эта команда выглядит следующим образом:
# python -m pip install fsb795
...
#
Можно также скачать установочные пакеты python3 и python2 и локально их установить.
Название пакета, по аналогии с модулями из пакета pyasn1-modules, например, rfc2459 и т.д., указывает на то, что он предназначен для работы с сертификатами, соответствующими требованиям Приказа ФСБ РФ от 27 декабря 2011 г. № 795 «Об утверждении требований к форме квалифицированного сертификата...».
Доступ к сертификату в пакете fsb795 реализован через класс 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__":
. . .
Для создании экземпляра объекта для конкретного сертификата достаточно выполнить следующий оператор:
$ 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(<файл/строка с сертификатом>)
>>
В качестве параметра при создании экземпляра класса указывается сертификат, который может находится либо в файле формата PEM или DER или быть строкой в формате PEM.
После создания каждый экземпляр имеет четыре атрибута: pyver, formatCert, cert_full и cert.
По атрибуту pyver можно проверить как прошло распарсивание сертификата. Если pyver равен пустой строке, то файл или строка не содержит сертификата. В противном случае атрибут pyver содержит версию языка 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
>>>
Атрибут formatCert при успешном создании экземпляра класса Certificate содержит тип формата файла/строки с сертификатом. Это может быть PEM или DER. Зачем этот атрибут нужен станет ясно ниже.
Пакет fsb795 создавался с использованием пакета pyasn1. Итак, осталось нерассмотренными два атрибута. В атрибуте cert хранится tbs-сертификат, готовый к использованию с пакетом pyasn1. Другой атрибут cert_full хранит весь декодированный сертификат с учетом rfc2459. Покажем, как можно получить алгоритм публичного ключа, имея атрибут cert и подключенный пакет pyasn1:
>>> pubkey = c2.cert['subjectPublicKeyInfo']
>>> ff = pubkey['algorithm']
>>> ff1 = ff['algorithm']
>>> print (ff1)
1.2.643.2.2.19
>>>
В конце можно будет оценить возможности пакета fsb795 по получению информации о публичном ключе квалифицированного сертификата.
Когда экземпляр класса Certificate успешно создан, то в нашем распоряжении оказываются методы которые позволяют легко получить необходимые данные из сертификата. Всю информацию о публичном ключе мы можем получить следующим образом:
>>> 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
>>>
На данный момент класс Certificate содержит следующие методы:
- subjectSignTool() – возвращает строку с наименованием СКЗИ владельца сертификата;
- issuerSignTool() – возвращает список из четырех элементов с информацией криптографических средствах издателя сертификата;
- classUser() – возвращает строку с oid-ами классов защищенности СКЗИ владельца сертификата, разделенными символами ";;";
- issuerCert() – возвращает словарь с полями и значениями отличительного имени DN издателя сертификата и число, определяющее принадлежность сертификата (2 – юридическое лицо);
- subjectCert() – возвращает словарь с полями и значениями отличительного имени DN владельца сертификата и число, определяющее принадлежность сертификата (2 – юридическое лицо);
- publicKey() – возвращает словарь, содержащий значение ключа ('valuepk') и параметры ключа ('curve' и 'hash');
- signatureCert – возвращает два значения: алгоритм подписи и значение подписи;
- validityCert – возвращает словарь с двумя ключами 'not_after' и 'not_before';
- keyUsage() – возвращает список областей действия ключа;
- serialNumber() – возвращает серийный номер сертификата в десятичном виде;
- prettyPrint() – возвращает строку с 'распечаткой' сертификата в терминах pyasn1 (self.cert_full.prettyPrint()).
В спойлере лежит тестовый пример, который наглядно демонстрирует работу этих методов.
Тест test795.py для тестирования пакета 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=================================')
Для запуска тестового примера достаточно выполнить команду:
$python test795.py
Имея в своем распоряжении пакет fsb795 было естественным написать на языке python самодостаточную платформонезависимую графическую утилиту для просмотра квалифицированных сертификатов. В качестве графической поддержки использован пакет Tkinter:
Утилита viewCertFL63 имеет три вкладки. На вкладке «О сертификате » помимо прочего отображается текущее время. Мы еще вернемся к нему ниже. Для выбора сертификата достаточно нажать кнопку «Выбрать»:
Обратите внимание на кнопку (те кто работают на Windows этой кнопки не увидят), она позволяет скрывать так называемые невидимые файлы/каталоги (hidden). Для того чтобы эта кнопка появилась достаточно выполнить следующие команды:
if sys.platform != "win32":
root.tk.call('set', '::tk::dialog::file::showHiddenBtn', '1')
root.tk.call('set', '::tk::dialog::file::showHiddenVar', '0')
Очень полезная кнопка. Итак, после выбора сертификата вкладка «О сертификате» примет вид:
Что здесь примечательного так это то, что если во время просмотра сертификата закончится срок его действия, то на иконке в левом верхнем углу печать разломается на две половинки. Каждый может убедится в этом, переставив на компьютере часы на один год вперед.
На вкладке «Детали» можно подробно просмотреть характеристики выбранного атрибута квалифицированного сертификата:
И, наконец, третья вкладка «Текст». В этой вкладке отображается содержимое всего сертификата:
Для просмотра сертификата можно использовать не только Python (кнопка «Python»), то и утилиты openssl и pp из состава Network Serurity Services (NSS). Если у кого-то не окажется этих утилит, то первую можно получить, собрав openssl с поддержкой российской криптографии. При использовании утилиты pp вывод сертификата выглядит так:
Выше мы упоминали про атрибут formatCert класса Certificate пакета fsb795. Так вот значение этого атрибута нам необходимо для указания формата файла с сертификатом при запуске той или утилиты. Например, вызов утилиты pp при формате файле PEM выглядит следующим образом:
$pp –t c –u –a –i <файл сертификата>
Параметр «-a» и указывает на формат файла PEM. Для формата DER он не указывается.
Аналогичным образом задается и параметр "–inform " для openssl.
Кнопка «Утилита» служит для указания пути к утилитам openssl или pp.
Дистрибутивы утилиты viewCertFL63 находятся здесь.
Сборка дистрибутивов была сделана с использованием пакета pyinstaller:
$python pyinstaller.py --noconsole -F viewCertFL63.py