Мне кажется, вам следует импортировать ключ немного другим способом. См. 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
). Более того, вы можете удалить приватный ключ сразу после использования, импортировать его, только если вам действительно нужно его использовать, и не удерживать его постоянно . Все это важно для лучшей безопасности.