studio - encriptacion aes c#
La importación de una clave DSA desde una cadena xml falla para un usuario. Permisos? Instalación rota? Mal KSP? (1)
Un usuario recientemente reportó un error extraño al usar mi software. Yo uso firmas DSA para verificar las licencias. Cuando el software importa la clave pública para verificar una firma, el método FromXmlString del proveedor de FromXmlString lanza una CryptographicException con la descripción "La clave no es válida para su uso en el estado especificado " .
Parece que el método _OpenCSP llamado desde System.Security.Cryptography.Utils.CreateProvHandle devuelve un NTE_BAD_KEY_STATE (0x8009000b). Esta es la primera vez que alguien me informa de este error, y ese código no ha cambiado durante años.
¿Cuáles son las posibles causas de esto? ¿Un error de permisos enmascarados? ¿Una instalación CAPI rota? ¿Bloqueado por .net configuración de confianza / permisos? ¿Basura almacenada por un proveedor de almacenamiento de claves o un KSP que devuelve algo inesperado a cryptoapi?
He buscado en Google el código de error / descripción / etc, pero no encontré ninguna respuesta real sobre lo que podría causar esto ...
Una versión aislada del código que falla está aquí: 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();
}
}
... y en la máquina del usuario afectado devuelve:
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
Actualización: el mismo código funciona bien cuando se ejecuta con .net fx 2.0 en la misma máquina, pero falla con .net fx 4.0.
Actualización 2: Parece que el proveedor de DSA busca claves almacenadas en% APPDATA% / Microsoft / Crypto / DSS / [SID] incluso luego de inicializar con una clave existente. ¿Podría haber un conflicto con este mecanismo? ¿Alguien sabe más acerca de cómo funciona esa cosa de almacenamiento de claves y por qué se golpea al cargar una clave pública desde una cadena?
El problema, que usted describe, me parece muy interesante, pero sin información adicional y algunos experimentos es difícil decir cuál es la razón. Así que trato de describir cómo entiendo el problema.
En primer lugar, las clases de criptografía .NET utilizan internamente el CryptoAPI no administrado. Así que el método _OpenCSP
llama internamente a la función CryptAcquireContext . En su documentación podemos leer lo siguiente sobre el error NTE_BAD_KEY_STATE
(0x8009000BL):
La contraseña del usuario ha cambiado desde que se cifraron las claves privadas.
Las claves privadas de los usuarios utilizadas por el proveedor de DSA se guardan como archivos en el directorio %APPDATA%/Microsoft/Crypto/DSS/[SID]
y se cifrarán con un algoritmo relativamente sofisticado sobre el que puede leer here . Es importante comprender que los archivos del directorio corresponden a los contenedores de claves de las claves del usuario. Normalmente, el usuario tiene acceso completo a los archivos en el sistema de archivos. Los archivos se cifrarán con la clave que depende de la contraseña del usuario. En muchos casos estándar, los archivos se volverán a cifrar después del cambio de contraseña, pero el algoritmo de recuperación, que depende de muchas cosas. Si se restableció la contraseña en lugar del cambio por parte del usuario (por parte de un administrador de dominio / operador de cuenta, etc.), el antiguo contenido del directorio %APPDATA%/Microsoft/Crypto/DSS/[SID]
podría no ser más útil. Por ejemplo, si el usuario no es un usuario de Active Directory (un usuario local) y el administrador local restablece su contraseña, se producirá el problema con los contenedores criptográficos.
Entonces, la primera sugerencia sería preguntarle al usuario si su contraseña de Active Directory fue restablecida. A continuación, debe verificar que el directorio %APPDATA%/Microsoft/Crypto/DSS/[SID]
existe en el perfil del usuario y el usuario tiene acceso completo al directorio en el sistema de archivos. Debe eliminar todos los archivos del directorio (creando previamente la copia de respaldo de los archivos). Por cierto, es interesante saber si el usuario tiene un perfil central guardado (guardado en el servidor). Si tiene un perfil central, se puede verificar que el mismo problema, que usted describe, existe en la otra computadora para el usuario y el otro usuario no tendrá problemas en su computadora original.
Una pregunta más que no está del todo clara para mí es por qué se usa el contenedor de claves del directorio %APPDATA%/Microsoft/Crypto/DSS/[SID]
porque solo usa claves públicas . En CryptoAPI, se debe usar CryptAcquireContext
con NULL
como parámetro CRYPT_VERIFYCONTEXT
y CRYPT_VERIFYCONTEXT
en dwFlags
. No estoy seguro de que .NET use el indicador CRYPT_VERIFYCONTEXT
y podría ser indirecto su problema.
Puede crear DSACryptoServiceProvider
con el constructor teniendo el parámetro CspParameters . CspParameters en el otro lado tiene la propiedad Flags que se extiende en .NET 4.0 con el valor CreateEphemeralKey . La descripción de CspProviderFlags.CreateEphemeralKey
está muy cerca de la descripción del indicador CRYPT_VERIFYCONTEXT
de la función CryptAcquireContext
. Por lo tanto, el uso puede intentar usar CreateEphemeralKey o CspProviderFlags.CreateEphemeralKey
junto con CspProviderFlags.UseDefaultKeyContainer
(el parámetro NULL
as pszContainer
de CryptAcquireContext
significa también el contenedor de claves predeterminado).
Además, si es posible, puede intentar depurar el problema en la computadora donde se puede reproducir el problema. Para la depuración puede usar fuentes .NET que se pueden habilitar (ver here y here ) o descargar desde here . Luego puede responder algunas preguntas sobre los valores de CspParameters
que se usan actualmente en su programa y comparar los valores de .NET 3.5 y .NET 4.0.
Si lo que escribí no ayudará a resolver el problema, puede adjuntar su pregunta con información adicional:
- ¿Qué sistema operativo y qué paquete de servicio tiene la computadora donde se puede reproducir el problema?
- ¿El problema es dependiente del usuario? Quiero decir: ¿tiene otros usuarios en la misma computadora el mismo problema?
- ¿Existe el problema con el usuario del dominio (directorio activo) o con la cuenta de usuario local? ¿El usuario que tiene el perfil de usuario central problemático se guarda en el servidor? Si lo ha hecho, ¿el usuario puede reproducir el problema también en las otras computadoras?
- ¿Podría describir un poco más el entorno en el que utiliza la verificación de clave pública? Especialmente, ¿el programa se ejecuta en el contexto de seguridad del usuario o haces alguna suplantación?
ACTUALIZADO : Después de leer el foro donde se publicó originalmente el problema, me vuelvo pesimista sobre la resolución del problema. Si no tiene contacto directo con la computadora donde se puede reproducir el problema y la comunicación con el único usuario que tiene el problema se realiza solo por publicación en el foro ... Sin embargo, he estado pensando en el problema y por eso decidí Intento reproducir el problema yo mismo. Y tuve éxito en esto. Así que voy a describir aquí mis resultados con cuidado. Describiré cómo se puede reproducir el problema para que se vea exactamente como se describe en el hilo del foro .
Debes hacer los siguientes pasos:
1) Crea una cuenta de prueba local en su computadora. 2) Inicia sesión con la cuenta de prueba y genera el contenedor de claves predeterminado para el proveedor de DSA. Puede hacer esto, por ejemplo, con respecto al pequeño programa .NET que llama a la siguiente función simple:
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 función devuelve el nombre del archivo que se creará en el directorio %APPDATA%/Microsoft/Crypto/DSS/[SID]
y que contendrá el par de claves generado. 3) Usted cierra la sesión de prueba e inicia sesión con otra cuenta que tiene derechos administrativos locales. Restablece la contraseña de la cuenta de prueba. 4) Inicia sesión una vez más con la cuenta de prueba y verifica que el programa de prueba, que publicaste, compilado en Visual Studio 2010 para .NET 4.0 produce el error NTE_BAD_KEY_STATE
(0x8009000b) y se lanzará la excepción correspondiente. 5) Si recompila el programa para .NET 3.5 en lugar de .NET 4.0 (también puede usar Visual Studio 2010), el programa de prueba se ejecutará sin errores.
Por lo tanto, todos los resultados serán exactamente como se describen en el hilo del foro .
Si elimina o cambia el nombre del archivo con el contenedor de claves predeterminado para el proveedor de DSA, se solucionará el problema. Como describí antes, después de restablecer la contraseña del usuario, el contenido del contenedor de claves predeterminado no podrá ser descifrado. Entonces, si conoce el nombre del contenedor predeterminado que es único para el usuario (puede ver el nombre, por ejemplo, de las huellas del Monitor de proceso ), solo puede copiar cualquier archivo de contenedor clave de cualquier otro usuario y cualquier otra computadora a la directorio %APPDATA%/Microsoft/Crypto/DSS/[SID]
, cambie el nombre del archivo para que el nombre sea el nombre del contenedor predeterminado y ... tendrá absolutamente los mismos resultados que con el restablecimiento de la contraseña del usuario.
Hice algunos experimentos con diferentes configuraciones de CspParameters
como el parámetro de DSACryptoServiceProvider
(vea mis primeras sugerencias sobre el uso de CreateEphemeralKey ), pero sin ningún éxito. Después de eso, depuré el código fuente de .NET 4.0 y puedo decir definitivamente que la llamada de la función _OpenCSP
, cuyo código no está abierto, se llamará con los parámetros que son independientes de los parámetros del constructor DSACryptoServiceProvider
. Por lo tanto, no se puede encontrar una solución alternativa al problema para .NET 4.0 con la configuración diferente de CspParameters
como parámetro de DSACryptoServiceProvider
.
Entonces, si desea modificar su código para que funcione también en la situación con el proveedor de claves predeterminado dañado, actualmente veo solo 2 formas de resolver el problema:
- Implemente la totalidad o la parte del código utilizando la función
CryptAcquireContext
noCryptAcquireContext
con el indicadorCRYPT_VERIFYCONTEXT
. - Detecte el problema con el error
NTE_BAD_KEY_STATE
(0x8009000b) e incluya la parte del código que eliminó o temporalmente cambió el nombre del archivo de%APPDATA%/Microsoft/Crypto/DSS/[SID]
que contiene el contenedor de claves predeterminado dañado. Para detectar el nombre del archivo, creo que puede intentar usar la funciónCryptGetProvParam
con los parámetrosPP_ENUMCONTAINERS
y / yPP_ENUMCONTAINERS
.
Lo siento por el largo texto de mi respuesta y gracias a todos los que pueden leerlo hasta este lugar. :-)
Espero que mi respuesta le ayude a KristoferA a resolver el problema y mejorar el software que usted desarrolla.