Вставка сертификата (с privatekey) в корневом хранилище сертификатов LocalMachine завершается ошибкой in.NET 4

у меня возникли проблемы с вставкой нового сертификата CA с privatekey в корневое хранилище сертификатов localmachine.

вот что происходит:

//This doesn't help either.
new StorePermission (PermissionState.Unrestricted) { Flags = StorePermissionFlags.AddToStore }.Assert();
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
privkey.PersistKeyInCsp = true;
//This shouldn't be necessary doesn't make a difference what so ever.
RSACryptoServiceProvider.UseMachineKeyStore = true;
cert.PrivateKey = privkey;
store.Open (OpenFlags.MaxAllowed);
store.Add (cert);
store.Close ();

сертификат вставляется, и все это выглядит денди: (см!) note it says it has a private key

Примечание: is говорит, что у него есть privatekey.

таким образом, вы скажете, что можно было бы найти его с FindPrivateKey

C:UsersAdministratorDesktop>FindPrivateKey.exe Root LocalMachine -t "54 11 b1 f4 31 99 19 d3 5a f0 5f 01 95 fc aa 6f 71 12 13 eb"
FindPrivateKey failed for the following reason:
Unable to obtain private key file name

Use /? option for help 

это мило .... НО ЭТО НЕПРАВИЛЬНО!! (2 тупых собак ссылка)

и диалоговое окно экспорта сертификата дает мне это очень прекрасное сообщение: alt text

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

Я просто хотел бы знать, почему?

(протестировано на Windows Server 2008 R2 и Windows 7)

будь я проклят!

он работает, когда я компилирую его в v3.5!!!!

Что делать?

6 ответов


у меня была точно такая же проблема и решение оказалось очень простым. Все, что мне нужно было сделать, это пройти

X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet

в конструктор x509certificate2 С по. Теперь вы используете DotNetUtilities для преобразования сертификата bouncycastle в .net, но вспомогательный метод создает сертификат .net с помощью DefaultKeySet (вместо MachineKeySet + PersistKeySet ).

и расположите закрытый ключ следующим образом:

var cspParams = new CspParameters
{
      KeyContainerName = Guid.NewGuid().ToString(),
      KeyNumber = (int)KeyNumber.Exchange,
      Flags = CspProviderFlags.UseMachineKeyStore
};

var rsaProvider = new RSACryptoServiceProvider(cspParams);

надеюсь, это поможет.


мне кажется, что вы должны импортировать ключ немного по-другому. См.http://support.microsoft.com/kb/950090 для примера.

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

оно вам нужно сохранить закрытый ключ на магазине ключа машины, который вы должны по крайней мере защитить ключ для чтение только для некоторых выбранных пользователей, а не для всех. Контейнер ключей-это просто файл в файловой системе (см. файлы в директории "%ALLUSERSPROFILE%\Microsoft\Crypto\Keys"), который имеет дескрипторы безопасности, как и другие файлы в NTFS. Для изменения дескрипторов безопасности файлов можно использовать CspKeyContainerInfo.CryptoKeySecurity собственность и AddAccessRule, RemoveAccessRule и так далее.

обновлено: прежде всего, извините за долгий ответ.

я мог бы разделить ваша программа код в двух частях. В первой части вы создаете самозаверяющий сертификат, который можно использовать в качестве сертификатов CA, и сохраняете его как rootcert.pfx-файл. Во второй части вы импортируете сертификат, но используете RSACryptoServiceProvider заполнено свойствами предыдущего созданного ключа вместо использования rootcert.pfx-файл.

я предлагаю заменить вторую часть вашего кода на более стандартный и простой код: импорт сертификата с закрытым ключом из rootcert.pfx-файл как описано в http://support.microsoft.com/kb/950090. Это работает очень хорошо.

я не использую сам BouncyCastle, поэтому я не мог бы прокомментировать первую часть вашего кода, но в целом то, что вы делаете в коде, вы могли бы сделать и в отношении программы makecert.exe утилита из Windows SDK. Вы можете сделать следующее

MakeCert.exe -pe -ss MY -a sha1 -cy authority -len 2048 -m 120 -r -# 1
             -n "CN=Some Root CA, C=NL, OU=BleedingEdge, ST=Somewhere, L=Somelane"

затем вы можете экспортировать сертификат с закрытым ключом или без него в отношении Оснастки сертификата (для mmc.исполняемый.) В приведенном выше примере я не ограничиваю CA для некоторых специальных EKU, поэтому вы можете использовать его без каких-либо ограничений, но если вам нужны ограничения, вы можете просто добавить дополнительные параметры в программы makecert.exe. Вы также можете использовать MakeCert.exe для создания другого сертификата, который подписан с сертификатом CA. Таким образом, вы можете сделать небольшую PKI в отношении MakeCert.только exe.

мне кажется, что создание сертификат-это действительно отдельная часть кода. Ваша главная проблема-во второй части.

если вы хотите импортировать сертификат CA, вы должны принять во внимание некоторые важные вещи:

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

команду certmgr.EXE-файл -добавить -с ЦС.cer-s-r localMachine AuthRoot

  • вы должны импортировать сертификат CA С помощью закрытого ключа на компьютере один компьютер и только для пользователя, который будет выдавать другие сертификаты (который подпишет новые сертификаты с закрытым ключом CA). Одно использование для импорта сертификата в мой хранилище сертификатов CurrentUser. Так код на компьютере может выглядеть как

следующее:

