Vous êtes sur la page 1sur 9

Crear aplicaciones ASP.

NET
seguras
Autenticacin, autorizacin y comunicacin
segura
Consulte la Pgina de entrada como punto de partida y para obtener una descripcin
completa del documento Crear aplicaciones ASP.NET seguras.
Resumen
En este artculo se describe cmo crear una biblioteca de clases administradas que
expone la funcionalidad DPAPI a las aplicaciones que desean cifrar datos por e!emplo
cadenas de conexin de base de datos y credenciales de cuentas.
Cmo: Crear una biblioteca DPAPI
Es frecuente que las aplicaciones "eb necesiten almacenar datos importantes para la
seguridad como cadenas de conexin de base de datos y credenciales de cuentas de
ser#icio en arc$i#os de configuracin de aplicaciones. Por ra%ones de seguridad este
tipo de informacin no debe almacenarse nunca en texto sin cifrar y debe cifrarse
siempre antes de su almacenamiento.
En este artculo se describe cmo crear una biblioteca de clases administradas que
encapsula las llamadas a la API de proteccin de datos &DPAPI Data Protection API'
para cifrar y descifrar los datos. Despu(s esta biblioteca se puede utili%ar en otras
aplicaciones administradas como las aplicaciones "eb A)P.*E+ los ser#icios "eb y las
aplicaciones de )er#icios Empresariales.
)i desea #er artculos relacionados en los que se utili%a la biblioteca DPAPI creada en
este documento consulte los siguientes artculos en la seccin ,eferencia de esta gua-
. /Cmo- 0tili%ar DPAPI &almac(n del equipo' en A)P.*E+/
. /Cmo- 0tili%ar DPAPI &almac(n del usuario' en A)P.*E+ con la aplicacin de
)er#icios Empresariales/

Notas
En el sistema operati#o 1icrosoft2 "indo3s2 4555 y los sistemas operati#os
posteriores se proporciona la API de proteccin de datos &DPAPI' de "in642 para
cifrar y descifrar datos.
DPAPI forma parte de la API de cifrado &Crypto API' y se implementa en el arc$i#o
crypt64.dll. Consta de dos m(todos- CryptProtectData y CryptUnprotectData.
DPAPI es especialmente 7til en cuanto que puede eliminar el problema de
administracin de cla#es al que estn expuestas las aplicaciones que utili%an
criptografa. )i bien el cifrado asegura que los datos son seguros deben tomarse
medidas adicionales para garanti%ar la seguridad de la cla#e. DPAPI utili%a la
contrase8a de la cuenta de usuario asociada al cdigo que llama a las funciones
9
DPAPI para obtener a partir de ella la cla#e de cifrado. Como resultado la cla#e la
administra el sistema operati#o en lugar de la aplicacin.
DPAPI puede traba!ar con el almac(n del equipo o el almac(n del usuario &lo que
requiere que se cargue un perfil de usuario'. De forma predeterminada DPAPI
utili%a el almac(n del usuario aunque se puede especificar que se utilice el
almac(n del equipo si se pasa el indicador C,:P+P,;+EC+<=;CA=<1AC>I*E
a las funciones DPAPI.
El uso del perfil de usuario permite un ni#el de seguridad adicional porque limita qui(n
puede tener acceso al secreto. )lo el usuario que cifra los datos puede
descifrarlos. )in embargo el uso del perfil de usuario requiere mayor traba!o de
desarrollo si se utili%a DPAPI en una aplicacin "eb A)P.*E+ porque es
necesario reali%ar pasos explcitos para cargar y descargar los perfiles de usuario
&A)P.*E+ no carga automticamente los perfiles de usuario'.
El uso del almac(n del equipo es ms fcil de desarrollar porque no requiere la
administracin de perfiles de usuario. )in embargo a menos que se utilice un
parmetro de entropa adicional la seguridad es menor porque cualquier usuario
del equipo puede descifrar los datos. &El parmetro de entropa es un #alor
aleatorio dise8ado para $acer ms difcil la posibilidad de descifrar el secreto.' El
problema de utili%ar un parmetro de entropa adicional es que debe almacenarse
de forma segura en la aplicacin lo que presenta otro problema de administracin
de cla#es.
Nota: si se utili%a DPAPI con el almac(n del equipo la cadena cifrada es
especfica de un equipo determinado y por lo tanto deben generarse los datos
cifrados en todos los equipos. *o copie los datos cifrados entre los equipos de un
grupo o cl7ster de ser#idores.
)i se utili%a DPAPI con el almac(n del usuario puede descifrar los datos en
cualquier equipo con un perfil de usuario m#il.

Requisitos
A continuacin se describen las recomendaciones de $ard3are soft3are infraestructura
de red conocimientos y )er#ice Pac? que se necesitan-
1icrosoft "indo3s 4555
)istema de desarrollo 1icrosoft @isual )tudio2 .*E+

