RSA шифрование на Python. | LinuxBlog.РФ

Что такое rsa ?

RSA это асимметричный алгоритм шифрования. Зашифровка и расшифровка сообщение происходит двумя разными ключами, так называемый Публичны ключ за зашифровки и приватный(секретный) ключ для расшифровки сообщений . Допустим Сережа хочет отправить сообщение Алисе.

Rsa и python.

Для реализации RSA в Питоне мы будем использовать модуль который так и называется RSA. Он поддерживает шифрование и расшифровку, подписание и проверку подписей в соответствии с PKCS#1 версия 1.5.

Первым делом нам надо сгенерировать пару ключей(публичный и приватный).

import rsa
(pubkey, privkey) = rsa.newkeys(512) # 512 bits длина ключа, рекомендуется не меньше 1024

Так же модуль поддерживает сохранение и загрузку ключей в формате PEM и DER.

pubkey_pem = pubkey.save_pkcs1() #  (format='PEM') 
privkey_pem = privkey.save_pkcs1()
pubkey = rsa.PublicKey.load_pkcs1(pubkey_tmp, 'PEM') #(keyfile:bytesformat='PEM') 

Теперь зашифруем и расшифруем сообщение :

message = 'hello Alisa!'.encode('utf8')
crypto = rsa.encrypt(message, pubkey) # Зашифровка
message = rsa.decrypt(crypto, privkey) # Расшифровка
print(message.decode('utf8'))

Проблема больших сообщений.

RSA может шифровать только сообщения, которые меньше, чем ключ. Пара байт теряются на случайном заполнении, а остальное доступно для само послание. Например, 512-битный ключ может кодировать 53-байт сообщения (512 бит = 64 байта, 11 байт используются для случайного заполнения и другая вещь.)

Но оф. руководство нам предлагает для шифрования больших сообщений воспользоваться блочным шифром, например AES. А его ключ передать зашифрованным с помощью алгоритма RSA :

import rsa.randnum
aes_key = rsa.randnum.read_random_bits(128)# Создаем случайный ключ 128 бит
encrypted_aes_key = rsa.encrypt(aes_key, public_rsa_key) # Зашифровываем ключ и передаем для расшифровки большого сообщения.

На этом все. Успехов )

Разбираем и просматриваем квалифицированные сертификаты средствами python/tkinter

RSA шифрование на Python. | LinuxBlog.РФ

Квалифицированные сертификаты быстро стали неотъемлемой частью повседневной жизни. И все больше людей хотят увидеть этого «зверя» изнутри. Это с одной стороны. А с другой стороны разрабатывается все больше приложений, в которых задействуется информация иэ этих сертификатов. И это не только атрибуты ИНН или ОГРН владельца или издателя сертификата. Это может быть и информация о том какой криптопровайдер использован владельцем сертификата (атрибут subjectSignTool) для генерации закрытого ключа или на базе каких сертифицированных средств создан удостоверяющий центр (УЦ), выпустивший тот или иной сертификат. И если написать программку, которая будет анализировать выпускаемые сертификаты, то можно будут собрать интересную статистику по тому какие СКЗИ используют владельцы сертификатов и на базе каких (правда это менее интересно) сертифицированных (или несертифицированных) средств развернуты УЦ (атрибут issuerSignTools):

RSA шифрование на Python. | LinuxBlog.РФ

На просторах Хабра уже предпринималась успешная попытка разобрать квалифицированный сертификат. К сожалению, разбор коснулся только получения атрибутов ИНН, ОГРН и СНИЛС, входящие в состав отличительного имени 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 декабря 2022 г.

№ 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 2022, 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 содержит следующие методы:

В спойлере лежит тестовый пример, который наглядно демонстрирует работу этих методов.

Для запуска тестового примера достаточно выполнить команду:

$python test795.py

Имея в своем распоряжении пакет fsb795 было естественным написать на языке python самодостаточную платформонезависимую графическую утилиту для просмотра квалифицированных сертификатов. В качестве графической поддержки использован пакет Tkinter:

RSA шифрование на Python. | LinuxBlog.РФ

Утилита viewCertFL63 имеет три вкладки. На вкладке «О сертификате » помимо прочего отображается текущее время. Мы еще вернемся к нему ниже. Для выбора сертификата достаточно нажать кнопку «Выбрать»:

RSA шифрование на Python. | LinuxBlog.РФ

Обратите внимание на кнопку (те кто работают на Windows этой кнопки не увидят), она позволяет скрывать так называемые невидимые файлы/каталоги (hidden). Для того чтобы эта кнопка появилась достаточно выполнить следующие команды:

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

Очень полезная кнопка. Итак, после выбора сертификата вкладка «О сертификате» примет вид:

RSA шифрование на Python. | LinuxBlog.РФ

RSA шифрование на Python. | LinuxBlog.РФ Что здесь примечательного так это то, что если во время просмотра сертификата закончится срок его действия, то на иконке в левом верхнем углу печать разломается на две половинки. Каждый может убедится в этом, переставив на компьютере часы на один год вперед.
На вкладке «Детали» можно подробно просмотреть характеристики выбранного атрибута квалифицированного сертификата:

RSA шифрование на Python. | LinuxBlog.РФ

И, наконец, третья вкладка «Текст». В этой вкладке отображается содержимое всего сертификата:

RSA шифрование на Python. | LinuxBlog.РФ

Для просмотра сертификата можно использовать не только Python (кнопка «Python»), то и утилиты openssl и pp из состава Network Serurity Services (NSS). Если у кого-то не окажется этих утилит, то первую можно получить, собрав openssl с поддержкой российской криптографии. При использовании утилиты pp вывод сертификата выглядит так:

RSA шифрование на Python. | LinuxBlog.РФ

Выше мы упоминали про атрибут 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

Цифровая подпись.

Цифровая подпись (ЦП) позволяет подтвердить авторство электронного документа . Подпись связана как с автором, так и с самим документом с помощью криптографических методов, и не может быть подделана с помощью обычного копирования. Наш модуль RSA позволяет подписывать сообщения для подтверждения автора и целостности сообщения :

(pubkey, privkey) = rsa.newkeys(512)
message = 'Test message'
signature = rsa.sign(message, privkey, 'SHA-1') # Создание подписи rsa.sign(message, priv_key, hash_method),можно использовать ‘MD5’, ‘SHA-1’, ‘SHA-224’, 'SHA-256’, ‘SHA-384’ и ‘SHA-512’

Для проверки подписи используйте rsa.verify() функция. Эта функция возвращает значение True, если проверка прошла успешно:

>>> message = 'Test message'
>>> rsa.verify(message, signature, pubkey)
True

Если подпись не действительно выйдет исключение rsa.pkcs1.VerificationError

>>> message = 'Test message not true'
>>> rsa.verify(message, signature, pubkey)
Traceback (most recent call last):
File "", line 1, in
File "/home/sybren/workspace/python-rsa/rsa/pkcs1.py", line 289, in verify
raise VerificationError('Verification failed')
rsa.pkcs1.VerificationError: Verification failed

Оцените статью
ЭЦП64
Добавить комментарий