Использование Crypto API

Содержание
  1. Введение
  2. Capicom
  3. Cryptopro
  4. Алгоритмы
  5. Вкратце
  6. Запуск режима разработки
  7. Запуск тестов
  8. Зачем мне этот пакет?
  9. Импорт сессионных ключей
  10. Контейнеры ключей
  11. Криптопро.net sdk работа с эцп
  12. Криптопровайдеры
  13. Лицензия
  14. Метод getalgorithm
  15. Метод getbyhash
  16. Метод gethash
  17. Метод getissuer
  18. Метод getowner
  19. Метод haskeyusageoid
  20. Метод iskeyvalid
  21. Метод signhash
  22. Метод signpkcs7
  23. Метод signxml
  24. Метод verifyhash
  25. Метод verifyxml
  26. Методы объекта cryptopro
  27. Методы объекта сертификата
  28. Ошибка параметр задан неверно. (0x80070057)
  29. Поддерживаемые скзи
  30. Подключение
  31. Примеры
  32. Проверка пакета перед публикацией в npm
  33. Проверка цифровой подписи
  34. Работа с форматом base64 (pem)
  35. Расшифровывание
  36. Расшифровывание и проверка совмещенной цифровой подписи и шифрованных данных
  37. Сертификаты
  38. Совмещение цифровой подписи и шифрованных данных
  39. Строение и возможности crypto api
  40. Тем, кто хочет помочь
  41. Установка
  42. Хеширование
  43. Цифровая подпись
  44. Шифрование
  45. Экспорт сессионных ключей
  46. Метод getextendedkeyusage

Введение

Кажется, что все находится в рабочем состоянии для вышеупомянутых фундаментальных операций с криптографическими сообщениями. За исключением работы, выполняемой на уровне данных, которые обычно принимаются в криптографии. Другими словами, выходные данные, создаваемые фундаментальными операциями Crypto API, могут быть обработаны только этими фундаментальными операциями. Использование функций высокого уровня при работе в криптографических приложениях устраняет этот недостаток.

Стандарт RFC 2315 является основой для функций PKCS 7. Кроме того, будет указано, что важно знать особенности этого стандарта.

Инструментом для кодирования и описания различных типов данных, используемых в криптографических приложениях, является стандарт P KCS 7. В результате появляются рекомендации по кодированию подписанных и зашифрованных сообщений.

Вы можете создавать кроссплатформенные приложения благодаря многочисленным модификациям стандарта PKCS ECU. Высокоуровневые функции облегчают работу с криптографическими данными, не скрывая всех нюансов использования PKCS 7.

Невозможность загрузки входных данных блоками — это одна из проблем функций высокого уровня, в частности. Низкоуровневые функции, которые будут рассмотрены позже, используются при работе с большими объемами данных и более тонком использовании всех возможностей Crypto API в контексте обработки сообщений.

Capicom

Иногда применение функций Crypto API напрямую сопряжено с серьезными трудностями. В Web-клиентах, где невозможно вызвать процедуры напрямую. Для таких целей, а также для упрощения работы с Crypto API был создан тип объектов СAPICOM (CryptoApi camicom).

Все, что упоминается в этой статье, от шифрования до работы с сертификатами, почти полностью покрывается реализацией объекта. Благодаря использованию двойных интерфейсов к объекту можно получить доступ как от клиентов с ранней привязкой (C), так и от клиентов без поздней привязки.

Высокоуровневые методы криптографической защиты сообщений, которые обсуждались ранее, являются основой всех функций CAPICOM. В частности, PKCS 7 при вторичном кодировании служит основополагающим стандартом для выходных данных.

ПРЕДУПРЕЖДЕНИЕ

Не существует полной прямой связи между функциями Crypto API и Capicom и объектом Capicom, несмотря на то, что последний использует широко распространенный стандарт PKCS 7 для отображения выходных данных. Выходные данные объекта Capicom неприемлемы в качестве входных данных для функций Crypto API. Перед выполнением функций в объекте Capicom происходит предварительное изменение входных данных. Алгоритм, используемый для выполнения функции подписи, например, выглядит следующим образом:

1. Получите входные данные.

Для внутреннего использования измените их.

3. Используйте стандартные API для криптографии.

Для «изменения» входных данных используется простая вставка символа с кодом 0x00.

Поскольку объект CAPICOM является компонентом API scripto, он не будет подробно рассмотрен в этой статье.

Cryptopro

JavaScript API, работающий с КриптоПро ЭЦП асинхронно

Это проект Vgoma / Crypto-Pro Fork

Оригинальный README и форк API отличаются.

Алгоритмы

Внутри провайдеров Crypto API реализует криптографические алгоритмы на практике. Вы можете установить или получить различные параметры алгоритма, а также многое другое, используя криптопровайдер. В Crypto API принято разделять алгоритмы на группы:

  • Алгоритмы шифрования/дешифрования,
  • Алгоритмы получения хэш-значений,
  • Алгоритмы получения цифровых подписей,
  • Алгоритмы обмена шифровальными ключами.

Для более глубокого понимания шифрования необходимо ознакомиться с идеями симметричных и асимметричных алгоритмов. Протокол, известный как симметричный алгоритм, использует один ключ для шифрования и дешифрования.

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

Открытый ключ и секретный ключ — это два ключа, которые используют алгоритмы. Информация обычно шифруется с помощью открытого ключа, а расшифровывается с помощью секретного ключа. Открытый ключ не является конфиденциальным и предназначен для передачи по сетям общего пользования.

Человек, который получает открытый ключ и передает его владельцу секретного ключа по открытым каналам. Для расшифровки данных на стороне получателя может быть использован только секретный ключ из пары, к которой принадлежал открытый ключ.

