Inserindo Certificado (com chave privada) na raiz, o armazenamento de certificados LocalMachine falha no .NET 4

Estou tendo problemas ao inserir um novo certificado de CA com chave privada no armazenamento de certificados raiz da máquina local.

Isto é o que acontece:

//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 (); 

O certificado é inserido e tudo parece dândi: (veja!) note que diz que tem uma chave privada

Nota: diz que tem uma chave privada.

Então você diria que um seria capaz de encontrá-lo com 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 

É fofo … MAS É ERRADO !! (2 cães estúpidos de referência)

E a janela de exportação de certificados me dá uma mensagem muito boa: texto alternativo

Este código é executado enquanto se passa por um administrador usando este trecho: clique aqui

Eu adoraria saber por quê?

(testado no Windows Server 2008 R2 e Windows 7)

Eu vou ser amaldiçoado!

Funciona quando eu compilo para v3.5 !!!!

O que fazer?

Eu tive exatamente o mesmo problema e a solução acabou sendo bem simples. Tudo que eu tive que fazer é passar

 X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet 

para o ctor do X509Certificate2. Agora você está usando o DotNetUtilities para converter o certificado bouncycastle para o .net, mas o método auxiliar cria o certificado .net com o DefaultKeySet (em vez de MachineKeySet + PersistKeySet).

E organize a chave privada assim:

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

Eu espero que isso ajude.

Parece-me que você deve importar a chave de uma outra maneira. Veja http://support.microsoft.com/kb/950090 para um exemplo.

Além disso, não acho bom salvar a chave privada em UseMachineKeyStore . Na maioria dos casos, você precisa importar o certificado com a chave privada em Minha loja de algum usuário e importar no certificado somente raiz sem chave privada.

Se você precisar salvar a chave privada no armazenamento de chaves do computador, deverá proteger pelo menos a chave para leitura apenas para alguns usuários selecionados e não para todos. O contêiner de chave é apenas um arquivo no sistema de arquivos (consulte os arquivos no diretório “% ALLUSERSPROFILE% \ Microsoft \ Crypto \ Keys”) que possui descritores de segurança, como outros arquivos em NTFS. Para alterar os descritores de segurança dos arquivos, você pode usar a propriedade AddAccessRule e AddAccessRule , RemoveAccessRule e assim por diante.

ATUALIZADO : Primeiro de tudo desculpe pela resposta longa.

Eu poderia dividir o código do seu programa em duas partes. Na primeira parte, você gera um certificado autoassinado que pode ser usado como um certificado de CA e o salva como arquivo rootcert.pfx . Na segunda parte, você importa o certificado, mas usa RSACryptoServiceProvider preenchido com as propriedades da chave criada anteriormente, em vez de usar rootcert.pfx .

Eu sugiro replace a segunda parte do seu código para um código mais simples e padrão: import certificado com a chave privada de rootcert.pfx como descrito em http://support.microsoft.com/kb/950090 . Funciona muito bem.

Eu não uso eu mesmo o BouncyCastle, então eu não pude comentar a primeira parte do seu código, mas em geral o que você faz no código que você poderia fazer também com relação ao utilitário MakeCert.exe do Windows SDK. Você pode fazer como seguir

 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" 

Em seguida, você pode exportar o certificado com ou sem chave privada em relação ao snap-in de certificado (para mmc.exe). No exemplo acima, eu não limito a CA para algum EKU especial, então você pode usá-lo sem qualquer restrição, mas se você precisar das restrições, basta adicionar parâmetros adicionais ao MakeCert.exe . Você também pode usar o MakeCert.exe para criar outro certificado assinado com o certificado da autoridade de certificação. Então você é capaz de fazer pequenos PKI com respeito apenas ao MakeCert.exe.

Parece-me que a criação do certificado é realmente uma parte separada do seu código. Seu principal problema está na segunda parte.

Se você quiser importar o certificado da CA, você deve levar em consideração algumas coisas importantes:

  • Você deve importá-lo em Root ou AuthRoot em localMachine em cada (ou vários) computadores de sua organização, mas deve importar o certificado sem a chave privada . Você pode fazer isso com relação a seguir

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

  • Você deve importar o certificado da CA com a chave privada no computador em um computador e apenas para o usuário que emitirá outros certificados (quem assinará novos certificados com a chave privada da CA). Um uso para importar o certificado no armazenamento de certificados My do CurrentUser . Então, o código no computador pode parecer

Segue:

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

O código funcionará se você alterar o StoreName.My e o StoreLocation.CurrentUser para outros valores, mas eu não recomendo que você faça isso.