// import PFX
X509Certificate2 cert = new X509Certificate2 (@"c:\Oleg\rootcert.pfx", "password",
    X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
// save certificate and private key
X509Store storeMy = new X509Store (StoreName.My, StoreLocation.CurrentUser);
storeMy.Open (OpenFlags.ReadWrite);
storeMy.Add (cert);

// get certificate without private key
// one can import certificate from rootcert.cer instead
byte[] certBlobWithoutPrivateKey = cert.Export (X509ContentType.Cert);
// save pure certificate in Root of the local machine
X509Certificate2 certWithoutPrivateKey = new X509Certificate2 (certBlobWithoutPrivateKey);
X509Store storeRoot = new X509Store (StoreName.Root, StoreLocation.LocalMachine);
storeRoot.Open (OpenFlags.ReadWrite);
storeRoot.Add (certWithoutPrivateKey);

код будет работать, если вы измените StoreName.My и StoreLocation.CurrentUser к другим значениям, но я не рекомендую вам это делать.

вообще импорт сертификатов в .NET коде выглядит немного странно и не показывает, что будет сделано под капотом. Windows знает только Ключевые Контейнеры где приватные ключи (быть точно ключевой парой) будут сохранены с уважение CSP и Хранилища Сертификатов где будут сохранены сертификаты (см. http://msdn.microsoft.com/en-us/library/bb204781.aspx о местоположении магазина). Чтобы иметь возможность сохранять информацию о контейнере ключей в хранилище сертификатов, Microsoft представила так называемый Расширенные Свойства Сертификата. Если вы используете в .NET свойства X509Certificate2 как Thumbprint, FriendlyName, HasPrivateKey, Archived и так далее вы работаете с расширенными свойствами сертификат. Поэтому я рекомендую импортировать сертификат CA дважды. Один в Root или AuthRoot без задание CERT_KEY_PROV_INFO_PROP_ID Расширенные Свойства Сертификата и еще раз My магазине С настройка информации о месте контейнера Ключей с закрытым ключом (CERT_KEY_PROV_INFO_PROP_ID). Кроме того, вы можете удалить закрытый ключ сразу после использования, импортировать его только в том случае, если вам действительно нужно его использовать и не держите его навсегда!--32-->. Все это важно для обеспечения большей безопасности.


Я столкнулся с этой проблемой, и кажется, что даже пользователь, с которым вы запускаете инструмент FindPrivateKey не имеет доступа к ключу, и поэтому вы получите сообщение "Невозможно получить имя файла закрытого ключа". Вы можете запустить инструмент как процесс LocalSystem.

подробнее здесь:

http://www.itsolutionbraindumps.com/2011/02/finding-private-key-for-your.html

Динко


новый X509Certificate2 (localPFXPath, inputPass, X509KeyStorageFlags.MachineKeySet & X509KeyStorageFlags.PersistKeySet); с & вместо | работал для меня.


обычно сертификаты в Root не будут иметь закрытого ключа для управления. Вы должны импортировать в мою папку, если вы связываете ключ в веб-запроса. У меня есть исключение TLS/SSl, где у меня есть цепочка клиентских сертификатов. Если вы храните всю цепочку сертификатов в моем магазине, я избавился от этого исключения. Где проблема с учетными записями пользователей. Утилита для хранения сертификатов использует текущую учетную запись пользователя, а фактическое приложение работает на системной учетной записи.


основная проблема заключается в том, что API сертификатов .NET-это просто оболочка вокруг api c++ advapi32 certificate manager, поэтому вы не можете указать все параметры, которые передаются этому api, который фактически отвечает за вставку сертификата в хранилище сертификатов Windows и сохранение ключей. Суть в том, что "UseMachineStore " опция должна быть передана в CspProviderFlags, который в свою очередь передается на капи.CRYPT_MACHINE_KEYSET. Это маленький парень, который определяет, сохраняется ли ключ по-настоящему или нет. Кажется, есть несколько разных причин, по которым эта опция не устанавливается, даже если вы установили X509KeyStorageFlags.PersistKeySet и MachineKeySet и экспорт. Все эти варианты живут только до тех пор, пока глупый ключ остается в C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\ папка. Если CRYPT_MACHINE_KEYSET не устанавливается во время импорта, а затем advapi32 сдувает ключ, как только дескриптор сертификата удаляется GC.

решение: добавьте сертификат в доверенный корень перед импортом сертификата в хранилище персональных компьютеров. при чтении журналов из CAPI2, я фактически вижу два вызова " объектов X509 "каждый раз, когда сертификат"импортируется". Всегда есть <Flags value="20" CRYPT_MACHINE_KEYSET="true"/>, (что мы хотим), но другой не делает, если "проверить цепную политику" не возвращает нет ошибки. Так это выглядит advapi32 проверяет "действительность" сертификата и либо возвращает исключение, которое поглощается X509Certificate2 (мне нравится, сколько пустых блоков catch они имеют в этом коде) или advapi32 просто в одностороннем порядке решает не сохранять ключи для недоверенных сертификатов. (Кстати, я подозреваю, что это изменение поведения между 2008 и 20012 годами, но я этого не доказал.) Чтобы обойти это, я добавил If-check в свой код, чтобы добавить сертификат, который, если эмитент равен субъекту (это самозаверяющий сертификат), добавьте сертификат в корень, прежде чем добавлять его в My.

    if (certificate.Issuer.Equals(certificate.Subject))
    {
        using (X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine)) { 
            store.Open(OpenFlags.ReadWrite);
            store.Add(certificate);
            store.Close();
        }
    }

    using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine)){ 
        store.Open(OpenFlags.ReadWrite);
        store.Add(certificate);
        store.Close();
    }

Примечание: я обнаружил, что это излишне, если использовать сертификат, который делает не есть идентификатор ключа субъекта уже в нем. Каким-то образом, когда вы запускаете api для фактического создания лыж вместо его передачи, он запускает условие для передачи флага magic CRYPT_MACHINE_KEYSET advapi32.