CryptoPro / corefx

This repo contains the .NET Core foundational libraries, called CoreFX. It includes classes for collections, file systems, console, XML, async and many others. We welcome contributions.
https://github.com/dotnet/core
MIT License
27 stars 7 forks source link

Кириллица в именах контейнеров (+cms) #60

Closed emejibka closed 1 year ago

emejibka commented 1 year ago

Здравствуйте. При вычислении подписи на некоторых сертификатах получаем ошибку

   at Internal.NativeCrypto.CapiHelper.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
   at System.Security.Cryptography.Gost3410_2012_256CryptoServiceProvider.get_SafeProvHandle()
   at System.Security.Cryptography.Gost3410_2012_256CryptoServiceProvider.get_SafeKeyHandle()
   at System.Security.Cryptography.Gost3410_2012_256CryptoServiceProvider.GetKeyPair()
   at System.Security.Cryptography.Gost3410_2012_256CryptoServiceProvider..ctor(CspParameters parameters)
   at Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKey[T](X509Certificate2 certificate, Boolean silent, Boolean preferNCrypt)
   at Internal.Cryptography.Pal.Windows.PkcsPalWindows.GetPrivateKeyForSigning[T](X509Certificate2 certificate, Boolean silent)
   at System.Security.Cryptography.Pkcs.CmsSignature.Gost2012_256CmsSignature.Sign(ReadOnlySpan`1 dataHash, HashAlgorithmName hashAlgorithmName, X509Certificate2 certificate, AsymmetricAlgorithm key, Boolean silent, Oid& signatureAlgorithm, Byte[]& signatureValue)
   at System.Security.Cryptography.Pkcs.CmsSignature.Sign(ReadOnlySpan`1 dataHash, HashAlgorithmName hashAlgorithmName, X509Certificate2 certificate, AsymmetricAlgorithm key, Boolean silent, Oid& oid, ReadOnlyMemory`1& signatureValue)
   at System.Security.Cryptography.Pkcs.CmsSigner.Sign(ReadOnlyMemory`1 data, String contentTypeOid, Boolean silent, X509Certificate2Collection& chainCerts)
   at System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer, Boolean silent)
...

код для вычисления подписи

                var contentInfo = new ContentInfo(rawData);
                var signedCms = new SignedCms(contentInfo, true);
                var cmsSigner = new CmsSigner(gostCert);
                cmsSigner.SignedAttributes.Add(new Pkcs9SigningTime(DateTime.Now));
                signedCms.ComputeSignature(cmsSigner, true);

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

Версия криптопро - 5.0.12000 Сборка corefx из коммита задачи - https://github.com/CryptoPro/corefx/issues/53

Fasjeit commented 1 year ago

Сможете приложить сертификат с ключом, на котором воспроизводится ошибка?

Fasjeit commented 1 year ago

Так как ошибка при открытии ключа - возможные проблемы - нет доступа (прав) к закрытому ключа для пользователя, истёк срок действия закрытого ключа. Проверить это можно на клиенте через утилиту csptest (протестировать соответствующий контейнер закрытого ключа).

emejibka commented 1 year ago

Могу, но не публично, Напишите почту.

Проверить это можно на клиенте через утилиту csptest (протестировать соответствующий контейнер закрытого ключа).

Проверяли, ошибок нет, сейчас приложу вывод.

emejibka commented 1 year ago
/opt/cprocsp/bin/amd64/certmgr -list -thumbprint bd545df6eacf88bcb14365ac08948c6e65bdf011
Certmgr 1.1 (c) "Crypto-Pro", 2007-2021.
Program for managing certificates, CRLs and stores.
=============================================================================
1-------
Issuer              : E=SupportIIT@infotecs.ru, STREET="ул. Мишина, д. 56, стр. 2, эт. 2, пом. IX, ком. 11", C=RU, S=77 г. Москва, L=Москва, O="Акционерное Общество ""ИнфоТеКС Интернет Траст""", OGRN=1027739113049, INNLE=7743020560, CN="АО ""ИИТ"""
Subject             : E=***, C=RU, S=***, L=***, G=***, SN=***, SNILS=***, INN=***, CN=***
Serial              : 0x01D89758DB5EA9C0000A9E9A00060002
SHA1 Thumbprint     : bd545df6eacf88bcb14365ac08948c6e65bdf011
SubjKeyID           : 438dca4528108897794b1508858fbc6832cc8ea9
Signature Algorithm : ГОСТ Р 34.11-2012/34.10-2012 256 бит
PublicKey Algorithm : ГОСТ Р 34.10-2012 256 бит (512 bits)
Not valid before    : 14/07/2022  08:08:08 UTC
Not valid after     : 14/07/2023  08:08:08 UTC
PrivateKey Link     : Yes
Container           : HDIMAGE\\wqaqgvda.000\612C
Provider Name       : Crypto-Pro GOST R 34.10-2012 Cryptographic Service Provider
Provider Info       : Provider Type: 80, Key Spec: 1, Flags: 0x0
Identification Kind : Personal presence
OCSP URL            : http://cades.iitrust.ru:8777/ocsp
OCSP URL            : http://193.162.30.68:8777/ocsp
CA cert URL         : http://uc1.iitrust.ru/uc/CA-IIT-(K3)-2022.cer
CA cert URL         : http://uc2.iitrust.ru/uc/CA-IIT-(K3)-2022.cer
CA cert URL         : http://193.162.30.86/uc/CA-IIT-(K3)-2022.cer
CA cert URL         : http://193.162.30.76/uc/CA-IIT-(K3)-2022.cer
CDP                 : http://uc1.iitrust.ru/uc/CA-IIT-(K3)-2022.crl
CDP                 : http://uc2.iitrust.ru/uc/CA-IIT-(K3)-2022.crl
CDP                 : http://193.162.30.86/uc/CA-IIT-(K3)-2022.crl
CDP                 : http://193.162.30.76/uc/CA-IIT-(K3)-2022.crl
CDP                 : https://uc1.iitrust.ru/uc/CA-IIT-(K3)-2022.crl
CDP                 : https://uc2.iitrust.ru/uc/CA-IIT-(K3)-2022.crl
Extended Key Usage  : 1.3.6.1.5.5.7.3.2 Проверка подлинности клиента
                      1.3.6.1.5.5.7.3.4 Защищенная электронная почта
