lunes, 17 de enero de 2011

Devolver Fichero desde servidor al cliente.

En ocasiones, queremos hacer que nuestros usuarios descarguen el típico fichero Excel, PDF, Word, etc., pero no queremos que estos se abran en la misma ventana del explorer, sino que se les dé la opción de guardar el fichero o que se abra en la aplicación correspondiente.

Conseguirlo es muy sencillo, y para ello no tenemos más que "engañar" al navegador y decirle que lo que se está descargando no es un Excel, PDF o Word en particular, sino que se trata de una serie de bytes que se tiene que bajar sin rechistar 

El siguiente código hace esa función:

public static void descarga(string filepath, string filename)
{
            Response.Clear();
            Response.ContentType = "application/octet-stream";
            Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
            Response.Flush();
            Response.WriteFile(filepath);
            Response.End();
}


Esta función se puede ejecutar desde donde queramos (lo típico es llamarlo cuando el usuario presiona un botón). básicamente, lo que hace es:

1.- Limpia el contenido de salida.
2.- Le cambia el contentType a tipo octet... aquí es donde "engañamos al navegador".
3.- Le añadimos la cabecera Content-Disposition y le damos un nombre al fichero. Esto es opcional, y lo que hace es dar el nombre que queremos que aparezca si el usuario decide guardar el fichero.
4.- Manda la info que tenemos hasta ahora (la única cabecera que hemos añadido) a la salida hacia el usuario.
5.- Mandamos el fichero en sí desde Response.WriteFile(filepath), donde, obviamente, filepath es el path interno del fichero en nuestro servidor.
6.- Enviamos todo y terminamos la ejecución de la página.

miércoles, 12 de enero de 2011

Server Application Unavailable

Cuando se produzca el error: 


The web application you are attempting to access on this web server is currently unavailable. Please hit the “Refresh” button in your web browser to retry your request. 

Administrator Note: An error message detailing the cause of this specific request failure 
can be found in the application event log of the web server. Please review this log entry to 
discover what caused this error to occur. 


Se debe a que IIS 6 permite una caracteristica llamada Application Pool. Esta permite aislar los procesos en los que corren nuestras aplicaciones, de manera que si ocurre un error con alguno, no afecta a los demas que no corran en el mismo application pool. Resulta que el IIS permite que varias versiones del framework vivan y se ejecuten felices en el mismo servidor, pero no en el mismo proceso (es decir, en el mismo application pool). La solucion es bien sencilla. Crear un nuevo application pool en el servidor para cada framework. 

Debemos de abrir la administracion de Internet Information Services. Boton derecho sobre Grupos de aplicaciones -> Nuevo -> Grupo de aplicaciones -> Ponemos el nombre en el Id de Grupo- Despues vamos al Sitios Web -> Seleccionamos el sitio -> Propiedades -> Directorio particular -> Grupo de aplciaciones -> Seleccionamos la que hemos creado. 

Reiniciamos el sitio Web. TACHAN!!!!!!!!!!! FUNCIONA. 

viernes, 7 de enero de 2011

Autentificación con DNIe en IIS7

Esta guía permite configurar un servidor IIS7 para que solicite certificados de autenticación del DNIe (DNI electrónico). Buena parte de lo que aquí cuento forma parte de un piloto desarrollado por Paco del Campo.
Algunas cosas que rodean al DNIe parecen seguir el principio de security through obscurity. Lástima que el portal del DNI Electrónico no tenga información sobre los dos perfiles fundamentales en la difusión de los servicios con autenticación digital, los programadores y los técnicos de sistemas.

Posibles usos de los certificados

Un sitio web puede utilizar certificados del DNIe para identificar de forma completamente segura a un usuario de un servicio electrónico.
Si los usuarios que acceden son totalmente desconocidos para nuestra organización (no tenemos cuenta de usuario o forma de identificación equivalente), el certificado nos permite conocer la identidad del solicitante, su nombre y su NIF. Con estos datos, podemos asociarlos a un posible registro existente en nuestra base de datos o crear un nuevo registro.
Si por contra, el usuario del DNIe tiene una cuenta de usuario en nuestro directorio, podemos decidir asociar la clave pública de este certificado con esa cuenta de usuario. De esta forma, tendremos un método de autenticación de alta seguridad, que podría reemplazar al tradicional usuario/contraseña.

Pasos a seguir

El proceso consta de las siguientes fases:
  • Registrar en el servidor los certificados de la CA raíz y de la CA subordinada del DNIe
  • Instruir a IIS7 para que solicite certificados de cliente,
  • Definir el tipo de equivalencia entre certificados y cuentas de usuario
  • Decidir qué política se va a seguir con la validación de certificados revocados
Antes de empezar, es fundamental asegurarse de que el cliente en el que está el lector de DNIe funciona correctamente, validándolo con alguno de los servicios de dnielectronico.es.

Registro de CAs

El servidor IIS debe tener registradas las CA raíz y subordinadas que permitan validar los certificados que queremos emplear, en nuestro, la CA Raíz y la CA subordinada 001.
Se descargan en el servidor y se da doble clic para instalarlas. Dentro del asistente para importarlas, es importante elegir la opción de selección manual del almacén de certificados, y la opción 'mostrar almacenes físicos'. De esta forma, siendo administradores de la máquina, podemos registrar los certificados en el almacén de sistema, de forma que otros servicios puedan verlos. Si no somos administradores, o UAC está activo, los certificados se registrarán para este usuario.
En de la CA raíz se meterá en el de "Entidades de certificación raíz de confianza" y el de la subordinada en "Entidades de certificación intermedias".
image image

Configuración de IIS7

Tenemos que activar SSL en el servidor web deseado, para lo que se seguirán los pasos de generar o solicitar un certificado y activar el binding del servidor al puerto 443. Es importante comprobar en este punto que somos capaces de establecer una conexión segura al servidor.
El siguiente paso es indicar a IIS7 que queremos utilizar certificados de cliente en el proceso de establecimiento de la conexión SSL. Esto se realiza desde la configuración de SSL de IIS.
image
También debemos revisar la configuración de autenticación, para asegurarnos, con independencia del tratamiento de los certificados, que nuestro sitio tiene permisos correctos de acceso, que se controlarán limitando los permisos de las carpetas IIS correspondientes.
image 
Ahora tenemos que decidir cómo hacer la equivalencia entre certificados y cuentas de usuario de la máquina. Hay dos posibilidades:
  • Uno a uno (oneToOneMappings): el usuario del certificado tiene ya una cuenta en nuestro dominio, por lo hacemos que cada certificado equivalga a una cuenta del dominio o de la máquina. La asociación se hace con la clave pública del certificado.
  • Muchos a uno (manyToOneMappings): hacemos que todos los certificados que cumplan un determinado criterio se asocien a una cuenta del dominio o de la máquina, que es la que finalmente accederá a los contenidos. Este escenario es el más habitual cuando no conocemos a priori los certificados que vamos a manejar, en el que cualquier certificado válido puede ser usado para acceder a nuestro servicio.
La configuración de estas opciones no se puede realizar directamente desde la administración de IIS, sino editando los ficheros XML del directorio \Windows\System32\inetsrv\config, en concreto el fichero applicationHost.config.
Sin embargo, hay una herramienta tremendamente útil que nos ayudará, el Administration Pack para IIS (versión beta, para entornos x32, hay otra descarga para x64), que ofrece "Configuration Editor", una herramienta que se integra con la consola de administración y que permite editar cualquier parámetros de los ficheros .config generales o específicos de una web, explorar las posibles configuraciones (analiza el esquema de los ficheros), buscar... Una vez instalado, aparece en la parte de abajo de las herramientas de administración.
image
Vamos a realizar una configuración manyToOne, por lo que abriremos en Configuration Editor en el servidor deseado, y exploramos la clave system.webServer/security/authentication/iisClientCertificateMappingAuthentication.
En esta página se explica cómo hacer el mapeo oneToOne, similar a lo aquí expuesto, salvo por la necesidad de incorporar la clave pública del certificado que se desea asociar.
image
Activamos la opción manyToOneCertificateMappingsEnabled y entramos en la opción ManyToOneMappings, en la que definiremos cada una de las asociaciones.
image
Creamos una asociación, dándole un nombre descriptivo, e introduciendo los datos de una cuenta de usuario que será la que acceda a los contenidos para los usuarios que se validen con el certificado correspondiente. Es conveniente crear una cuenta sin ningún tipo de privilegio, y añadirla a la seguridad de acceso de la carpeta IIS que tenga los contenidos.
image
Ahora tenemos que crear una o varias reglas, que serán los patrones de certificados que aceptamos  para esta asociación. Podemos hacer reglas muy generales (todos los certificados de una autoridad) u otras más específicas. Para ello, elegimos qué campo/subcampo del certificado queremos usar y cuál debe ser su valor. Se pueden usar wildcards como *.
image
En nuestro caso, el certificateField es el Issuer, y dentro del mismo, elegimos el certificateSubField OU, que en el caso del DNIe es DNIE.
image
Aceptamos todos los cambios realizados.

