27 votes

L'importation d'une clé DSA à partir d'une chaîne xml échoue pour un utilisateur. Autorisations? Installation cassée? Mauvais KSP?

Un utilisateur a récemment rapporté une étrange erreur lors de l'utilisation de mon logiciel. J'utilise des signatures DSA de vérifier les licences. Lorsque le logiciel importations de la clé publique pour vérifier une signature, la DSA fournisseur de FromXmlString méthode lève une CryptographicException avec la description "Clé non valide pour l'utilisation dans l'état spécifié."

Il semblerait que le _OpenCSP méthode est appelée à partir du Système.De sécurité.La cryptographie.Utils.CreateProvHandle renvoie une NTE_BAD_KEY_STATE (0x8009000b). C'est la première fois que quelqu'un a signalé cette erreur à moi, et que le code n'a pas changé depuis des années.

Quelles sont les causes probables de cette situation? Masqué autorisations d'erreur? Une fracture de la CAPI installation? Bloqué par .net de la fiducie et/ou de paramètres d'autorisations? Junk stockées par un fournisseur de stockage des clés, ou un KSP retour quelque chose d'inattendu cryptoapi?

J'ai googlé le code d'erreur/description/etc, mais n'ai pas trouvé de réponses quant à ce qui pourrait causer ce...

Isolé version du code qui échoue est ici: 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();
  }
}

...et les victimes de l'utilisateur de la machine, il retourne:

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

Mise à jour: Le même code fonctionne très bien lors de l'exécution sous .net fx 2.0 sur la même machine, mais ne sous .net fx 4.0.

Mise à jour 2: Il semble que le DSA fournisseur de recherche pour les clés stockés dans %APPDATA%\Microsoft\Crypto\DSS\[SID], même alors, l'initialisation avec une clé existante. Pourrait-il y avoir un conflit avec ce mécanisme? Quelqu'un sait plus sur la façon dont cette clé de stockage que ça fonctionne, et pourquoi il est frappé lors du chargement d'une clé publique à partir d'une chaîne de caractères?

83voto

Oleg Points 136406

Le problème, que vous décrivez, me semble très intéressant, mais sans que des informations supplémentaires et certaines expériences, il est difficile définitivement à dire ce qu'est la raison. J'ai donc essayer de décrire comment je comprends le problème.

Tout d'abord .NET classes de chiffrement utilisent en interne non managé CryptoAPI. Donc, la méthode de _OpenCSP d'appels en interne CryptAcquireContext fonction. Dans sa les documents, nous pouvons lire ce qui suit à propos de l'erreur NTE_BAD_KEY_STATE (0x8009000BL):

Le mot de passe utilisateur a changé depuis les clés privées ont été chiffrés.

Les utilisateurs privés, les clés utilisées par la DSA, fournisseur sont enregistrés en tant que fichiers dans le répertoire %APPDATA%\Microsoft\Crypto\DSS\[SID] , et seront chiffrées avec relativement algorithme sophistiqué sur lequel vous pouvez lire ici. Important de comprendre que les fichiers à partir du répertoire correspond à l'conteneurs de clés de l'utilisateur touches. Généralement, l'utilisateur a accès à tous les fichiers dans le système de fichiers. Les fichiers seront cryptés avec la clé qui dépendent de l'utilisateur mot de passe. Dans de nombreux cas standard, les fichiers seront chiffrés à nouveau après le changement de mot de passe, mais l'algorithme de récupération, qui dépendent de beaucoup de choses. Si le mot de passe a été réinitialisé au lieu de la changer par l'utilisateur lui-même (par un administrateur de domaine/opérateur de compte et ainsi de suite), l'ancien contenu du répertoire %APPDATA%\Microsoft\Crypto\DSS\[SID] n'a pas pu être plus utile. Par exemple, si l'utilisateur n'est pas un annuaire Active Directory de l'utilisateur (un utilisateur local) et le local de l'administrateur de réinitialiser son mot de passe, puis le problème avec les crypto-conteneurs auront lieu.