В повседневной жизни асимметричные алгоритмы не очень полезны. В чем причина? Для асимметричных алгоритмов просто слишком сложные числа. Симметричный криптографический алгоритм, имеющий значение сеансового ключа, обычно выполняет шифрование данных.

Затем для шифрования ключа используется асимметричный алгоритм (длина ключа пренебрежимо мала по сравнению с потенциальной размерностью данных). Обратные списки данных создаются на стороне получателя путем расшифровки асимметричного ключа с помощью симметричного алгоритма. Можно объединить преимущества обоих методов, используя зашифрованный код.

Все важные алгоритмы Crypto API следуют этой методологии. Пары сеансовых ключей хранятся в контейнерах ключей и необходимы для шифрования и обмена данными.

Назовите фундаментальные операции, выполненные на алгоритмах Crypto API:

  • Перечисляет все криптографические алгоритмы. Это делается путем вызова функции CryptGetProvParam с параметром dwParam, установленным в PP_ENUMALGS.
  • Получить параметры алгоритма. Обычно выполняется непосредственно во время перечисления алгоритмов путем вызова функции CryptGetProvParam с параметром dwParam, установленным в значение PP_ENUMALGS_EX. В этом случае результирующая структура может содержать практически всю информацию об алгоритме.

Поиск поставщика криптоуслуг, который использует конкретный алгоритм, с которым мы знакомы, может быть необходим в некоторых обстоятельствах. У поставщиков криптоуслуг есть специальный цифровой номер, который они могут использовать для идентификации алгоритмов. Этот номер похож на Dword и имеет тип Alg_id.

В дополнение к ALG_ID существует еще один адрес OID. Идентификатор представляет собой строку с точечными разделителями, взятую из стандартов RSA Security.

Согласно стандартам, каждому набору цифр в OID присваивается номер, который может быть использован для доступа к дополнительным данным (например, названию компании, создавшей алгоритм). Многочисленные алгоритмы преобразуются в типы в процессе преобразования между типами.

Вкратце

  1. Подключение
  2. Получение информации о ключе
  3. Получение всех сертификатов, установленных на компьютере
  4. Вычисление хэша
  5. Создание подписи
  6. Проверка подписи

Запуск режима разработки

Устанавливаем зависимости

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

И пример, на котором можно тестировать изменения.
Удобнее всего тестировать на примере с тэгом script, тк он отвязан от фреймворков
и использует сборку в формате UMD из папки dist/, постоянно обновляемую пока работает
сборщик. Запускаем его таким образом:

После выполнения npm link ../../ в директории examples/script-tag/node_modules папка crypto-pro-js станет ярлыком,
указывающим на папку содержащую локально собранный пакет.

Запуск тестов

Тесты, созданные с использованием шутки

Зачем мне этот пакет?

КриптоПРО ЭЦП Browser Plug-In доступен в разных браузерах в двух версиях.
Асинхронной (в современных браузерах) и синхронной (в браузерах постарше).
С помощью этого пакета можно не писать реализацию под каждую версию плагина дважды.

Импорт сессионных ключей

Фундаментальная цель импорта ключей объясняется следующим образом:

Функция получает контекст CryptoProvider в качестве своего первого аргумента. Следует отметить, что ключевой контейнер должен быть указан во время инициализации CryptoProvider, чтобы функция CryptimPortKey функционировала должным образом.

Это имеет решающее значение, в частности, для успешного импорта контейнером секретных ключей. Ссылка на импортируемые данные предоставляется параметром PBDATA, а параметр DWDATElen определяет размер этих данных. Ручка Key, которая используется при импорте (для расшифровки сеансового ключа), указывается параметром HPUBKEY.

Пример использования данной функции показан ниже:

Контейнеры ключей

База данных, используемая для создания и обмена цифровыми подписями, включает в себя контейнер ключей. В качестве хранилища ключей могут использоваться такие вещи, как реестр на диске или участок временной памяти.

Ключевые контейнеры бывают двух разновидностей: системный уровень и пользовательский уровень. Какой контекст является контейнером контекста пользователя?Никто другой не имеет доступа к контейнерам по умолчанию.

Пользовательские программы обычно не используют контейнеры ключей системного уровня, поскольку они доступны без интерактивной идентификации пользователя. Вместо них это делают сервисные приложения. Контейнеры ключей существуют только в контексте криптопровайдера; самостоятельно они не существуют.

Ключи различаются у разных криптопровайдеров. Даже один и тот же математический алгоритм может быть реализован разными криптопровайдерами по-разному. Однако между криптопровайдерами могут быть существенные различия в том, как хранятся ключи.

Основные операции с контейнерами ключей:

Криптопро.net sdk работа с эцп

Я решил идти по пути наименьшего сопротивления и использовать консольную утилиту Cyptcp компании КриптоПро. Для работы утилиты нужен криптопровайдер КриптоПро CSP. КриптоПро CSP на одну машине стоит около 2 000 руб, а Cyptcp около 700 руб.
Cyptcp и АРМ КриптоПро совместимы между собой.

Я формирую команды:

 /// <summary>
/// Формирование команд для утилиты Cyptcp
/// </summary>
public static class Command
{
    /// <summary>
    /// Команда на запуск дешифрования
    /// </summary>
    /// <param name="nameCertificate">Имя сертификата</param>
    /// <param name="pathEncryptedFile">Путь к защифрованному файлу </param>
    /// <param name="pathSave">Путь к дешифрованному файлу</param>
    /// <returns></returns>
    public static string Decript(string nameCertificate, string pathEncryptedFile, string pathSave)
    {
        return "-decr -dn ""   nameCertificate   "" "  """  pathEncryptedFile   ""   ""   pathSave """;
    }

    /// <summary>
    /// Проверка подписи. Сертификат выбирается из хранилища сертификатов
    /// </summary>
    /// <param name="pathSignFile">Путь к подписанному файлу</param>
    /// <param name="path">Путь к очищенному от подписи файлу</param>
    /// <returns></returns>
    public static string VerificationSignature(string pathSignFile, string pathFile)
    {
        return "-verify -verall "    """   pathSignFile   ""   ""   pathFile   """;
    }

    public static string Sign(string nameCertificate, string pathFile, string pathSignedFile)
    {
        return "-sign -dn ""   nameCertificate   ""  "   """   pathFile   ""   ""   pathSignedFile   """;
    }

    public static string Encrypt(string nameRecipientCertificate, string pathSigFile, string pathSave)
    {
        return "-encr -dn ""   nameRecipientCertificate   "" "   """   pathSigFile   ""   ""   pathSave   """;
    }
}

Команда для запуска утилиты:

   private void RunCommand(string command, string pathApplication)
    {
        using (Process process = new Process())
        {
            //Имя файла.
            process.StartInfo.FileName = pathApplication;
            //Директория.
            process.StartInfo.WorkingDirectory = Path.GetDirectoryName(pathApplication);
            //Команда. 
            process.StartInfo.Arguments = command;

            //Не показывать окно приложения.
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.UseShellExecute = false;

            process.StartInfo.RedirectStandardInput = true;

            process.StartInfo.StandardOutputEncoding = Encoding.Default;

            process.Start();

            //Ответ на диалог выбора сертификата
            process.StandardInput.WriteLine("y");
        }
    }

Криптопровайдеры

Независимый модуль, известный как «криптопровайдер», обеспечивает прямую связь с криптографическими алгоритмами. Каждый криптопровайдер должен предлагать:

  • Реализация стандартного интерфейса криптопровайдера;
  • Работа с ключами шифрования, предназначенными для обеспечения работы конкретных алгоритмов для данного криптопровайдера;
  • Невозможность вмешательства третьих лиц в схему работы алгоритмов.

В реализации провайдеров используются динамически загружаемые библиотеки (DL). Таким образом, для изменения потока алгоритмов криптопровайдера (всех компонентов) требуется цифровая подпись.

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

Следующие категории могут быть использованы для разделения задач, связанных с работой с поставщиками крипто -услуги:

  • Функции для инициализации контекста и получения параметров криптопровайдера;
  • Функции для генерации ключей и работы с ними;
  • Функции для шифрования/дешифрования данных;
  • Функции для хэширования и получения цифровой подписи данных.

Следующие функции входят в состав функций инициализации контекста:

  • CryptAcquireContext. Во-первых, эта функция инициализирует контекст криптопровайдера (она получает ссылку на HANDLE, которая может быть использована позже в других функциях). Кроме того, используя последний параметр этой функции, можно создать или удалить ключевой контейнер.
  • CryptContextAddRef. Эта функция используется для увеличения внутреннего счетчика ссылок криптопровайдера. Рекомендуется использовать эту функцию, когда контекст криптопровайдера передается как член различных структур, передаваемых функциям.
  • CryptReleaseContext. Эта функция предназначена для освобождения контекста криптопровайдера, полученного с помощью функции CryptAcquireContext. На самом деле, он просто уменьшает внутренний счетчик ссылок криптопровайдера (подобно механизму подсчета ссылок COM-объекта). Когда внутренний счетчик ссылок становится равным нулю, контекст криптопровайдера полностью освобождается и больше нигде не может быть использован.
  • CryptGetProvParam. Эта функция используется для получения значения различных параметров криптопровайдера. Следует сказать, что лишь ограниченный набор параметров определен стандартом для всех криптовалютных провайдеров. Набор параметров криптопровайдера может значительно отличаться в зависимости от реализации криптопровайдера.

Генерация ключей включает следующие функции:

Лицензия

MIT

Метод getalgorithm

Информация об алгоритме

Метод getbyhash

Получение сертификата с использованием хеш-ключа на основе отпечатков пальцев

Метод gethash

Вычисление хеш-значения Gost для набора данных.

Параметры:

  • Строка для вычисления хэша {String}
  • Вычисление хэша двоичных данных {Логическое значение, по умолчанию: false}

Метод getissuer

Кредит от эмитента (IssuerName) в вашем регионе

Метод getowner

Тема: «Заявление на получение кредита на ваше имя»

Метод haskeyusageoid

Изучение ключа шифрования для идентификаторов OID

Метод iskeyvalid

Как определить, является ли ключ действительным

Метод signhash

Создание ЭП по хэш значению

Параметры:

  • Хэш / отпечаток {String}
  • Вычисленный хэш {String}
  • Отменен {Boolean, по умолчанию:false}

Метод signpkcs7

Формат PKCC используется при создании Pkcs7.

Параметры:

  • Хеш / Fingerprint {String}
  • Данные для подписи {String}
  • Прикрепленная / отсоединенная {Boolean, default:false}

Метод signxml

Создание EP в формате XML

Параметры:

  • Хэш/отпечаток {Строка}
  • Данные для подписи {Строка}

Метод verifyhash

Верификация подписи по хэш значению

Параметры:

  • Хэш / отпечаток {String}
  • Вычисленный хэш данных {String}
  • Подписанная строка {String}
  • Offline {Boolean, по умолчанию:false}.

Метод verifyxml

Проверка элементов в формате XML

Параметры:

  • Строка с подписью {String}

Методы объекта cryptopro

Вы можете получить сертификаты из частных ключей, хранящихся на общем флеш -накопителе, используя методы GetContainerCertificates и Getall CentifierCertificate.

Методы объекта сертификата

Сертификат предоставляет следующие API:

Ошибка параметр задан неверно. (0x80070057)

Прочитайте этот вопрос, если вы столкнулись с проблемой при попытке подписать сообщение подписями PKCS7.

Поддерживаемые скзи

КриптоПРО CSP (v4.0)

Подключение

Достаточно подключить один файл, чтобы эта крошечная штука начала плавать.