Em geral, a importação de certificados no código .NET parece um pouco estranho e não mostra o que será feito sob o capô. O Windows conhece apenas os Key Containers onde as chaves privadas (para ser exatamente o par de chaves) serão salvas em relação a CSP e Certificate Stores, onde os certificados serão salvos (consulte http://msdn.microsoft.com/pt-br/library/bb204781 .aspx sobre a localização da loja). Para poder salvar informações sobre o contêiner de chave no armazenamento de certificados, a Microsoft introduziu as chamadas Propriedades estendidas do certificado . Se você usar nas propriedades .NET de X509Certificate2 como Thumbprint , FriendlyName , HasPrivateKey , Archived e assim por diante, você trabalha com as Propriedades estendidas do certificado. Por isso, recomendo que você importe o certificado da CA duas vezes. Um em Root ou AuthRoot sem definir CERT_KEY_PROV_INFO_PROP_ID Certificate Extended Properties e mais uma vez em My store com a configuração de informações sobre o local do Key Container com a chave privada ( CERT_KEY_PROV_INFO_PROP_ID ). Além disso, você pode considerar remover a chave privada diretamente após o uso, importá-la somente se você realmente precisar usá-la e não segurá-la permanentemente . Tudo isso é importante para ter melhor segurança.

Eu encontrei esse problema e parece que mesmo o usuário com o qual você está executando a ferramenta FindPrivateKey não tem access à chave e, portanto, você obteria a mensagem “Não é possível obter nome de arquivo de chave particular”. Você poderia executar a ferramenta como processo LocalSystem.

Mais informações aqui:

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

Dinko

novo X509Certificate2 (localPFXPath, inputPass, X509KeyStorageFlags.MachineKeySet & X509KeyStorageFlags.PersistKeySet); com o & em vez do | trabalhou para mim.

Geralmente, os Certificados no Root não terão chave privada para gerenciar. Você deve importar para Minha pasta se estiver associando a chave na solicitação da web. Eu tenho a exceção TLS / SSl onde eu tenho cadeia de certificados de cliente. Se você armazenar toda a cadeia de certificados na Minha loja, eu me livrei dessa exceção. Onde o problema está com as contas de usuário. O utilitário para armazenar os certificados usa a conta de usuário atual e o aplicativo real é executado na conta do sistema.

O problema básico é que a API de certificados .NET é apenas um wrapper em torno da API do gerenciador de certificados advapi32 em C ++, portanto, você não precisa especificar todas as opções que são passadas para essa API que é realmente responsável por inserir o certificado no Windows. loja cert e persistindo as chaves. A conclusão é que a opção “ UseMachineStore ” precisa ser passada para o CspProviderFlags, que por sua vez é passado para o CAPI.CRYPT_MACHINE_KEYSET . Este é o carinha que determina se a chave é persistida de verdade ou não. Parece haver várias razões diferentes pelas quais essa opção não é configurada, mesmo que você defina X509KeyStorageFlags.PersistKeySet e MachineKeySet e Exportable . Todas essas opções só vivem enquanto a chave estúpida permanece na pasta C: \ ProgramData \ Microsoft \ Crypto \ RSA \ MachineKeys \. Se CRYPT_MACHINE_KEYSET não for definido no momento da importação, então o advapi32 explodirá a chave assim que o identificador de certificado for descartado pelo GC.

Solução: adicione o certificado à raiz confiável ANTES de importar o certificado para o armazenamento da máquina pessoal. Ao ler os logs do CAPI2 , vejo duas chamadas para “X509 Objects” toda vez que o certificado é “importado”. Sempre temos o , (o que queremos), mas o outro não, a menos que “Verify Chain Policy” não retorne nenhum erro. Portanto, parece que o advapi32 está verificando a “validade” do certificado e retorna uma exceção que é engolida pelo X509Certificate2 ( adoro quantos blocos de captura vazios eles têm nesse código ) ou o advapi32 decide unilateralmente não persistir as chaves por não confiável certificados. (By the way, eu suspeito que esta é uma mudança de comportamento entre 2008 e 20012, mas eu não provou isso.) Para contornar isso, eu adicionei um If-check ao meu código para adicionar o certificado que se o Emissor é igual ao Assunto (é um certificado auto-assinado) e, em seguida, adicione o certificado à Raiz antes de adicioná-lo ao Meu.

  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(); } 

Nota: Descobri que isso é desnecessário se estiver usando um certificado que não tenha um identificador de chave do assunto. De alguma forma, quando você aciona a API para realmente gerar o SKI em vez de entregá-lo, ele aciona o condicional para passar o sinalizador CRYPT_MACHINE_KEYSET para advapi32.