Donc, la première suggestion serait de demander à l'utilisateur son mot de passe Active Directory a été réinitialisé. Ensuite, vous devez vérifier que le répertoire %APPDATA%\Microsoft\Crypto\DSS\[SID] existent dans le profil de l'utilisateur et que l'utilisateur a un accès complet au répertoire du système de fichiers. Vous devez supprimer tous les fichiers du répertoire (la création d'précédemment la copie de sauvegarde des fichiers). Au passage, il est intéressant de savoir si l'utilisateur au centre du profil enregistré (enregistré sur le serveur). Si il a du profil central on peut vérifier que le même problème que vous décrivez, il existe sur l'autre ordinateur de l'utilisateur et d'autres utilisateur n'a pas de problèmes sur son ordinateur d'origine.

Encore une question, qui n'est pas très clair pour moi, c'est pourquoi le conteneur de clé de la le répertoire %APPDATA%\Microsoft\Crypto\DSS\[SID] sont utilisés à tous parce que vous utilisez seulement les clés publiques. Dans CryptoAPI il faut utiliser CryptAcquireContext avec NULL comme pszContainer paramètre et CRYPT_VERIFYCONTEXT en dwFlags. Je ne suis pas sûr que .NET, utilisez l' CRYPT_VERIFYCONTEXT drapeau et il pourrait être indirect votre problème.

Vous pouvez créer DSACryptoServiceProvider avec le constructeur ayant CspParameters paramètre. CspParameters de l'autre côté a des Drapeaux de la propriété qui se prolonge dans le .NET 4.0 avec la valeur CreateEphemeralKey. La description de l' CspProviderFlags.CreateEphemeralKey est très proche de la description de l' CRYPT_VERIFYCONTEXT drapeau de l' CryptAcquireContext fonction. Donc, utiliser pouvez essayer d'utiliser CspProviderFlags.CreateEphemeralKey ou CspProviderFlags.CreateEphemeralKey avec CspProviderFlags.UseDefaultKeyContainer (NULL comme pszContainer paramètre de CryptAcquireContext signifie aussi, le conteneur de clé par défaut).

De plus, si c'est possible, vous pouvez essayer de déboguer le problème sur l'ordinateur où le problème peut être reproduit. Pour le débogage, vous pouvez utiliser .NET sources, qui peut être activé (voir ici et ici) ou téléchargé à partir d' ici. Vous pouvez ensuite répondre à quelques questions sur les valeurs de CspParameters qui est actuellement utilisé dans votre programme et de comparer les valeurs .NET 3.5 et .NET 4.0.

Si ce que j'ai écrit ne va pas aider à résoudre le problème, s'il vous plaît pourriez-vous ajouter à votre question avec plus d'informations:

  • Dont le système d'exploitation et le service pack a l'ordinateur où le problème peut être reproduit?
  • Est le problème dépendantes de l'utilisateur? Je veux dire: a d'autres utilisateurs sur le même ordinateur et le même problème?
  • Est la le problème existe avec le contrôleur de domaine (active directory) de l'utilisateur ou avec le compte d'utilisateur local? A l'utilisateur qui a le problème central de profil de l'utilisateur qui sont enregistrés sur le serveur? Si il a, que ne peut le problème peut être reproduit également sur les autres ordinateurs par l'utilisateur?
  • Pourriez-vous décrire un peu plus l'environnement dans lequel la situation, vous utilisez la clé publique de vérification? Est en particulier le programme s'exécute dans le contexte de sécurité utilisateur ou de vous faire quelques usurpation d'identité?

Mise à JOUR: Après la lecture de la tribune où le problème de l'origine a été posté, je deviens pessimiste à propos de la résolution du problème. Si vous n'avez pas de contact direct à l'ordinateur où le problème peut être reproduit et la communication avec le seul utilisateur qui a le problème ne sont effectués que par poster dans le forum... Néanmoins, j'ai été la réflexion sur le problème et j'ai donc décidé d'essayer de reproduire le problème moi-même. Et j'ai eu du succès dans ce. Je vais donc décrire ici mes résultats avec précaution. Je vais décrire comment le pouvez reproduire le problème afin qu'il ressemble exactement comme il est décrit dans le fil de discussion du forum.