Примеры

Для функционирования этим программам требуются NodeJS LT.

Проверка пакета перед публикацией в npm

Необходимо протестировать работу в заявленных браузерах, сделав это на локально запакованной версии пакета.
Для этого собираем пакет:

npm run package
mv package ..

Перемещение папки пакета вверх поможет вам избежать конфликтов при создании ссылок на текущую папку.

Если в любом из каталогов есть ссылка на предварительно собранную упаковку, мы включаем там ссылку.

Испытания прототипов как в режиме разработки, так и в режиме производства.

./. /.-package

Проверка цифровой подписи

Цифровая подпись проверяется с использованием функций Smerve Veright и CrypverifyMessageGinature. Данные вместе с цифровой подписью сначала подтверждены.

Анализируя возвращаемое значение функции, можно определить результат проверки — достоверна цифровая подпись или нет. Вторая функция проверяет достоверность подписи, даже если в ней отсутствуют данные, которые необходимо подписать. Первая функция описывается следующим образом:

Первым входом функции служит указатель на структуру типа CRBPT_VERIFE-PARA. Адрес криптопровайдера, который используется для проверки подписи, содержится в полях структуры.

Функция CryptVerifyMessageGinature ищет этот сертификат внутри самой подписи, когда ссылка на эту функцию является нулевой. Количество подписчика, чья подпись должна быть проверена, определено параметром DWSignerIndex.

Блоки входных данных указываются параметрами PP PBSignedBlob и SBSigned Blob, а первый индекс имеет значение 0. Информация о блоке памяти, где хранится декодированное сообщение, может передаваться между PBDECODE и PCDEDEAD.

Если PCBDECODED установлен на NULL, и эта информация не требуется. Двойной указатель на контекст сертификата абонента возвращается параметром PPSignerTert.

Летернатифен из использования этой функции аналогичен примеру для функции CryptVerifyDetachedMessageGignature, которая была представлена выше.

Функция, проверяющая отсоединенную цифровую подпись

Значение параметров Pverifypara и dwsigneindex совпадает с функцией cryptversemessage. Область памяти, в которой хранятся проверенные данные, описывается PBDetachedSignedBlob и CB Dentaching, подписанные Bloob.

Массивы, передаваемые через параметры rgpb и RcCB, имеют определенное количество элементов, на которое указывает параметр cToBeSigned. Область памяти, на которой проверяется цифровая подпись, содержится в параметре rgpbToBeSigned.

Метрика rgpbToBeSigned включает минимальный размер блока подписи, указанный метрикой rgippttobos. Двунаправленная стрелка указывает на раздел метаданных сертификата в спецификации ppSignerCert. Результаты проверок (была ли использована правильная или неправильная цифровая подпись) сообщаются как эффективность процедуры CryptVerifyDetachedMessageSignature.

Ниже приведена иллюстрация использования этой функции:

Работа с форматом base64 (pem)

Важно отметить, что PEM (Privacy-exchanged Mail) — это еще один компонент стандарта PKCS 7. Принятый стандарт представляет собой последовательность из двух кодировок: первичная кодировка в PKCS, а вторичная — в Base64.

После завершения шифрования с помощью высокоуровневой функции CryptEncryptionMessage мы можем получить иллюстрацию применения стандарта PEM путем кодирования результата в кодировке Base64.

Base64 очень прост в использовании и применяется во многих библиотеках. Я хочу специально предложить читателям метод понимания функций BASE64ENCODE и BESS64DEKOD. Функции, перечисленные в названии этой строки, отмечены в заголовочном файле заголовка ATL «Atlenc.h»; их использование достаточно просто.

char data[] = "Test";

int len = 0;

len = Base64EncodeGetRequiredLength(sizeof(data),  ATL_BASE64_FLAG_NOCRLF); 
LPSTR out = static_cast<LPSTR>(malloc(len   1));
ZeroMemory(out, len   1);
Base64Encode((BYTE*)data, sizeof(data), out, &len, ATL_BASE64_FLAG_NOCRLF);

std::cout << "Encode completed" << std::endl;


std::cout << "Encoded string: " << out << std::endl;

char* decode = static_cast<char*>(malloc(strlen(data)   1));
ZeroMemory(decode, strlen(data)   1);

Base64Decode(out, len, (BYTE*)decode, &len);

std::cout << "Decode completed" << std::endl;


std::cout << "Decoded string: " << decode << std::endl;

free(out);
free(decode);

Расшифровывание

Ниже приводится описание системы CryptDecryptMessage для дешифровки зашифрованного содержимого:

Указатель на структуру типа CRM_DECRP PAR служит первым параметром этой функции. В полях этой структуры будет передан список сертификатов, в котором будет искаться ключ сеанса (сертификат обмена).

Информация о блоке входных данных, который необходимо расшифровать, передается через параметры pbEncryptedBlob и cbEncryptedBlob.

Расшифрованные данные будут храниться в этом буфере. Когда это не требуется, параметр ppXchgCert должен быть установлен в NULLO. Этот параметр возвращает ссылку на контекст сертификата, который был использован для обмена.

ПРЕДУПРЕЖДЕНИЕ

Локальный контейнер необходим для импорта файла сеансового ключа (сертификата обмена) на стороне получателя. Функция завершается, если секретный ключ для пары ключей не может быть найден.

Ниже приводится иллюстрация использования этой функции:

HCERTSTORE hSystemStore;

if(!(hSystemStore = CertOpenSystemStore(NULL, "MY")))
{
  Error("CertOpenSystemStore");
  return;
}

std::cout << "System store is opened" << std::endl;


CRYPT_DECRYPT_MESSAGE_PARA DecryptPara;
ZeroMemory(&DecryptPara, sizeof(DecryptPara));

DecryptPara.cbSize = sizeof(DecryptPara);
DecryptPara.dwMsgAndCertEncodingType = 
  X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;

HCERTSTORE StoreArray[] = { hSystemStore };

DecryptPara.cCertStore = 1;
DecryptPara.rghCertStore = StoreArray;

DWORD dwSize = 0;

if(!CryptDecryptMessage(&DecryptPara, encrypted, count, NULL, 
&dwSize, NULL))
{
  Error("CryptDecryptMessage");
  return;
}

BYTE* decrypted = static_cast<BYTE*>(malloc(dwSize   1));
ZeroMemory(decrypted, dwSize   1);

if(!CryptDecryptMessage(&DecryptPara, encrypted, count, decrypted,
 &dwSize, NULL))
{
  Error("CryptDecryptMessage");
  return;
}

std::cout << "Decryption completed" << std::endl;

std::cout << "Result string: " << decrypted << std::endl;

free(decrypted);
free(encrypted);
CertCloseStore(hSystemStore, 0);

Расшифровывание и проверка совмещенной цифровой подписи и шифрованных данных

Используйте специальную функцию CryptDecryptAndVerifyMesageSignature, которая имеет следующее описание, для расшифровки и проверки комбинированной цифровой подписи и зашифрованных данных:

Рассчитаемые параметры для этой функции полностью соответствуют рассчитанным параметрам для функций CryptDecryptMessage и CryptVerifyMessageGinature. Результаты тестов — будь то цифровая подпись правильной или нет — получаются как производительность процедуры CryptDecryptingAnderifyMessageGinature.

Ниже приведен пример использования этой функции:

HCERTSTORE hSystemStore;

if(!(hSystemStore  =  CertOpenSystemStore(NULL, "MY")))
{
  Error("CertOpenSystemStore");
  return;
}

std::cout << "System store is opened" << std::endl;


CRYPT_DECRYPT_MESSAGE_PARA DecryptPara;
ZeroMemory(&DecryptPara, sizeof(DecryptPara));

DecryptPara.cbSize = sizeof(DecryptPara);
DecryptPara.dwMsgAndCertEncodingType = 
  X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;

HCERTSTORE StoreArray[] = {hSystemStore};

DecryptPara.cCertStore = 1;
DecryptPara.rghCertStore = StoreArray;


CRYPT_VERIFY_MESSAGE_PARA VerifyPara;
ZeroMemory(&VerifyPara, sizeof(VerifyPara));

VerifyPara.cbSize = sizeof(VerifyPara);
VerifyPara.dwMsgAndCertEncodingType = 
  X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
VerifyPara.hCryptProv = NULL;

BOOL result = CryptDecryptAndVerifyMessageSignature(&DecryptPara, 
  &VerifyPara, 0, encrypted, count, NULL, NULL, NULL, NULL);

std::cout << "Decryption and verification completed" << std::endl;

std::cout << "Result: string " << ((result)?"verified!":"NOT verified!") 
  << std::endl;

free(encrypted);
CertCloseStore(hSystemStore, 0);

Сертификаты

Эта тема будет полностью объяснена в отдельном разделе. Поскольку CryptsignMessage используется для операций высокого уровня при обработке криптографических сообщений, также сказано о сертификатах. Понимание функций сертификатов в системе имеет решающее значение.

  • Хранение дополнительной информации о владельце сертификата
  • Хранение значения открытого ключа владельца
  • Получение ссылки на контейнер ключей. Контейнер ключей может быть физически связан с сертификатом, установленным в системе. Это означает, что при наличии только ссылки на контейнер сертификата можно установить однозначную связь с секретным ключом и использовать его, например, при проверке цифровой подписи или расшифровке данных.

Сертификаты можно рассматривать как удостоверения личности в упрощенной форме. Только заслуживающие доверия учреждения, такие как полиция или ФСБ. Однако теоретически вы можете генерировать сертификаты самостоятельно.

В этом сценарии у пользователя, который будет использовать этот сертификат, есть выбор, верить ему или нет (поскольку он не может нарисовать паспорт). Только последние два сертификата из приведенного выше списка — те, которые хранят открытый ключ и могут быть связаны с контейнером Keys, который также хранит секретный ключ — имеют решающее значение для нашего исследования высокоуровневых функций криптографической обработки информации. На данном этапе этой информации будет достаточно.

Вы можете вызвать Internet Explorer, чтобы ознакомиться с сертификатами, если вы никогда раньше с ними не работали. Вам будет показан список установленных в системе сертификатов.

Совмещение цифровой подписи и шифрованных данных

Стандарт PKCS «7» также позволяет получить контент с цифровой подписью в дополнение к применению формирования отдельного зашифрованного и отдельного подписанного контента.

Функция CryptSignAndEncryptionMessage в криптографическом API предоставляет такую операцию:

Параметры составных частей функции CryptSignAndEncryptionMessage идентичны, поскольку вся функция состоит из последовательного применения функций для создания цифровой подписи и функции шифрования.

Ниже показано, как использовать эту функцию:

HCRYPTPROV hProv;
HCERTSTORE  hSystemStore; 

if (!(hSystemStore  =  CertOpenSystemStore(NULL, "MY")))
{
  Error("CertOpenSystemStore");
  return;
}

std::cout << "System store is opened" << std::endl;

PCCERT_CONTEXT pCert = NULL;

while (pCert = CertEnumCertificatesInStore(hSystemStore, pCert))
{
  if(!strcmp(pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 
    "1.2.840.113549.1.1.1"))
  {
    DWORD dwKeySpec;
    if (CryptAcquireCertificatePrivateKey(pCert, 0, NULL, &hProv, 
       &dwKeySpec, NULL))
      break;
  }
}

std::cout << "Certificate found" << std::endl;


CRYPT_SIGN_MESSAGE_PARA SignPara;
ZeroMemory(&SignPara, sizeof(SignPara));

