Плагин Flutter для формирования электронной цифровой подписи с помощью алгоритмов ГОСТ по стандарту PKCS7 Cryptographic Message Syntax (CMS)
⚠ Рекомендуется работать через GitHub из-за ограничения по размеру в pub.dev для плагинов в 100 МБ (не хватает места для нативных SDK КриптоПро). Также, там есть эти библиотеки, требуемые в инструкции по установке (если работать с pub.dev).
crypt_signature:
git:
url: https://github.com/MaximMonakhov/crypt_signature.git
ref: v5.4.0-github
Описание
Плагин принимает сертификаты в формате PKCS12 .pfx
Поддерживаемые алгоритмы для Android: GOST R 34.10-2001, GOST R 34.10-2012, GOST R 34.10-2012 Strong
Поддерживаемые архитектуры для Android: arm64-v8a, armeabi-v7a
Поддерживаемые алгоритмы для iOS: GOST R 34.10-2012
⚠ Приватный ключ должен быть помечен как экспортируемый
Установка
Подключение плагина к Android проекту
-
Скопировать
.aar
библиотеки изandroid/libs
плагина к себе в проект вandroid\app\libs
-
Добавить в
build.gradle
minSdkVersion 24
buildTypes {
release {
shrinkResources false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: '*.aar')
}
- Создать файл
proguard-rules.pro
вandroid/app
-keep public class ru.CryptoPro.*
Библиотеки .aar указаны в плагине как compile-only, так как невозможно к .aar (коим является этот плагин) подключать другие .aar, для этого требуется скопировать их к себе в проект и подключить как implementation. Proguard используется, чтобы запретить обфускацию кода, которая происходить при выполнении релизной сборки.
Подключение плагина к iOS проекту
Добавить папки en.lproj, locale, ru.lproj
и файлы kis_1, root.sto, config.ini, license.enc
из ios/Resources
плагина к себе в проект через Xcode (Add files to Runner...).
Это нужно для того, чтобы эти файлы нашел КриптоПРО для проверки целостности. Это ресурсы копируются в папку с плагином, а КриптоПРО ищет их в корневой папке приложения.
Использование
Работать с плагином можно:
- Используя собственный интерфейс плагина
- Через классы по нужному типу подписи (MessageSignRequest, PKCS7MessageSignRequest, ...)
- Напрямую через методы
Описание режимов работы:
MessageSignRequest
- Высчитывает хэш от сообщения, подписывает его и возвращает сигнатуру в формате Base64.
PKCS7MessageSignRequest
- Поддерживает detached/attached форматы PKCS7.
- При выборе сертификата пользователем, получает сообщение через функцию
getMessage
. - Высчитывает хэш от полученного сообщения.
- Формирует PKCS7 и атрибуты подписи.
- Высчитывает хэш от атрибутов подписи.
- Подписывает этот хэш атрибутов.
- Вставляет сигнатуру в PKCS7 и возвращает его в формате Base64.
PKCS7HASHSignRequest
- Работает как и
PKCS7MessageSignRequest
, но не высчитывает хэш от первоначального сообщения. - External Digest.
- При выборе сертификата пользователем, получает хэш для подписи через функцию
getDigest
.
- Работает как и
CustomSignRequest
- Создан для выполнения собственной логики ЭП.
Иными словами. PKCS7MessageSignRequest
считает хэш сам, а PKCS7HASHSignRequest
получает его готовый извне.
Пример работы режимов:
MessageSignRequest
Применяется, когда у проверяющего есть сертификат пользователя и он хочет только проверить ЭП. Например, аутентификация путем ЭП случайного набора байт.PKCS7MessageSignRequest
Применяется, когда у проверяющего нет информации о сертификате пользователя и дополнительной информации. Например, регистрация путем ЭП случайного набора байт.PKCS7HASHSignRequest
Применяется, когда с сервера нецелесообразно передавать изначальное сообщение (из-за его большого размера к примеру). Тогда на выполнение ЭП передается уже готовый хэш от этого сообщения. Например, ЭП документов.
Использование через интерфейс
CryptSignature.interface
При вызове данного метода открывается экран с возможностью добавления/выбора/хранения сертификатов.
Методы плагина
- Инициализировать провайдер
CryptSignature.initCSP()
- Установить новую лицензию
CryptSignature.setLicense(String license)
- Получить информацию о текущей лицензии
CryptSignature.getLicense()
- Добавить сертификат в хранилище
CryptSignature.addCertificate(File file, String password)
- Получить список сертификатов, добавленных пользователем
CryptSignature.getCertificates()
- Очистить список сертификатов
CryptSignature.clear()
- Вычислить хэш сообщения/документа
CryptSignature.digest(Certificate certificate, String password, String message)
- Вычислить подпись хэша
CryptSignature.sign(Certificate certificate, String password, String digest)
Пример
Пример использования плагина через интерфейс
/// MessageSignRequest
CryptSignature crypt = await CryptSignature.getInstance();
SignResult? result = await crypt.interface(context, MessageSignRequest("СООБЩЕНИЕ_В_BASE64"));
print(result?.signature); // Сигнатура в формате Base64
/// PKCS7MessageSignRequest
CryptSignature crypt = await CryptSignature.getInstance();
Future<String> getMessage(Certificate certificate) async => message; // Callback для запроса на сервер с выбранным пользователем сертификатом для формирования сообщения
PKCS7SignResult? result = await crypt.interface(context, PKCS7MessageSignRequest(getMessage));
print(result?.pkcs7.encoded); // PKCS7 в формате Base64
/// PKCS7HASHSignRequest
CryptSignature crypt = await CryptSignature.getInstance();
Future<String> getDigest(Certificate certificate) async => digest; // Callback для запроса на сервер с выбранным пользователем сертификатом для формирования хэша
PKCS7SignResult? result = await crypt.interface(context, PKCS7HASHSignRequest(getDigest));
print(result?.pkcs7.encoded); // PKCS7 в формате Base64
Пример использования плагина через SignRequest
Рекомендуется, если не пользуетесь интерфейсом. PKCS7SignRequest сам создаст PKCS7 и подпишет его атрибуты подписи.
/// MessageSignRequest
CryptSignature crypt = await CryptSignature.getInstance();
// Получение файла .pfx и запрос пароля
MessageSignRequest request = MessageSignRequest("СООБЩЕНИЕ_В_BASE64");
SignResult result = await request.signer(certificate, password);
print(result.signature); // Сигнатура в формате Base64
/// PKCS7MessageSignRequest
CryptSignature crypt = await CryptSignature.getInstance();
// Получение файла .pfx и запрос пароля
Certificate certificate = await crypt.addCertificate(file, password);
Future<String> getMessage(Certificate certificate) async => message; // Callback для запроса на сервер с выбранным пользователем сертификатом для формирования сообщения
PKCS7MessageSignRequest request = PKCS7MessageSignRequest(getMessage);
PKCS7SignResult result = await request.signer(certificate, password);
print(result.pkcs7.encoded); // PKCS7 в формате Base64
/// PKCS7HASHSignRequest
CryptSignature crypt = await CryptSignature.getInstance();
// Получение файла .pfx и запрос пароля
Certificate certificate = await crypt.addCertificate(file, password);
Future<String> getDigest(Certificate certificate) async => digest; // Callback для запроса на сервер с выбранным пользователем сертификатом для формирования сообщения
PKCS7HASHSignRequest request = PKCS7HASHSignRequest(getDigest);
PKCS7SignResult result = await request.signer(certificate, password);
print(result.pkcs7.encoded); // PKCS7 в формате Base64
Пример использования плагина через методы
/// Стандартная подпись данных
CryptSignature crypt = await CryptSignature.getInstance();
// Получение файла .pfx и запрос пароля
Certificate certificate = await crypt.addCertificate(file, password);
// Получение сообщения для ЭП
DigestResult digestResult = await crypt.digest(certificate, password, message);
SignResult signResult = await crypt.sign(certificate, password, digestResult.digest);
print(signResult.signature);
В случае PKCS7 подписи, PKCS7 нужно будет сформировать самому и подписывать атрибуты подписи. Как пример можно посмотреть как работает Signer в PKCS7MessageSignRequest или PKCS7HASHSignRequest.
Известные проблемы
- Ошибка при вызове encode у PKCS7 второй раз после прикрепления сигнатуры. Возникает из-за того, что пакет ASN1 зачем-то кэширует результат первого кодирования без сигнатуры.
- Отсутствует поддержка GOST R 34.10-2001, GOST R 34.10-2012 Strong для iOS