При вставке сертификата (с помощью privatekey) в Root хранилище сертификатов LocalMachine завершается сбоем в .NET 4 - PullRequest
17 голосов
/ 02 сентября 2010

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

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

//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

Примечание: это говорит, что у него есть приватный ключ.

То есть вы могли бы найти его с помощью FindPrivateKey

C:\Users\Administrator\Desktop>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 ]

9 голосов
/ 23 июня 2011

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

X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet

в X509Certificate2 ctor.Теперь вы используете 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);

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

9 голосов
/ 05 сентября 2010

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

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

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

ОБНОВЛЕНО : Прежде всего извините за длинный ответ.

Я мог бы разделить код вашей программы на две части. В первой части вы создаете самозаверяющий сертификат, который можно использовать в качестве сертификатов ЦС, и сохраняете его как 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.exe). В приведенном выше примере я не ограничиваю CA для какого-либо специального EKU, поэтому вы можете использовать его без каких-либо ограничений, но если вам нужны ограничения, вы можете просто добавить дополнительные параметры в MakeCert.exe . Вы также можете использовать MakeCert.exe для создания другого сертификата, который подписан сертификатом CA. Таким образом, вы можете сделать небольшую PKI только для MakeCert.exe.

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

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

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

CertMgr.exe -add -c CA.cer -s -r localMachine AuthRoot

  • Вы должны импортировать сертификат CA с закрытым ключом на компьютер на одном компьютере и только для пользователя, который выдаст другие сертификаты (который подпишет новый сертификаты с закрытым ключом CA). Одно использование для импорта сертификата в My хранилище сертификатов 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 хранилище с настройкой информации о место Key Container с закрытым ключом (CERT_KEY_PROV_INFO_PROP_ID). Более того, вы можете удалить приватный ключ сразу после использования, импортировать его, только если вам действительно нужно его использовать, и не удерживать его постоянно . Все это важно для лучшей безопасности.

2 голосов
/ 11 февраля 2011

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

Больше информации здесь:

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

Dinko

0 голосов
/ 26 октября 2017

Основная проблема заключается в том, что API сертификатов .NET является просто оболочкой для API менеджера сертификатов C ++ advapi32, поэтому вы не можете указать все параметры, которые передаются в этот API, который фактически отвечает за прикрепление сертификата. в хранилище сертификатов Windows и сохраняя ключи. Суть в том, что параметр « UseMachineStore » должен быть передан в CspProviderFlags , который, в свою очередь, передается в CAPI.CRYPT_MACHINE_KEYSET . Это маленький парень, который определяет, будет ли ключ сохранен на самом деле или нет. Кажется, есть несколько разных причин, почему эта опция не устанавливается, даже если вы установили X509KeyStorageFlags.PersistKeySet и MachineKeySet и Exportable . Все эти параметры действуют только до тех пор, пока тупой ключ остается в папке 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, чтобы добавить сертификат, который, если эмитент равен Субъект (это самозаверяющий сертификат) затем добавляет сертификат в корневой каталог, прежде чем добавить его в 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 для фактического генерирования SKI вместо его передачи, он вызывает условную передачу магического флага CRYPT_MACHINE_KEYSET в advapi32.

0 голосов
/ 22 августа 2016

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

0 голосов
/ 10 марта 2016

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

...