=============================================================================

[ErrorCode: 0x00000000]

/opt/cprocsp/bin/amd64/csptest -keyset -check -cont 'HDIMAGE\\wqaqgvda.000\612C'
CSP (Type:80) v5.0.10008 KC1 Release Ver:5.0.12000 OS:Linux CPU:AMD64 FastCode:READY:AVX.
AcquireContext: OK. HCRYPTPROV: 9163283
GetProvParam(PP_NAME): Crypto-Pro GOST R 34.10-2012 KC1 CSP
Container name: "***"
Check header passed.
Signature key is not available.
Exchange key is available. HCRYPTKEY: 0x8d6ac3
Symmetric key is not available.
UEC key is not available.
License: Cert without license
Check container passed.
Check sign passed.
Check verify signature on private key passed.
Check verify signature on public key passed.
Check import passed.
Certificate in container matches AT_KEYEXCHANGE key.
Keys in container:
  exchange key
Extensions:
  OID: 1.2.643.2.2.37.3.10
  PrivKey: Not specified - 13.01.2024 11:37:17 (UTC)
Total: SYS: 0.000 sec USR: 0.010 sec UTC: 0.080 sec
[ErrorCode: 0x00000000]
emejibka commented 1 year ago

Отправил, пароль на архив C3S9kbvHYTvidjAZC8gc

emejibka commented 1 year ago

Что-нибудь ещё нужно?

Fasjeit commented 1 year ago

Пока нет.

Fasjeit commented 1 year ago

Проблема в кириллической кодировке (1251) имени контейнера. При подписи получаем имя контейнера через CryptGetProvParam, получаем массив байт в 1251, но читаем его как ASCII, получаем кракозябры, пытаемся открыть контейнер с кривым именем - создаём новый контейнер вместо обращения к существующему - падаем с ошибкой.

Проблема только на Linux.

Место - GetStringProvParam

Fasjeit commented 1 year ago

@emejibka Проблему воспроизвели и локализовали, спасибо. В ближайшее время постараемся поправить. За статусом можно следить тут.

emejibka commented 1 year ago

Имя контейнера можно задать при установке сертификата?

Fasjeit commented 1 year ago

В зависимости от того, как устанавливается сертификат.

Как я понимаю у вас второй вариант (контейнер + сертификат в контейнере).

Если установка сертификата из контейнера - имя контейнера хранится в name.key в формате asn1. Можно записать туда имя без кириллических символов - тогда работа с контейнером будет корректной (не забыть поменять длину в asn1 теге),

Или просто удалить name.key перед установкой контейнера, тогда имя контейнера будут соответствовать имени папки.

Т.е. срочный "грязный фикс" для клиента будет таким - сохранить файлы контейнера, удалить контейнер через csptest, удалить файл name.key, установить контейнер с сертификат из него.

emejibka commented 1 year ago

Как я понимаю у вас второй вариант (контейнер + сертификат в контейнере). Не, на клиенте делаем экспорт сертификата в pfx, на сервере импортируем его с помощью команды certmgr -install -pfx -file "путь к файлу" -silent -pin "пароль"

Fasjeit commented 1 year ago

Через certmgr, судя по всему, не задаётся имя контейнера, а берётся из серта... Тогда остаётся такой вариант.

emejibka commented 1 year ago

Уже можно попробовать тестовую сборку или ещё рано?

Fasjeit commented 1 year ago

Да, можно пробовать сборку https://ci.appveyor.com/project/CryptoPro/corefx/builds/45372589

N.B. В текущей версии используется рантайм 3.1.30. Возможно придётся установить соотвествующий рантайм и SDK.

emejibka commented 1 year ago

Спасибо, с обновлением ошибка не воспроизводится

Fasjeit commented 1 year ago

Windows работает и так, понимает, что там 1251.

Для Linux проблема была в том, что csp возвращала 1251, но дотнет работал с ASCII. В результате получали кракозябры + испорченные байте (т.к. ascii заменял непечатные символы). UTF8, UTF16, UTF7 - давали аналогичный результата. Решение - по умолчанию используем на Linux кодировку Latin1, которая умеет декодировать любой массив байт. В результате хотя бы имеем возможность открыть контейнер, имя которого получено из сертификата с использованием GetProvParam (да, в cspParams будут кракозябры, но CryptAcqureContext на вход получит нужный массив байт, так как Latin1 не портит байты).

Если же на Linux уже зарегистрирована кодировка 1251 - используем её (делается отдельно через пакет System.Text.Encoding.CodePages с последующим вызовом Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)), после чего в cspParams будет попадать корректная строка. (Это же надо использовать, если кто то захочет руками открыть кириллический контейнер)

Статической проперти Encoding.Latin1 в "сборочном" рантайме по каким то причинам нет, но можно пользоваться Latin1 через Encoding.GetEncoding(28591), что и делаем.

Тестов не будет, подробнее в связанном запросе для libcore https://gitlab.cp.ru/cloud/misc/libcore/-/issues/12