Validación de certificados revocados

Por defecto IIS tratará de validar el estado de revocación de los certificados empleados, pero por defecto, no existe un servicio OCSP en el que validarlos.
El Ministerio de Industria tiene un servicio OCSP operativo en http://ocsp.ctpa.mityc.es. Para poder usarlo, debemos modificar las propiedades de la CA intermedia AC DNIE 001. Para modificarlo, abrimos una mmc de certificados sobre la cuenta de máquina y editamos las propiedades (con botón derecho sobre la CA) de AC DNIE 001, añadiendo un servicio OCSP adicional a la dirección anterior.
image
La otra posibilidad es desactivar la validación de las listas de revocación, en caso de que no tengamos acceso a la red o queramos que sea así. Tenemos que usar la herramienta netsh para modificar sus propiedades.
Arrancamos netsh y usamos el comando http para entrar en este contexto. Con el comando show sslcert veremos la relación completa de endpoints http definidos, alguno de los cuales tiene ssl activo. Podemos ver uno concreto con la opción ipport.
Es importante copiar toda la configuración que se muestre del que queremos cambiar, porque tendremos que borrarlo y crearlo de nuevo (se puede hacer show, delete y add, pero no set ¿?).
Vemos la configuración completa:
image
Lo borramos con:
delete sslcert ipport=192.168.0.103:443
Y lo creamos de nuevo, cambiando la opción verifyclientcertrevocation.
add sslcert ipport=192.168.0.103:443 certhash=16d044b6c802473cc3c59c204e8f92928b3dd011
    appid={4dc3e181-e14b-4a21-b022-59fc669b0914} certstorename=MY
    verifyclientcertrevocation=disable

Y comprobamos todo...

Creamos una página default.aspx en el servidor, con un código que nos permita recoger y mostrar los datos del certificado, para así asegurarnos que todo funciona.
<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>


<body>
<h2>Información en el certificado</h2>
<p>        
<%
HttpClientCertificate cs = Request.ClientCertificate;


Response.Write("ClientCertificate Settings:<br>");
Response.Write("Certificate = " + cs.Certificate + "<br>");
Response.Write("Cookie = " + cs.Cookie + "<br>");
Response.Write("Flags = " + cs.Flags + "<br>");
Response.Write("IsPresent = " + cs.IsPresent + "<br>");
Response.Write("Issuer = " + cs.Issuer + "<br>");
Response.Write("IsValid = " + cs.IsValid + "<br>");
Response.Write("KeySize = " + cs.KeySize + "<br>");
Response.Write("SecretKeySize = " + cs.SecretKeySize + "<br>");
Response.Write("SerialNumber = " + cs.SerialNumber + "<br>");
Response.Write("ServerIssuer = " + cs.ServerIssuer + "<br>");
Response.Write("ServerSubject = " + cs.ServerSubject + "<br>");
Response.Write("Subject = " + cs.Subject + "<br>");
Response.Write("ValidFrom = " + cs.ValidFrom + "<br>");
Response.Write("ValidUntil = " + cs.ValidUntil + "<br>");
Response.Write("What's this = " + cs.ToString() + "<br>");


%>        
</p>
</body>
</html>


Si algo falla, recibiremos mensajes de error en el cliente. Lo mejor es comprobar en los ficheros de log del servidor (\inetpub\logs\) para ver el código y subcódigo de error (ej. 403.16) y así analizar qué es lo que está fallando.


Y si todo va bien:


image