Para lle#ar a cabo los procedimientos de este artculo tambi(n es necesario que tenga
conocimientos de la $erramienta de desarrollo 1icrosoft @isual CAB.
Resumen
En este artculo se incluyen los siguientes procedimientos-
9. Crear una biblioteca de clases en CA
4. Asignar un nombre seguro al ensamblado &opcional'

1. Crear una biblioteca de clases en C#
En este procedimiento se crea una biblioteca de clases en CA que expone los m(todos
Encrypt y Decrypt. Encapsula las llamadas a las funciones DPAPI de "in64.
Para crear una biblioteca de clases en C#
4
9. Inicie @isual )tudio .*E+ y cree un nue#o proyecto de biblioteca de clases de
@isual CA con el nombre DataProtection.
4. 0tilice el Explorador de soluciones para cambiar el nombre del arc$i#o class9.cs
por DataProtection.cs.
6. En el arc$i#o DataProtection.cs cambie el nombre class1 por DataProtector y
cambie el nombre del constructor predeterminado de igual manera.
C. En el Explorador de soluciones $aga clic con el botn secundario del mouse
&ratn' en DataProtection y a continuacin $aga clic en Propiedades.
D. >aga clic en la carpeta Propiedades de coni!uracin y configure Permitir
bloques de cdi!o no se!uros en "rue.
E. >aga clic en Aceptar para cerrar el cuadro de dilogo Propiedades.
F. Al principio del arc$i#o DataProtection.cs agregue las siguientes instrucciones
usin! deba!o de la instruccin usin! existente.
using System.Text;
using System.Runtime.InteropServices;
D. Agregue las siguientes instrucciones DllImport al principio de la clase
DataProtector para que se pueda llamar a las funciones DPAPI de "in64 y a la
funcin de utilidad #ormat$essa!e mediante PGIn#o?e.
[DllImport("Crypt32.dll", SetLastError=true,
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
private static extern bool CryptProtectData(
ref DATA_BLOB pDataIn,
String szDataDescr,
ref DATA_BLOB pOptionalEntropy,
IntPtr pvReserved,
ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
int dwFlags,
ref DATA_BLOB pDataOut);
[DllImport("Crypt32.dll", SetLastError=true,
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
private static extern bool CryptUnprotectData(
ref DATA_BLOB pDataIn,
String szDataDescr,
ref DATA_BLOB pOptionalEntropy,
IntPtr pvReserved,
ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
int dwFlags,
ref DATA_BLOB pDataOut);
[DllImport("kernel32.dll",
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
private unsafe static extern int FormatMessage(int dwFlags,
ref IntPtr lpSource,
int dwMessageId,
int dwLanguageId,
ref String lpBuffer, int nSize,
IntPtr *Arguments);
6
E. Agregue las siguientes definiciones de estructura y constantes utili%adas por las
funciones DPAPI.
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct DATA_BLOB
{
public int cbData;
public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct CRYPTPROTECT_PROMPTSTRUCT
{
public int cbSize;
public int dwPromptFlags;
public IntPtr hwndApp;
public String szPrompt;
}
static private IntPtr NullPtr = ((IntPtr)((int)(0)));
private const int CRYPTPROTECT_UI_FORBIDDEN = 0x1;
private const int CRYPTPROTECT_LOCAL_MACHINE = 0x4;
F. Agregue a la clase un tipo enumerado p7blico con el nombre %tore. )e usa para
indicar si DPAPI debe utili%arse en combinacin con el almac(n del equipo o el
almac(n del usuario.
public enum Store {USE_MACHINE_STORE = 1, USE_USER_STORE};
H. Agregue a la clase una #ariable miembro pri#ada de tipo %tore.
private Store store;
I. ,eemplace el constructor predeterminado de la clase por el siguiente constructor
que acepta un parmetro %tore y coloca el #alor suministrado en la #ariable
miembro pri#ada store.
public DataProtector(Store tempStore)
{
store = tempStore;
}
95. Agregue a la clase el siguiente m(todo &ncrypt p7blico.
public byte[] Encrypt(byte[] plainText, byte[] optionalEntropy)
{
bool retVal = false;
DATA_BLOB plainTextBlob = new DATA_BLOB();
C
DATA_BLOB cipherTextBlob = new DATA_BLOB();
DATA_BLOB entropyBlob = new DATA_BLOB();
CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();
InitPromptstruct(ref prompt);
int dwFlags;
try
{
try
{
int bytesSize = plainText.Length;
plainTextBlob.pbData = Marshal.AllocHGlobal(bytesSize);
if(IntPtr.Zero == plainTextBlob.pbData)
{
throw new Exception("Unable to allocate plaintext buffer.");
}
plainTextBlob.cbData = bytesSize;
Marshal.Copy(plainText, 0, plainTextBlob.pbData, bytesSize);
}
catch(Exception ex)
{
throw new Exception("Exception marshalling data. " + ex.Message);
}
if(Store.USE_MACHINE_STORE == store)
{//Using the machine store, should be providing entropy.
dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN;
//Check to see if the entropy is null
if(null == optionalEntropy)
{//Allocate something
optionalEntropy = new byte[0];
}
try
{
int bytesSize = optionalEntropy.Length;
entropyBlob.pbData = Marshal.AllocHGlobal(optionalEntropy.Length);;
if(IntPtr.Zero == entropyBlob.pbData)
{
throw new Exception("Unable to allocate entropy data buffer.");
}
Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize);
entropyBlob.cbData = bytesSize;
}
catch(Exception ex)
{
throw new Exception("Exception entropy marshalling data. " +
ex.Message);
}
}
D
else
{//Using the user store
dwFlags = CRYPTPROTECT_UI_FORBIDDEN;
}
retVal = CryptProtectData(ref plainTextBlob, "", ref entropyBlob,
IntPtr.Zero, ref prompt, dwFlags,
ref cipherTextBlob);
if(false == retVal)
{
throw new Exception("Encryption failed. " +
GetErrorMessage(Marshal.GetLastWin32Error()));
}
}
catch(Exception ex)
{
throw new Exception("Exception encrypting. " + ex.Message);
}
byte[] cipherText = new byte[cipherTextBlob.cbData];
Marshal.Copy(cipherTextBlob.pbData, cipherText, 0, cipherTextBlob.cbData);
return cipherText;
}
99. Agregue a la clase el siguiente m(todo Decrypt p7blico.
public byte[] Decrypt(byte[] cipherText, byte[] optionalEntropy)
{
bool retVal = false;
DATA_BLOB plainTextBlob = new DATA_BLOB();
DATA_BLOB cipherBlob = new DATA_BLOB();
CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();
InitPromptstruct(ref prompt);
try
{
try
{
int cipherTextSize = cipherText.Length;
cipherBlob.pbData = Marshal.AllocHGlobal(cipherTextSize);
if(IntPtr.Zero == cipherBlob.pbData)
{
throw new Exception("Unable to allocate cipherText buffer.");
}
cipherBlob.cbData = cipherTextSize;
Marshal.Copy(cipherText, 0, cipherBlob.pbData, cipherBlob.cbData);
}
catch(Exception ex)
{
throw new Exception("Exception marshalling data. " + ex.Message);
}
DATA_BLOB entropyBlob = new DATA_BLOB();
E
int dwFlags;
if(Store.USE_MACHINE_STORE == store)
{//Using the machine store, should be providing entropy.
dwFlags = CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN;
//Check to see if the entropy is null
if(null == optionalEntropy)
{//Allocate something
optionalEntropy = new byte[0];
}
try
{
int bytesSize = optionalEntropy.Length;
entropyBlob.pbData = Marshal.AllocHGlobal(bytesSize);
if(IntPtr.Zero == entropyBlob.pbData)
{
throw new Exception("Unable to allocate entropy buffer.");
}
entropyBlob.cbData = bytesSize;
Marshal.Copy(optionalEntropy, 0, entropyBlob.pbData, bytesSize);
}
catch(Exception ex)
{
throw new Exception("Exception entropy marshalling data. " +
ex.Message);
}
}
else
{//Using the user store
dwFlags = CRYPTPROTECT_UI_FORBIDDEN;
}
retVal = CryptUnprotectData(ref cipherBlob, null, ref entropyBlob,
IntPtr.Zero, ref prompt, dwFlags,
ref plainTextBlob);
if(false == retVal)
{
throw new Exception("Decryption failed. " +
GetErrorMessage(Marshal.GetLastWin32Error()));
}
//Free the blob and entropy.
if(IntPtr.Zero != cipherBlob.pbData)
{
Marshal.FreeHGlobal(cipherBlob.pbData);
}
if(IntPtr.Zero != entropyBlob.pbData)
{
Marshal.FreeHGlobal(entropyBlob.pbData);
}
}
catch(Exception ex)
F
{
throw new Exception("Exception decrypting. " + ex.Message);
}
byte[] plainText = new byte[plainTextBlob.cbData];
Marshal.Copy(plainTextBlob.pbData, plainText, 0, plainTextBlob.cbData);
return plainText;
}
94. Agregue a la clase los siguientes m(todos auxiliares pri#ados.
private void InitPromptstruct(ref CRYPTPROTECT_PROMPTSTRUCT ps)
{
ps.cbSize = Marshal.SizeOf(typeof(CRYPTPROTECT_PROMPTSTRUCT));
ps.dwPromptFlags = 0;
ps.hwndApp = NullPtr;
ps.szPrompt = null;
}
private unsafe static String GetErrorMessage(int errorCode)
{
int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
int messageSize = 255;
String lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS;
IntPtr ptrlpSource = new IntPtr();
IntPtr prtArguments = new IntPtr();
int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0,
ref lpMsgBuf, messageSize, &prtArguments);
if(0 == retVal)
{
throw new Exception("Failed to format message for error code " +
errorCode + ". ");
}
return lpMsgBuf;
}
96. En el men7 'enerar $aga clic en 'enerar solucin.

(. Asi!nar un nombre se!uro al ensamblado )opcional*
)i la biblioteca de clases DPAPI administradas se #a a llamar desde una aplicacin de
)er#icios Empresariales &que debe tener un nombre seguro' la biblioteca de clases
DPAPI tambi(n debe tener un nombre seguro. En este procedimiento se crea un nombre
seguro para la biblioteca de clases.
H
)i la biblioteca de clases DPAPI administradas se #a a llamar directamente desde una
aplicacin "eb A)P.*E+ &que no tiene un nombre seguro' puede omitir este
procedimiento.
Para asi!nar un nombre se!uro al ensamblado
9. Abra una #entana de comandos y cambie el directorio a la carpeta del proyecto
DataProtection.
4. 0tilice la $erramienta sn.exe para generar un par de cla#es que se utili%ar para
firmar el ensamblado.
sn -k dataprotection.snk
6. @uel#a a @isual )tudio .*E+ y abra Assemblyinfo.cs.
C. Jusque el atributo Assembly+ey#ile y agregue una ruta de acceso al arc$i#o de
cla#es en la carpeta del proyecto.
[assembly: AssemblyKeyFile(@"..\..\dataprotection.snk")]
D. En el men7 'enerar $aga clic en 'enerar solucin.

Reerencias
Para obtener ms informacin consulte los siguientes artculos relacionados-
. /Cmo- 0tili%ar DPAPI &almac(n del equipo' en A)P.*E+/ en la seccin ,eferencia
de esta gua
. /Cmo- 0tili%ar DPAPI &almac(n del usuario' en A)P.*E+ con la aplicacin de
)er#icios Empresariales/ en la seccin ,eferencia de esta gua


I

Vous aimerez peut-être aussi