SignPara.cbSize = sizeof(SignPara);
SignPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
SignPara.HashAlgorithm.pszObjId = (LPSTR)CertAlgIdToOID(CALG_MD5);
SignPara.pSigningCert = pCert;
SignPara.cMsgCert = 1;
SignPara.rgpMsgCert = &pCert;


CRYPT_ENCRYPT_MESSAGE_PARA EncryptPara;
ZeroMemory(&EncryptPara, sizeof(EncryptPara));

EncryptPara.cbSize = sizeof(EncryptPara);
EncryptPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
EncryptPara.ContentEncryptionAlgorithm.pszObjId = 
  (LPSTR)CertAlgIdToOID(CALG_RC4);
EncryptPara.hCryptProv = hProv;

char string[] = "Test";

if (!CryptSignAndEncryptMessage(&SignPara, &EncryptPara, 1, &pCert, 
    (BYTE*)string, strlen(string), NULL, count))
{
  Error("CryptSignAndEncryptMessage");
  return;
}

*encrypted = static_cast<BYTE*>(malloc(*count));

if (!CryptSignAndEncryptMessage(&SignPara, &EncryptPara, 1, &pCert, 
    (BYTE*)string, strlen(string), *encrypted, count))
{
  Error("CryptSignAndEncryptMessage");
  return;
}

std::cout << "Encrypted and signed content is received" << std::endl;

CertFreeCertificateContext(pCert);
CertCloseStore(hSystemStore, 0);
CryptReleaseContext(hProv, 0);

Строение и возможности crypto api

Разнообразие задач для решения Crypto API вкратце можно перечислить следующим образом:

  • Надежное сокрытие данных;
  • Возможность передачи скрытых данных третьим лицам;
  • Надежная проверка информации, полученной от третьих лиц;
  • Расшифровка полученных конфиденциальных данных;
  • Работа с «идентификационными сертификатами» для третьих лиц;
  • Обеспечение работы с признанными криптографическими стандартами;
  • Расширяемость и работа с пока неизвестными алгоритмами.

Я попробую сначала подумать о решении проблемы расширения. Crypto API предоставил очень элегантное решение этой проблемы. Реализация Crypto API происходит полностью за пределами собственно Crypto API в отдельных, независимых динамических библиотеках (CSP).

Crypto API предоставляет пользователю унифицированный интерфейс для работы с CSP, не навязывая никаких требований к набору функций крипто-провайдера. Вы должны знать об названии и номере телефона поставщика крипто -провайдера, чтобы полностью использовать все его функции.

Способность четко идентифицировать сторону передачи/приема в протоколе передачи данных, в дополнение к задаче расширения, является основной функцией крипто -API. Использование механизма сертификата является общепринятым решением в этой ситуации.

Сертификаты превращаются в «цифровые паспорта», которые содержат персональные данные о своих владельцах. В последующих параграфах я остановлюсь лишь на некоторых функциях, которые он реализует из Crypto API. Сертификаты часто используются системными функциями Crypto API.

Рано или поздно в программных решениях встает вопрос о стандартизации данных для передачи между приложениями. Набор стандартов PKCS, разработанный компанией RSA Security, уже давно и успешно используется в области криптографии. В этом наборе рекомендаций рассматриваются все сценарии, которые могут возникнуть в криптографическом приложении.

Существуют стандарты для обмена подписанными и зашифрованными данными, а также сертификатами. Основная библиотека для работы с криптографическими данными в Windows называется Crypto API.

Для категоризации всего интерфейса Crypto API можно использовать пять функциональных категорий

1. Базовые криптографические функции

  • Функции для шифрования/дешифрования данных;
  • Функции для хэширования и цифровой подписи данных;
  • Функции для инициализации криптовалют и работы с полученным контекстом;
  • Функции для генерации ключа;
  • Функции для обмена ключами.

2. операции кодирования и декодирования. В данном контексте кодирование означает получение данных на выходе, записанных в формате ASN.1 (Abstract Syntax Notation One).

3. Особенности работы с сертификатами.

4. Криптографические сообщения высокого уровня.

5. Криптографические функции для обработки низкоуровневых зашифрованных сообщений.

Тем, кто хочет помочь

Буду благодарен за расширение/улучшение/доработку API.
Вам будут полезны примеры,
предоставляемые Крипто ПРО.

Установка

Для NPM:

npm install crypto-pro-js

Для Yarn:

Для Bower:

bower install webmasterskaya/crypto-pro-js

Как установить плагин UMD через eScript

Подключение пакета как ES модуля с Typescript или JavaScript:

Список необходимых полифил (при необходимости подключите себя)

  • Промеса
  • Массив.prototype.find

Хеширование

Применение математической функции, также известной как хэш -функция, к некоторым данным называется хэшинг. Любой объем данных, подчитанных хеш -функции, всегда дает множество независимых единиц. Существуют требования к «сопротивлению столкновения» для значения хэша.

Это означает, что чем сложнее найти два случайных входных массива с одинаковыми значениями, тем лучше и более эффективная функция хэш. Хеш -функция должна давать тот же результат при работе с теми же данными. Основная цель этого свойства — регулировать целостность данных.

Crypto API модифицирует хэш с помощью уникального объекта. Эти три функции могут быть использованы для манипулирования этим объектом:

  • CryptCreateHash;
  • CryptHashData;
  • CryptGetHashParam.

Функция CryptCreateHash используется для начальной инициализации хэш-объекта. Следующая фраза описывает эту функцию:

Первый параметр функции передается криптопровайдеру. Второй параметр указывает алгоритм получения значения хеша. В данном случае параметр hKey не нужен.

СОВЕТ

M AC, что в переводе на русский означает «код аутентификации сообщений», — это другое название «имитовставки». Согласно конструкции MAC, только тот, кто имеет необходимый ключ, может проверить хэш-значения с помощью MAC. Хотя это не совсем так, его можно считать дополнительным шифром, вытекающим из хэш-функции. Базовая цифровая подпись может быть создана с помощью MAC. Одним из видов MAC является HMAC (Hash-based Message Authentication Code).

