Подходит ли CAPICOM для шифрования с открытым ключом, используя КриптоПро CSP? Если да, то можно ли найти примеры?
На нашем сайте есть ссылка на страницу Микрософт, где лежит CAPICOM. В состав дистрибутива входят примеры использования. Для шифрования нужно пользовать метод Enveloped. Метод Encrypted работать с ГОСТом не будет, т.к. шифруется на пароле, алгоритмы шифрования перечислены. Eveloped аналогичен CryptEncryptMessage из CryptoAPI и на выходе формирует формат PKCS#7 Enveloped.
Отсутствие сертификата подписчика в подписанном сообщении
CAPICOM в первую очередь ориентирован на работу с сертификатами и всем, что с ними связано (хранилища, списки отзыва), а не с ключами или ключевыми контейнерами. Поэтому и класс CAPICOM.SignedData спроектирован и функционирует так, что сертификат подписчика всегда ищется в подписанном сообщении. При его отсутствии выдается соответствующая ошибка «The signer is not found in the signed message (0x80880251)». Никак обойти это ограничение нельзя – SignedData не принимает в качестве параметров сертификат (а тем более коллекцию сертификатов или хранилище), который нужно использовать для проверки. Самое простое решение – добавлять сертификат на этапе формирования подписи. CAPICOM сам всегда так поступает, а в CryptoAPI несложно передать сертификат в качестве параметра подписывающей функции.
Подпись текста в ANSI кодировке
Строковые параметры методов COM объектов, работающих через IDispatch, являются, по сути своей, юникодными строками – на каждый символ строки приходится 2 байта памяти. Поэтому, при передаче параметры явно или неявно преобразовываются к такому виду. Если в системе только одна сторона использует CAPICOM а вторая использует, например, CryptoAPI – то может возникнуть проблема несовпадения исходного документа для подписи и проверки.
Пример – одна сторона подписывает файл размером 3 байта, содержащий строку «123», с помощью ПО, написанного на Visual Basic 6 с использованием CAPICOM и отправляет его для проверки с неприсоединенной подписью. Вторая сторона для проверки использует ПО, написанное на C++ с использованием CryptoAPI (например, утилиту коммандной строки) и при проверке подписи получает ошибку NTE_BAD_SIGNATURE (0x80090006), хотя исходный документ не менялся. Причина ошибки в том, что в VB при чтении файла а затем при передаче параметров строка «123» была преобразована к юникоду, т.е. реально было подписано 6 байт «00 31 00 32 00 33», а проверено – 3 «31 32 33». Локализовать эту проблему можно, создав присоединенную подпись и проверив ее с помощью утилиты csptest (входит в состав КриптоПро CSP) – csptest –sfsign –verify –in <файл с подписанным сообщением> -out data.txt –base64 и затем сравнив содержимое полученного файла data.txt с исходным документом.
Решение этой проблемы – при необходимости в языках, которые делают неявное преобразование (как VB, .NET) всегда преобразовывать подписываемый и проверяемый текст к ANSI, если в системе строго не оговорено обратное. В VB для этого можно использовать функцию StrConv или написать свою для побайтного преобразования с использованием метода ByteArrayToBinaryString класса Utilities библиотеки CAPICOM. В .NET подобное преобразование сделать сложнее, и для этого не подойдёт метод ByteArrayToBinaryString, поскольку возвращаемое этим методом значение будет преобразовано в .NET-тип System.String, заточенный под юникодные строки. Ошибка преобразования возникнет, если в исходном массиве содержится нечётное число байт. Возможным решением проблемы является написание специального маршалера для передачи таких строк из .NET в CAPICOM и обратно. Пример интероп-ассембли для CAPICOM, в которой реализован такой маршалер, а в интерфейсе методов, подверженных описанной ошибке преобразования, тип System.String заменён на System.Byte[], можно взять здесь (Interop.CAPICOM.dll). Для использования этой интероп-ассембли необходимо в проекте явно сослаться на неё вместо CAPICOM.