Vous devez faire les étapes suivantes:

1) Vous créez un test local sur votre ordinateur. 2) vous Vous connectez avec le compte de test et de générer de la valeur par défaut du conteneur de clé pour le DSA fournisseur. Vous pouvez le faire par exemple dans le domaine de la petite .NET programme appel de fonction suivante:

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

la fonction retourne le nom du fichier qui sera créé dans le répertoire %APPDATA%\Microsoft\Crypto\DSS\[SID] et qui contiendra la clé générée par paire. 3) vous Vous déconnectez le test de compte et de se connecter avec un autre compte qui a les droits d'administrateur local. Vous réinitialiser le mot de passe du compte de test. 4) vous Vous connectez une fois de plus avec le compte de test et vérifier que le programme de test, qui vous avez posté, compilé dans Visual Studio 2010 .NET 4.0 produit l'erreur NTE_BAD_KEY_STATE (0x8009000b) et le correspondant de l'exception sera levée. 5) Si vous recompilez le programme pour .NET 3.5 au lieu de .NET 4.0 (vous pouvez utiliser Visual Studio 2010) le programme de test sera exécuté sans erreur.

Donc, tous les résultats seront exactement comme il est décrit dans le fil de discussion du forum.

Si vous supprimez ou renommez le fichier avec le conteneur de clé par défaut pour le DSA fournisseur le problème sera résolu. Comme je l'ai décrit avant, après la réinitialisation du mot de passe des utilisateurs le contenu du conteneur de clé par défaut sera pas en mesure d'être déchiffré. Donc si vous connaissez le nom du conteneur par défaut qui est unique pour l'utilisateur (vous pouvez voir le nom par exemple des traces de l' analyse de Processus) il vous suffit de copier n'importe quelle touche du fichier conteneur de tout autre utilisateur et de toute autre ordinateur dans le répertoire %APPDATA%\Microsoft\Crypto\DSS\[SID], renommez le fichier, de sorte que le nom sera la valeur par défaut nom de conteneur et ..., vous aurez absolument les mêmes résultats, comme avec la réinitialisation du mot de passe des utilisateurs.

J'ai fait quelques expériences avec différents paramètres de l' CspParameters comme le paramètre d' DSACryptoServiceProvider (voir mon premier suggestions à propos de l'utilisation de CspProviderFlags.CreateEphemeralKey), mais sans succès. Après cela, j'ai débogué le code source de .NET 4.0 et peut définitivement dire que l'appel d' _OpenCSP de la fonction, le code n'est pas ouvert, sera appelé avec les paramètres qui sont indépendants à partir des paramètres de l' DSACryptoServiceProvider constructeur. Donc vous ne pouvez pas trouver une solution de contournement du problème .NET 4.0 dans la manière dont les différents paramètres de l' CspParameters comme le paramètre d' DSACryptoServiceProvider.

Donc, si vous voulez modifier votre code, de sorte qu'il devrait fonctionner également dans la situation avec le corrompu par défaut fournisseur clé je vois pour le moment que 2 façons de résoudre le problème:

  1. Mettre en œuvre la totalité ou la partie de code à l'aide de non géré CryptAcquireContext fonction avec l' CRYPT_VERIFYCONTEXT drapeau.
  2. Détecter le problème avec l'erreur NTE_BAD_KEY_STATE (0x8009000b) et inclure la partie de code qui supprime ou temporaire renommé le fichier à partir d' %APPDATA%\Microsoft\Crypto\DSS\[SID] qui contient le corrompu conteneur de clé par défaut. Pour détecter le nom du fichier, je pense que vous pouvez essayer d'utiliser l' CryptGetProvParam fonction avec l' PP_UNIQUE_CONTAINER ou/et PP_ENUMCONTAINERS paramètres.

Je suis désolé pour le long texte de ma réponse et merci à tous ceux qui sont en mesure de le lire jusqu'à cet endroit. :-)

J'espère que ma réponse ne vous aider à KristoferA pour résoudre le problème et d'améliorer un logiciel qui vous développer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X