Требуемое значение для зарезервированного параметра DWFLAGS составляет 0. Функция возвращает дескриптор объекта, который он создал с помощью параметра PHHASH. Вы должны уничтожить хэш -объект, вызывая функцию CryptDestRoyHash, как только он больше не требуется, и больше не выполняя свою предполагаемую функцию.

Используя вызовы Crypthashdata, вы можете начать отправку данных после инициализации хэша. Следующая фраза описывает эту функцию:

Первый параметр данной функции передается ранее инициализированному хендлу объекта. Второй параметр передается в виде порции данных для хеш-функций. The length of the data being sent is indicated by the dwDataLen metric. The dwFlags parameter is usually close to zero.

Функции Crypthashdata необходимо получить значение хэш-функции после полного завершения передачи всего массива входных данных. Функция Cryptogethap использует формулу Cryptgethashparam для решения этой проблемы. Следующая фраза описывает эту функцию:

Первым параметром этой функции является хэндл объекта, который был инициализирован ранее. Второй параметр, DWPARAM, определяет тип запрашиваемого значения. Второй аргумент должен содержать значение hp_hashval для получения хэша.

Получение значения данных и сравнение его с проверенным необходимо для подтверждения точности хэша.

Пример получения хеш-значения представлен ниже:

HCRYPTPROV hProv;
HCRYPTHASH hHash;

if(!CryptAcquireContext(&hProv, 
"{EB57ED8A-CCCC-4bf5-8659-9DF2F05F24AD}", NULL, PROV_RSA_FULL, 0))
{
  Error("CryptAcquireContext");
  return;
}

std::cout << "Cryptographic provider initialized" << std::endl;

if(!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
{
  Error("CryptCreateHash");
  return;
}

std::cout << "Hash created" << std::endl;

char string[] = "Test";
DWORD count = strlen(string);

if(!CryptHashData(hHash, (BYTE*)string, count, 0))
{
  Error("CryptHashData");
  return;
}

std::cout << "Hash data loaded" << std::endl;


count = 0;

if(!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &count, 0))
{
  Error("CryptGetHashParam");
  return;
}

char* hash_value = static_cast<char*>(malloc(count   1));
ZeroMemory(hash_value, count   1);

if(!CryptGetHashParam(hHash, HP_HASHVAL, (BYTE*)hash_value, &count, 0))
{
  Error("CryptGetHashParam");
  return;
}

std::cout << "Hash value is received" << std::endl;


std::cout << "Hash value: " << hash_value << std::endl;

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

Стандарт PKCS предлагает два типа подписей: отделенная подпись, которая объединяется с подписываемыми данными, и отдельная подпись, в отличие от простейшего случая формирования цифровой подписи. Ниже описана функция CryptSignMessage, которая объединяет оба типа подписей в одну подпись вместе с текстом:

Указатель на структуру типа CRBPT_SIGN_MESSAGE служит первым входом этой функции. Информация о сертификате, который будет использоваться для подписи абонента, содержится в полях этой структуры.

ПРЕДУПРЕЖДЕНИЕ

Если флаг fdetachedSignature установлен в Faller, параметр CtoBesigned всегда 1. В частности, это связано с особенностями реализации функций криптографических сообщений низкого уровня и их функций позже.

Массив данных для создания цифровой подписи передается через параметр RGPBTOBESGNED. Массив размеров элементов, передаваемых в файлы тома, составляет параметр RGCBTOBESGNED. Цифровая подпись создается с помощью параметров PBSignedbloob и IPCB Signed Blob.

Ниже приведена иллюстрация того, как эта функция может быть использована:

HCERTSTORE  hSystemStore; 

if (!(hSystemStore = CertOpenSystemStore(NULL, "MY")))
{
  Error("CertOpenSystemStore");
  return;
}

std::cout << "System store is opened" << std::endl;

PCCERT_CONTEXT pCert = NULL;

while (pCert = CertEnumCertificatesInStore(hSystemStore, pCert))
{
  if(!strcmp(pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 
    "1.2.840.113549.1.1.1"))
  {
    DWORD dwKeySpec;
    HCRYPTPROV hProv;
    if(CryptAcquireCertificatePrivateKey(pCert, 0, NULL, &hProv,
 &dwKeySpec, NULL))
    {
      CryptReleaseContext(hProv, 0);
      break;
    }
  }
}

std::cout << "Certificate found" << std::endl;


CRYPT_SIGN_MESSAGE_PARA SignPara;
ZeroMemory(&SignPara, sizeof(SignPara));

SignPara.cbSize = sizeof(SignPara);
SignPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
SignPara.HashAlgorithm.pszObjId = (LPSTR)CertAlgIdToOID(CALG_MD5);
SignPara.pSigningCert = pCert;
SignPara.cMsgCert = 1;
SignPara.rgpMsgCert = &pCert;

char string[] = "Test";

const BYTE* DataArray[] = { (BYTE*)string };
DWORD SizeArray[] = { strlen(string) };

*count = 0;

if(!CryptSignMessage(&SignPara, true, 1, DataArray, SizeArray, 
NULL, count))
{
  Error("CryptSignMessage");
  return;
}

*signature = static_cast<BYTE*>(malloc(*count));

if(!CryptSignMessage(&SignPara, true, 1, DataArray, SizeArray, 
*signature, count))
{
  Error("CryptSignMessage");
  return;
}

std::cout << "Signature content is received" << std::endl;

Шифрование

Функция CryptEncryptionMessage используется для шифрования сообщений. Следующее предложение описывает эту функцию:

В качестве первого параметра функции передается указатель на структуру CR0PT_ENCERDAL. Поля структуры содержат сведения о криптопровайдере, используемом для шифрования, применяемых алгоритмах и вспомогательных данных.

СОВЕТ

Эти функции экспортируют сеансовый ключ, созданный во время шифрования с помощью самих сертификатов. Вполне вероятно, что сами сертификаты содержат открытые ключи получателей. Если получателей данных несколько, то для каждого из них создается индивидуальный массив экспортируемых ключей. Еще одной особенностью сертификатов является то, что каждый из них имеет специальный идентификационный номер. Этот номер добавляется к массиву экспортированных сеансовых ключей, который получается в результате, и на стороне получателя можно найти элемент этого массива, необходимый для успешного импорта.

Обработка данных запускается параметрами Pbtobeencrypted и SBTBENCRIPRIGIPINISE. В результате, используя параметры Pbencryptedblob и PCBENCRYPTED BLOB, мы соответственно возвращаем содержимое файла.

Ниже приведен пример того, как использовать эту функцию:

HCERTSTORE  hSystemStore; 
HCRYPTPROV hProv;

if (!(hSystemStore = CertOpenSystemStore(NULL, "MY")))
{
  Error("CertOpenSystemStore");
  return;
}

std::cout << "System store is opened" << std::endl;

PCCERT_CONTEXT pCert = NULL;

while (pCert = CertEnumCertificatesInStore(hSystemStore, pCert))
{
  if (!strcmp(pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, 
    "1.2.840.113549.1.1.1"))
  {
    DWORD dwKeySpec;
    if(CryptAcquireCertificatePrivateKey(pCert, 0, NULL, &hProv,
 &dwKeySpec, NULL))
      break;
  }
}

std::cout << "Certificate found" << std::endl;


CRYPT_ENCRYPT_MESSAGE_PARA EncryptPara;
ZeroMemory(&EncryptPara, sizeof(EncryptPara));

EncryptPara.cbSize = sizeof(EncryptPara);
EncryptPara.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
EncryptPara.ContentEncryptionAlgorithm.pszObjId = 
  (LPSTR)CertAlgIdToOID(CALG_RC4);
EncryptPara.hCryptProv = hProv;

char string[] = "Test"; 

if(!CryptEncryptMessage(&EncryptPara, 1, &pCert, (BYTE*)string,
 strlen(string), NULL, count))
{
  Error("CryptEncryptMessage");
  return;
}

*encrypted = static_cast<BYTE*>(malloc(*count));

if(!CryptEncryptMessage(&EncryptPara, 1, &pCert, (BYTE*)string,
 strlen(string), *encrypted, count))
{
  Error("CryptEncryptMessage");
  return;
}

std::cout << "Encryption completed" << std::endl;

CertCloseStore(hSystemStore, 0);
CryptReleaseContext(hProv, 0);

Экспорт сессионных ключей

После процесса шифрования встает вопрос о передаче недостоверных данных. Сами данные могут передаваться сами по себе, поскольку они защищены. Но следует помнить, что Crypto API использует симметричные алгоритмы шифрования, которые затрудняют декодирование данных, если принимающая сторона не имеет общего сеансового ключа.

Фундаментальная ключевая функция экспорта объясняется следующим образом:

Ключ Хендл, который будет экспортирован, является первым входным параметром. Цифровой экспорт ключа может быть визуализирован как отдельная операция для хранения ключей. Следовательно, для такой операции требуется другой ключ шифрования. Ключ сеанса в Crypto API обычно шифруется асимметрично.

Параметр hExpKey инициализирует контекст открытого ключа получателя. Параметр dwBlobType задает формат получаемого блока экспорта. Возможно, что будет экспортирован только открытый ключ.

В данном случае параметр HEXPKEY установлен в 0 (шифрование открытым ключом не требуется), и функция возвращает на выходе прямое значение официального открытого ключа. Для этого параметру DWBLOBTYPE требуется Pubilickea Bloba.

Обычно же, при экспорте сессионного ключа используется значение SIMPLEBLOB. Остальные значения данного параметра достаточно специфичны и применяются редко. Параметры pbData и pdwDataLen указывают на массив, выделенный для получения экспортируемого ключа, и на его размер.

СОВЕТ

В связи с этим я хотел бы обратить внимание читателя в один чрезвычайно важный момент: иногда для обмена ключами используются более сложные схемы, чем простое шифрование данных с помощью открытого ключа. Алгоритм обмена ключами с использованием метода Diffe Hellman является иллюстрацией такой сложной схемы обмена ключами. Глубокая литература по алгоритму можно найти там для более подробной информации.

Ниже приведена иллюстрация использования этой функции:

HCRYPTPROV hProv;
HCRYPTKEY hKey, hPublicKey, hNewKey;

ключейif (!CryptAcquireContext(&hProv, "{EB57ED8A-CCCC-4bf5-8659-9DF2F05F24AD}",
 NULL, PROV_RSA_FULL, 0))
  return;

std::cout << "Cryptographic provider initialized" << std::endl;

if (!CryptGenKey(hProv, CALG_RC4, 
CRYPT_EXPORTABLE | CRYPT_ENCRYPT | CRYPT_DECRYPT, &hKey))
  return;

std::cout << "Session key generated" << std::endl;

if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hPublicKey))
  return;

std::cout << "Public key is received" << std::endl;

count = 0;

if (!CryptExportKey(hKey, hPublicKey, SIMPLEBLOB, 0, NULL, &count)) 
  return;


BYTE* data = static_cast<BYTE*>(malloc(count)); 
ZeroMemory(data, count);

if (!CryptExportKey(hKey, hPublicKey, SIMPLEBLOB, 0, data, &count)) 
  return;

std::cout << "Key's export completed" << std::endl;

Метод getextendedkeyusage

Получение информации из ключа
Получение OID сертификата (улучшенного ключа)

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