Closed ekx77 closed 2 years ago
Сейчас при установке ключа пользуемся самописным методом SetCspPrivateKey
https://github.com/CryptoPro/corefx/blob/c704e18e502d41e9ffb624e4aaf362180f7f7cd8/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.PrivateKey.Win.cs#L73-L91
Сделан он по аналогии с методом копирования ключа от ms CopyWithPersistedCapiKey
https://github.com/CryptoPro/corefx/blob/c704e18e502d41e9ffb624e4aaf362180f7f7cd8/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.PrivateKey.Win.cs#L182-L196
Который является переносом аналогичного метода из .NET Framework https://referencesource.microsoft.com/#System/security/system/security/cryptography/x509/x509certificate2.cs,c99bf10e3e4c5c5e
Почему они переносят только флаг расположения ключа - хороший вопрос.
Более того, при получении ключа из сертификата тоже будет вариться объект исключительно смотря на флаг MachineKeyStore
https://github.com/CryptoPro/corefx/blob/c704e18e502d41e9ffb624e4aaf362180f7f7cd8/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.PrivateKey.Win.cs#L126-L132
Так с ходу ломать внутренние интерфейсы не возьмёмся пока что. Если что то выясниться дополнительно - отпишусь.
Получить флаги из CspKeyContainerInfo
можно через reflection, поле _parameters
типа CspParameters
. Аналогичное поле можно достать и из CspKeyContainerInfo. Свойство всегда было закрытым и в старом фремворке, открывать пока не планируем.
C рефлексией связываться очень бы не хотелось, лучше метод или свойство, хотя бы для Gost* классов.
Применительно к описанному сценарию вряд ли от использования CspParameters будет толк.
// поиск сертификата со ссылкой в хранилище, например установленным туда средствами УЦ 2.0
X509Certificate2 cert = ...
// При чтении ключа вызывается метод GetGost3410_2012_256PrivateKey,
// который перевызывает метод GetPrivateKey<T>
// и далее GetPrivateKeyCsp, который, как указано выше, игнорирует все флаги кроме CRYPT_MACHINE_KEYSET
// и заполняет cspParameters. Далее на основе данных параметров открывается
// контекст провайдера и получается ключ.
var key = cert.PrivateKey as Gost3410_2012_256CryptoServiceProvider;
var containerInfo = key.CspKeyContainerInfo;
var cert = new X509Certificate2("test.cer");
var p = new CspParameters(provType, provName, containerName)
{
KeyNumber = 1,
Flags = CspProviderFlags.UseMachineKeyStore | CspProviderFlags.NoPrompt,
};
var key = new Gost3410_2012_256CryptoServiceProvider(p);
// Логика при выставлении ключа - копируем параметры, из флагов копирует только CRYPT_MACHINE_KEYSET
// (см выше). Заполняем и запоминаем cspParameters.
cert.PrivateKey = key;
// если предположить, что реализовано свойство чтения\записи у `CspParameters Parameters` объекта
// Gost3410_2012_256CryptoServiceProvider, то при обращении к cert.PrivateKey произойдёт его
// инициализация на основе существующих параметров (см комментарии ранее) параметров.
// после инициализации менять параметры дело бесполезное, так как ключ и провайдер уже
// проинициализированы. Поэтому поля CspParameters закрыты или read-only, так как из изменение
// не вызывает переинициализацию провайдеров.
Иными словами, при чтении вы этот параметр не получите, так как он не выставляется, а при записи не сможете использовать, так как криптопровайдер и ключ уже инициализированы.
ps. Если нужно поработать с сертификатом pfx с ключом без окошек и созданием контейнеров можно экспериментально воспользоваться новым флагом X509KeyStorageFlags.CspNoPersistKeySet
в конструкторе x509.
Пример использования
using (var certificate = new X509Certificate2(
path,
"1",
X509KeyStorageFlags.CspNoPersistKeySet))
{
}
Менять CspParameters
у существующего PrivateKey
или Gost*Provider
не требуется.
Возможный вариант при чтении:
1) В GetPrivateKeyCsp
у CRYPT_KEY_PROV_INFO
флаг CRYPT_SILENT
не игнорировать, а записывать в CspParameters
, который используется для создания Gost*Provider
.
2) У классов Gost*Provider
сделать getter, который бы возвращал копию CspParameters
. переданного в конструкторе.
Возможный вариант при записи:
1) Создавать Gost*Provider
по CspParameters
в конструкторе.
2) В SetCspPrivateKey
учитывать NoPrompt
при заполнении CRYPT_KEY_PROV_INFO
.
По опыту, CRYPT_SILENT в ссылке на ключ - причина многих необъяснимых проблем у пользователей. Вероятно поэтом, MSFT не стал переносить этот флаг из ссылки на ключ в объект криптопровайдера. Соответсвенно и мы не будем так делать по умолчанию.
При чтении/установке X509Certificate2::PrivateKey теряется флаг CRYPT_SILENT, который может присутствовать в CRYPT_KEY_PROV_INFO::dwFlags. Примерный сценарий для чтения:
Примерный сценарий для записи: