Microsoft Crypto API (mscapi) con NCipher

Microsoft Crypto API es una interfaz para ejecutar operaciones de cifrado y gestionado de las claves que nos proporciona directamente el sistema operativo. Esta interfaz es implementada por los llamados proveedores y, por defecto, Windows trae consigo unos cuantos listos para ser usados.

 

Un proveedor, generalmente, es capaz de crear y destruir pares de claves y realizar operaciones de cifrado con ellas. Estas claves, generalmente se agrupan en contenedores, y los proveedores también son encargados de crear/destruir dichos contenedores.

image

Cada proveedor gestiona sus propios contenedores (aunque si dos proveedores son en realidad la misma implementación, usaran los mismos contenedores). Estos a su vez pueden ser a nivel de usuario o de maquina.

Para saber que proveedores tenemos instalados en nuestra máquina, basta con abrir el RegEdit y buscar en la siguiente ruta:

HKEY_LOCAL_MACHINE->SOFTWARE->Microsoft->Cryptography->Defaults->Provider

No todos los proveedores implementan las mismas funcionalidades, con lo que tambien tenemos tipos de proveedores. Para enumerar estos tipos, basta con irse a esta ruta del registro:

HKEY_LOCAL_MACHINE->SOFTWARE->Microsoft->Cryptography->Defaults->ProviderTypes

 

image

Si abrimos un proveedor podemos ver el tipo consultado la fila Type del mismo. En el ejemplo, Microsoft Base DS Cryptographic Provider es de tipo DSS Signature (tipo 3).

 

Usando un proveedor desde C#

Para realizar operaciones de cifrado desde código administrado, .NET implementa wrappers para la MSCAPI dentro del espacio de nombres System.Security.Cryptography con varias clases que engloban las diferentes llamadas que MSCAPI es capaz de ejecutar

 

image

 

Por ejemplo, el RSACryptServiceProvider tiene la capacidad de cifrar/descifrar/firmar… usando el algorithmo RSA.

Usar esta clase es tan sencillo (otro proveedores siguen el mismo patron) como instanciarla y llamar a los métodos que necesitemos.

   1:  using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
   2:          {
   3:              rsa.Encrypt(...);
   4:              rsa.Decrypt(...);
   5:              rsa.SignData(...);
   6:              rsa.VerifyData(...);
   7:              ...
   8:          }

En este código, cuando instanciamos el proveedor RSA, el wrapper va a usar el proveedor por defecto (Microsoft Enhanced RSA and  AES Cryptographic Provider) y el contenedor de usuario por defecto (ya que no estamos especificando nada). La pareja de claves, al no existir, se crearan automaticamente y persistirán dentro de nuestro SO para usarlas mas adelante.

 

Para modificar el comportamiento por defecto, podemos pasar una clase de parametros en el constructor especificando el tipo de proveedor y contenedor que queremos usar.

 

   1:  CspParameters cp = new CspParameters(1, "another provider");
   2:          cp.KeyContainerName = "UltraSecretDataContainer";
   3:          cp.Flags = CspProviderFlags.UseMachineKeyStore;
   4:   
   5:          using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp))
   6:          {
   7:              ...
   8:          }

 

nota: El nombre del proveedor deberá coincidir con el que existe dentro del registro que hemos visto anteriormente.

 

En el ejemplo anterior, vamos a usar otro proveedor llamado “another provider” (evidentemente no existe) y un contenedor llamado “UltraSecretDataContainer” que además se almacenará a nivel de máquina (no de usuario). El contenedor será creado automáticamente en caso de que no exista, lo mismo que su par de claves.

nota: Si intentamos descrifrar algo con un contenedor recién creado, no va a funcionar ya que un mensaje solo se puede descifrar con su par de claves, y si está recién creado, es que no se ha cifrado con ellas.

En la siguiente captura vemos el contendor y la pareja de claves con un nombre aleatorio que el proveedor ha establecido por nosotros.

image

Si lo que queremos es descartar (borrar) un par de claves, tenemos que instanciar el proveedor especificando el contenedor e indicarle que no persistan las claves.

 

   1:  // Create a new instance of RSACryptoServiceProvider that accesses
   2:          // the key container.
   3:          RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);
   4:   
   5:          // Delete the key entry in the container.
   6:          rsa.PersistKeyInCsp = false;
   7:   
   8:          // Call Clear to release resources and delete the key from the container.
   9:          rsa.Clear();

Ojo, todos los datos que hayamos encriptado con este contenedor no va a ser posible su posterior descifrado a no ser que tengamos un backup de las claves y las importemos a un nuevo contenedor.

 

NCipher

En los ejemplos anteriores, las claves son generadas y guardadas en el equipo, incluyendo la clave privada. En estas situaciones, si nuestro equipo es comprometido, las claves pueden ser robadas o, lo que es peor, el provider puede ser cambiado para que genere un par de claves no tan aleatorias como nos gustaría, es decir, si nuestro equipo se expone, la seguridad se va al traste. Para resolver escenarios así necesitaríamos un dispositivo especial para gestionar nuestras claves, lo que es llamado un HSM (Hardware security module).

 

Este tipo de dispositivos son capaces de ejecutar operaciones criptográficas sin exponer las claves privadas bajo ningún concepto. Podremos incremental sustancialmente el nivel de seguridad en nuestras aplicaciones ya que no vamos a tener que preocuparnos sobre como gestionar las peligrosas claves privadas y, de paso, nos beneficiaremos de un hardware exclusivo para las costosas (en CPU) operaciones de cifrado.

La protección que hacen los dispositivos HSM consiste básicamente (varia según modelos) en dos puntos. Si el hardware es comprometido (abierto, manipulado etc.) las claves serán borradas automáticamente

(protección de módulo) y estas claves, además, pueden ser cifradas a su vez por un token o varios (Protección OCS, en caso de el modelo que vamos a tratar, unas tarjetas SmartCard). Si una clave es protegida por un token, el token siempre será necesario para operar con dichas claves, pero si una clave solo es protegida a nivel de modulo, solo necesitaras establecer una conexión (con las correspondientes medidas de seguridad) para operar con las claves. El ecosistema de claves, tokens, dispositivos… es llamado word y cuando configurar un mundo (protegido por los correspondientes password de administrador y contraseñas) con sus claves privadas maestras, es imposible deshacer la operación salvo mandado el dispositivo al SAT para que lo reseteen (se perderán todas las claves)

image

El dispositivo que voy a usar para las pruebas en este artículo es el Thales NCipher connect[1], un dispositivo con doble fuente de alimentación para evitar interrupciones en el servicio, con conexión ethernet y capaz de realizar unas 6000 operaciones de firma por segundo según el fabricante. Vamos, que es un aparato potente, pero para más información mejor consultar la web del fabricante en las referencias.

Network HSM nShield Connect 6000

Integración de NCipher

Cuando adquirimos un dispositivo de estos, la parte de configuración termina en el momento que esta el word configurado en el/los dispositivos y enlazado con la o las máquinas desde donde se va a lanzar las peticiones. A partir de aquí es tarea de los programadores usar las funciones que ofrece. Algunas aplicaciones comerciales ya están pensadas para este tipo de aparatos y la integración viene realizada. En Sql SErver tenemos la posiblidad de cifrar a nivel de columna o de tabla siendo casi transparente a la hora de realizar las consultas, en IIS, podemos delegar la gestión de las conexiones SSL y sus certificados, igual que en Active Directory. Pero si estamos realizando nuestra propia aplicacion, tenemos mecanismos como usar directamente la libreria PKCS11 que implementan los drivers del aparato (configurada previamente) y realizar conexiones “a pelo” u optar por usar la Microsoft Crypto API con el proveedor de nCipher.

image

Optar por PCKS11 nos proporciona la ventaja de que tiene más métodos para interactuar con nCipher pero con la Crypto API, al ser practicamente un Wrapper, poder pasar de un proveedor a otro es totalmente transparente y vamos a conseguir una programación homogénea sea cual sea el proveedor de cifrado.

 

MSCAPI y Ncipher, manos a la obra.

Nota: para seguir los ejemplos tomamos como supuesto que el ‘word’ ya está configurado en la máquina windows que va ha hostear la aplicación

Nota 2: Los comandos de consola se encuentran en: $installationPathnfastbin, normalmente c:program files (x86)nCiphernfastbin

Cuando nos enfrentamos por primera vez este dispositivo, vamos a ver que la documentación en Internet es escasa (supongo que el alto precio de estos dispositivos no los hace populares cuanto menos), así que yo recomiendo, antes que nada, comprobar que realmente el dispositivo está instalado (que el Administrador no ha metido la pata).

