A importação de uma chave DSA da string xml falha para um usuário.Permissões?Instalação quebrada?KSP ruim?

StackOverflow https://stackoverflow.com/questions/4274062

Pergunta

Um usuário relatou recentemente um erro estranho ao usar meu software.Eu uso assinaturas DSA para verificar licenças.Quando o software importa a chave pública para verificar uma assinatura, o provedor de DSA FromXmlString método lança um CryptographicException com a descrição "Chave não válida para uso no estado especificado."

Parece que o método _OpenCSP chamado de System.Security.Cryptography.Utils.CreateProvHandle retorna um NTE_BAD_KEY_STATE (0x8009000b).Esta é a primeira vez que alguém me relata esse erro e esse código não muda há anos.

Quais são as prováveis ​​causas disso?Um erro de permissão mascarado?Uma instalação CAPI quebrada?Bloqueado pelas configurações de confiança/permissões do .net?Lixo armazenado por um provedor de armazenamento de chaves ou um KSP retornando algo inesperado para a cryptoapi?

Pesquisei no Google o código/descrição/etc do erro, mas não encontrei nenhuma resposta real sobre o que pode causar isso ...

Uma versão isolada do código que falha está aqui:http://forum.huagati.com/getattachment.ashx?fileid=78

using System;
using System.Security.Cryptography;
using System.Reflection;

public class Test
{
  public static void Main()
  {
    try
    {
      string key = "<DSAKeyValue><P>wrjxUnfKvH/1s5cbZ48vuhTjflRT5PjOFnr9GeUPZSIoZhYATYtME4JRKrXBtSkyioRNtE1xgghbGAyvAJ5jOWw88fLBF+P1ilsZyq72G1YcbB+co8ImQhAbWKmdCicO9/66Th2MB+7kms/oY3NaCzKEuR7J3b23dGrFpp4ccMM=</P><Q>xmxoSErIJCth91A3dSMjC6yQCu8=</Q><G>bwOLeEaoJHwSiC3i3qk9symlG/9kfzcgrkhRSWHqWhyPAfzqdV1KxJboMpeRoMoFr2+RqqKHgcdbzOypmTeN4QI/qh4nSsl5iEfVerarBOrFuRdOVcJO0d8WE233XQznd1K66nXa5L8d9SNZrM6umZ1YuBjhVsTFdPlIXKfGYhk=</G><Y>wZnEEdMUsF3U3NBQ8ebWHPOp37QRfiBn+7h5runN3YDee1e9bC7JbJf+Uq0eQmU8zDs+avEgD68NpxTKEHGr4nQ3rW6qqacj5SDbwO7nI6eN3wWrVhvrWcQm0tUO93m64HsEJREohfoL+LjqgrqIjZVT4D1KXE+k/iAb6WKAsIA=</Y><J>+zmcCCNm2kn1EXH9T45UcownEe7JH+gl3Lw2lhVzXuX/dYp5sGCA2lK119iQ+m3ogjOuwABATCVFLo6J66DsSlMd0I8WSD5WKPvypQ7QjY0Iv71J2N0FW0ZXpMlk/CE8zq4Z7arM1N564mNe</J><Seed>QDrZrUFowquY5Uay8YtUFOXnv28=</Seed><PgenCounter>Gg==</PgenCounter></DSAKeyValue>";

      DSACryptoServiceProvider csp2 = new DSACryptoServiceProvider();
      csp2.FromXmlString(key);

      Console.WriteLine("Success!");
    }
    catch (Exception ex)
    {
      int hResult = 0;
      try
      {
          PropertyInfo pi = typeof(Exception).GetProperty("HResult", BindingFlags.NonPublic | BindingFlags.Instance);
          hResult = (int)pi.GetValue(ex, null);
      }
      catch (Exception ex2)
      {
          Console.WriteLine("HResult lookup failed: " + ex2.ToString());
      }
      Console.WriteLine("Initializing CSP failed: " + ex.ToString() + "\r\nHResult: " + hResult.ToString("x"));
    }
    Console.WriteLine("\r\nPress Enter to continue");
    Console.ReadLine();
  }
}

...e na máquina do usuário afetado ele retorna:

Initializing CSP failed: System.Security.Cryptography.CryptographicException: Ke
y not valid for use in specified state.

at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters paramete
rs, Boolean randomKeyContainer)
at System.Security.Cryptography.Utils.get_StaticDssProvHandle()
at System.Security.Cryptography.DSACryptoServiceProvider.ImportParameters(DSA
Parameters parameters)
at System.Security.Cryptography.DSA.FromXmlString(String xmlString)
at Test.Main()
HResult: 8009000b

Atualizar: O mesmo código funciona bem ao ser executado no .net fx 2.0 na mesma máquina, mas falha no .net fx 4.0.

Atualização 2: Parece que o provedor DSA procura chaves armazenadas em %APPDATA%\Microsoft\Crypto\DSS\[SID] mesmo depois de inicializar com uma chave existente.Poderia haver um conflito com este mecanismo?Alguém sabe mais sobre como funciona esse armazenamento de chaves e por que ele é atingido ao carregar uma chave pública de uma string?

Foi útil?

Solução

O problema que você descreve me parece muito interessante, mas sem algumas informações adicionais e alguns experimentos é difícil dizer definitivamente qual é o motivo.Então tento descrever como entendo o problema.

Em primeiro lugar, as classes de criptografia .NET usam internamente a CryptoAPI não gerenciada.Então o método _OpenCSP ligue internamente CryptAcquireContext função.Na sua documentação podemos ler a seguir sobre o erro NTE_BAD_KEY_STATE (0x8009000BL):

A senha do usuário foi alterada desde então As chaves privadas foram criptografadas.

Usuários privado as chaves usadas pelo provedor DSA são salvas como arquivos no diretório %APPDATA%\Microsoft\Crypto\DSS\[SID] e será criptografado com um algoritmo relativamente sofisticado sobre o qual você pode ler aqui.Importante entender que os arquivos do diretório correspondem aos contêineres de chaves do usuário.Normalmente, o usuário tem acesso total aos arquivos no sistema de arquivos.Os arquivos serão criptografados com a chave que depende da senha do usuário.Em muitos casos padrão, os arquivos serão criptografados novamente após a alteração da senha, mas o algoritmo de recuperação depende de muitas coisas.Se a senha foi redefinida em vez de alterada pelo próprio usuário (por um administrador de domínio/operador de conta e assim por diante), o antigo contém o diretório %APPDATA%\Microsoft\Crypto\DSS\[SID] não poderia ser mais útil.Por exemplo, se o usuário não for um usuário do Active Directory (um usuário local) e o administrador local redefinir sua senha, o problema com os contêineres criptográficos ocorrerá.

Portanto, a primeira sugestão seria perguntar ao usuário se sua senha do Active Directory foi redefinida.Em seguida você deve verificar se o diretório %APPDATA%\Microsoft\Crypto\DSS\[SID] existe no perfil do usuário e o usuário tem acesso total ao diretório no sistema de arquivos.Você deve excluir todos os arquivos do diretório (criando previamente a cópia de backup dos arquivos).Aliás é interessante saber se o usuário possui perfil salvo central (salvo no servidor).Se tiver perfil central pode-se verificar se o mesmo problema que você descreve existe no outro computador do usuário e o outro usuário não terá problemas em seu computador original.

Mais uma questão que não está muito clara para mim é por que o contêiner de chave do diretório %APPDATA%\Microsoft\Crypto\DSS\[SID] são usados ​​porque você usa apenas chaves públicas.No CryptoAPI deve-se usar CryptAcquireContext com NULL como pszContainer parâmetro e CRYPT_VERIFYCONTEXT em dwFlags.Não tenho certeza se o .NET usa o CRYPT_VERIFYCONTEXT flag e isso pode ser indireto ao seu problema.

Você pode criar DSACryptoServiceProvider com o construtor tendo Parâmetros Csp parâmetro. Parâmetros Csp do outro lado tem Bandeiras propriedade que é estendida no .NET 4.0 com o valor CriarEphemeralKey.A descrição de CspProviderFlags.CreateEphemeralKey está muito próximo da descrição do CRYPT_VERIFYCONTEXT bandeira do CryptAcquireContext função.Então use pode tentar usar CspProviderFlags.CreateEphemeralKey ou CspProviderFlags.CreateEphemeralKey junto com CspProviderFlags.UseDefaultKeyContainer (NULL como pszContainer parâmetro de CryptAcquireContext significa também contêiner de chave padrão).

Além disso, se for possível, você pode tentar depurar o problema no computador onde o problema pode ser reproduzido.Para depuração você pode usar fontes .NET que podem ser habilitadas (veja aqui e aqui) ou baixado de aqui.Você pode então responder algumas perguntas sobre os valores de CspParameters que é usado atualmente em seu programa e compare os valores do .NET 3.5 e do .NET 4.0.

Se o que escrevi não ajudar a resolver o problema, você poderia anexar sua pergunta com informações adicionais:

  • Qual sistema operacional e qual service pack possui o computador onde o problema pode ser reproduzido?
  • O problema depende do usuário?Quero dizer:outros usuários no mesmo computador têm o mesmo problema?
  • O problema existe com o usuário do domínio (active directory) ou com a conta do usuário local?O usuário que tem o problema tem perfil de usuário central que está salvo no servidor?Se sim, o problema pode ser reproduzido também nos outros computadores pelo usuário?
  • Você poderia descrever um pouco mais o ambiente em que situação você utiliza a verificação de chave pública?Principalmente o programa é executado no contexto de segurança do usuário ou você faz alguma representação?

ATUALIZADA:Depois de ler o fórum onde o problema foi postado originalmente, fiquei pessimista quanto à solução do problema.Caso você não tenha contato direto com o computador onde o problema pode ser reproduzido e a comunicação com o único usuário que tem o problema é feita apenas por postagem no fórum...No entanto, estive pensando sobre o problema e decidi tentar reproduzi-lo sozinho.E tive sucesso nisso.Portanto, descreverei aqui meus resultados com cuidado.Descreverei como posso reproduzir o problema para que fique exatamente como descrito em o tópico do fórum.

Você deve seguir os seguintes passos:

1) Você cria uma conta de teste local em seu computador.2) Você faz login com a conta de teste e gera o contêiner de chave padrão para o provedor de DSA.Você pode fazer isso, por exemplo, em relação ao pequeno programa .NET que chama a seguinte função simples:

static string GenerateDsaKeyInDefaultContainer()
{
    const int PROV_DSS_DH = 13;
    CspParameters cspParam = new CspParameters(PROV_DSS_DH);
    cspParam.KeyContainerName = null;
    cspParam.KeyNumber = (int)KeyNumber.Signature;
    cspParam.Flags = CspProviderFlags.UseDefaultKeyContainer;
    DSACryptoServiceProvider csp = new DSACryptoServiceProvider(cspParam);
    return csp.CspKeyContainerInfo.UniqueKeyContainerName;
}

a função retorna o nome do arquivo que será criado no diretório %APPDATA%\Microsoft\Crypto\DSS\[SID] e que conterá o par de chaves gerado.3) Você sai da conta de teste e faz login com outra conta que tenha direitos administrativos locais.Você redefiniu a senha da conta de teste.4) Você faz login mais uma vez com a conta de teste e verifica se o programa de teste, que você postou, compilado no Visual Studio 2010 para .NET 4.0 produz o erro NTE_BAD_KEY_STATE (0x8009000b) e a exceção correspondente será lançada.5) Se você recompilar o programa para .NET 3.5 em vez de .NET 4.0 (você também pode usar o Visual Studio 2010), o programa de teste será executado sem erros.

Portanto, todos os resultados serão exatamente como estão descritos em o tópico do fórum.

Se você excluir ou renomear o arquivo com o contêiner de chave padrão do provedor DSA, o problema será resolvido.Como descrevi antes, após a redefinição da senha do usuário, o conteúdo do contêiner de chave padrão não poderá ser descriptografado.Portanto, se você souber o nome do contêiner padrão que é exclusivo para o usuário (você pode ver o nome, por exemplo, nos rastros do Monitor de Processo) você pode simplesmente copiar qualquer arquivo contêiner de chave de qualquer outro usuário e qualquer outro computador para o diretório %APPDATA%\Microsoft\Crypto\DSS\[SID], renomeie o arquivo para que o nome seja o nome do contêiner padrão e ...você terá absolutamente os mesmos resultados da redefinição da senha do usuário.

Fiz algumas experiências com diferentes configurações do CspParameters como parâmetro de DSACryptoServiceProvider (veja minhas primeiras sugestões sobre o uso de CspProviderFlags.CreateEphemeralKey), mas sem sucesso.Depois disso depurei o código fonte do .NET 4.0 e posso dizer com certeza que a chamada do _OpenCSP função, cujo código não está aberto, será chamada com os parâmetros que são independente dos parâmetros do DSACryptoServiceProvider construtor.Portanto, não é possível encontrar uma solução alternativa para o problema do .NET 4.0 com diferentes configurações do CspParameters como parâmetro de DSACryptoServiceProvider.

Portanto, se você deseja modificar seu código para que ele funcione também na situação com o provedor de chave padrão corrompido, vejo atualmente apenas duas maneiras de resolver o problema:

  1. Implemente todo ou parte do código usando não gerenciado CryptAcquireContext funcionar com o CRYPT_VERIFYCONTEXT bandeira.
  2. Detecte o problema com o erro NTE_BAD_KEY_STATE (0x8009000b) e inclua a parte do código que exclui ou renomeia temporariamente o arquivo de %APPDATA%\Microsoft\Crypto\DSS\[SID] que contém o contêiner de chave padrão corrompido.Para detectar o nome do arquivo, acho que você pode tentar usar CryptGetProvParam funcionar com o PP_UNIQUE_CONTAINER ou e PP_ENUMCONTAINERS parâmetros.

Lamento o longo texto da minha resposta e agradeço a todos que puderam lê-la até aqui.:-)

Espero que minha resposta ajude você KristoferA a resolver o problema e melhorar o software que você desenvolve.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top