para ello ejecutamos:

$csptest

La máquina empezará a probar en todos los providers disponibles ciertas operaciones de cifrado. Si encontramos una de las pruebas que haga referencia a ‘nCipher Enhanced Cryptographic Provider’, es que está instalado, sino, toca la instalación del provider.

image

Para usar nCipher desde nuestro código, tan sencillo como los ejemplos anteriores, pero especificando el provider de nCipher:

   1:  CspParameters cp = new CspParameters(1, "nCipher Enhanced Cryptographic Provider");
   2:          cp.KeyContainerName = "CarlosContainer";
   3:          cp.Flags = CspProviderFlags.UseMachineKeyStore;
   4:   
   5:          using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp))
   6:          {
   7:              ...
   8:          }

Con este código, si el contenedor no existe, ncipher lo creara en tiempo de ejecución, además del par de claves. Ejecutando el siguiente comando podemos comprobar que lo ha realizado correctamente.

$csputils.exe –U ALL –m

image

Su usamos –U es para insepccionar las cuentas de usuario, y –m las de máquina, es decir, que miramos en todos los sitios a ver que contenedores están creados en la máquina.

Las claves las podemos ver la utilidad keysafe que viene con el aparato:

image

El contenedor, si nos fijamos, no está protegido ya que no es una clave, lo que está protegido por modulo es la clave con ese nombre aleatorio que ha creado MSCAPI.

Para un checkeo de las claves

$keytst –m –p –x –s CarlosContainer (para ver las claves de ese contenedor claro)

image

 

Usar una clave protegida por token (una tarjeta) es un poco mas complicado ya que no podemos delegar en MSCAPI la creación de esta. Lo tenemos que hacer manualmente. La utilidad no permite crear una clave para MSCAPI, así que tenemos que crearla para PKCS11# y luego “meterla” o importarla en el contendor ya creado.

 

Para ello, creamos la clave con la utilidad keysafe de tipo PKCS11# y nos guardamos su ID, importante para la importación

 

image

Ahora creamos un contenedor, usando MSCAPI o “a mano”

$keytst –c MyNewContainer (to create a machine container, use –m too)

y nos guardamos su ID tambien.

Nota: Aqui hay un bug ya que si creamos el contenedor a mano, nos tendremos que mover a C:ProgramDatanCipherKey Management Datalocal para cambiar los permisos del nuevo fichero creado que representa al contenedor de la siguiente manera:

System -> full control

Administrators->full control

Users-> Read & Execute

Ahora importamos la clave en el contenedor, para ello, ejecutamos el siguiente comadno con los IDs guardados.

$cspimport –import –key NEWKEYID –appname pkcs11 NEWCONTAINERID exchange

image

No nos olvidemos importar la signature tambien

$cspimport –import –key NEWKEYID –appname pkcs11 NEWCONTAINERID signature

image

Ahora mismo,  para usar el contenedor es necesario meter el token (tarjeta) en el aparato, sino, no podremos cifrar/descifrar.

Si lo intentamos, nos aparecerá un asistente automaticamente en pantalla pidiendo el token. Para deshabilitarlo (en una aplicación web no tendria sentido una ventana en el servidor) tan sencillo como usar el flag no prompt a la hora de usar el contenedor.

cp.Flags = CspProviderFlags.UseMachineKeyStore | CspProviderFlags.NoPrompt;

En este ejemplo, usamos el contenedor de maquina sin el prompt. Si no está el token levantará una excepción (nada util y explicativa por cierto)

Conclusión

MSCAPI nos da un buen punto de partida para programar algo que requiera de criptografia. Además, la integración con aparatos externos es inmediata siempre y cuando estos tengan su propio provider. Lo malo de estos dispositivos, su alto precio y que la documentación es escasa, en concreto el modelo usado en las pruebas no da soporte para .NET, con lo que todo este articulo ha sido ha base de prueba error. Lo máximo que he encontrado ha sido una pregunta sin respuesta en StackOverflow, nada más.

Lineas futuras

Me gustaría completar este articulo probando otro dispositivo externo de otra marca, en concreto de Safenet[2], aunque no se si será posible. Este si ofrece soporte directo para .NET

[1]: http://www.thales-esecurity.com/Products/Hardware%20Security%20Modules/nShield%20Connect.aspx#2

[2]: http://www.safenet-inc.com/products/data-protection/data-encryption-control/datasecure/