2010-02-02 7 views
5

Estoy buscando una forma de inspeccionar el contenido de un HttpServletResponse para firmarlo con un hash MD5.MD5 Firmando una HttpServletResponse

El pseudocódigo podría tener este aspecto

process(Response response, Request request){ 

defaultProcessingFor(response,request); 

dispatcher.handle(response,request); 

// Here I want to read the contents of the Response object (now filled with data) to create a MD5 hash with them and add it to a header. 
} 

¿Es posible?

Respuesta

6

Sí, eso es posible. Necesita decorar la respuesta con la ayuda de HttpServletResponseWrapper en la que reemplaza el ServletOutputStream con una implementación personalizada que escribe los bytes tanto en el resumen MD5 como en el tren de salida "original". Finalmente proporcione un acceso para obtener la suma final de MD5.

actualización que sólo por diversión jugó un poco alrededor de ella, aquí hay un ejemplo patada de salida:

La envoltura respuesta:

public class MD5ServletResponse extends HttpServletResponseWrapper { 

    private final MD5ServletOutputStream output; 
    private final PrintWriter writer; 

    public MD5ServletResponse(HttpServletResponse response) throws IOException { 
     super(response); 
     output = new MD5ServletOutputStream(response.getOutputStream()); 
     writer = new PrintWriter(output, true); 
    } 

    public PrintWriter getWriter() throws IOException { 
     return writer; 
    } 

    public ServletOutputStream getOutputStream() throws IOException { 
     return output; 
    } 

    public byte[] getHash() { 
     return output.getHash(); 
    } 

} 

OutputStream MD5:

public class MD5ServletOutputStream extends ServletOutputStream { 

    private final ServletOutputStream output; 
    private final MessageDigest md5; 

    { 
     try { 
      md5 = MessageDigest.getInstance("MD5"); 
     } catch (NoSuchAlgorithmException e) { 
      throw new ExceptionInInitializerError(e); 
     } 
    } 

    public MD5ServletOutputStream(ServletOutputStream output) { 
     this.output = output; 
    } 

    public void write(int i) throws IOException { 
     byte[] b = { (byte) i }; 
     md5.update(b); 
     output.write(b, 0, 1); 
    } 

    public byte[] getHash() { 
     return md5.digest(); 
    } 

} 

Cómo úselo:

// Wrap original response with it: 
MD5ServletResponse md5response = new MD5ServletResponse(response); 

// Now just use md5response instead or response, e.g.: 
dispatcher.handle(request, md5response); 

// Then get the hash, e.g.: 
byte[] hash = md5response.getHash(); 
StringBuilder hashAsHexString = new StringBuilder(hash.length * 2); 
for (byte b : hash) { 
    hashAsHexString.append(String.format("%02x", b)); 
} 
System.out.println(hashAsHexString); // Example af28cb895a479397f12083d1419d34e7. 
+0

@Pablo: ¿También necesita firmar los encabezados? – Thilo

+0

@Thilo solo el contenido estaría bien –

+0

@BalusC aún no sé si ese es el enfoque que voy a tomar, pero esa clase 'HttpServletResponseWrapper' es __NICE__ (+1) –

0

¿Qué estás tratando de hacer?

Puede que sea mejor mirar un formato de mensaje estándar y envolver el contenido de su respuesta en dicho mensaje, y firmarlo. OAuth viene a la mente.

Además, si habilita SSL, el cliente puede estar seguro de que el contenido de la respuesta no se ha alterado.

+0

Tengo una aplicación en funcionamiento. Quiero agregar una firma - hash a la respuesta que asegura que el contenido no fue modificado en tránsito. –

+0

@Pablo: ¿tal vez solo activar SSL? – Thilo

+0

lamentablemente no es una opción :( –

1

Técnicamente, el término "firma" está reservado para, bueno, las firmas y las funciones hash no las calculan.

Para garantizar que los datos no se alteraron durante el tránsito, con una función hash, entonces debe tener una forma segura fuera de banda de transmitir el valor hash; no se puede agregar el valor hash dentro de los encabezados HTTP, ya que cualquiera que pueda alterar los datos transmitidos puede volver a calcular el hash a voluntad y modificar los encabezados HTTP como lo considere oportuno.

Con la criptografía, puede "concentrar" esa transmisión segura fuera de banda en una clave reutilizable. Si el cliente y el servidor tienen un valor secreto compartido, desconocido para el supuesto atacante, entonces el acrónimo es MAC, como en "Código de autenticación de mensaje"; un MAC habitual es HMAC.

En muchas situaciones prácticas, no se puede usar un MAC, porque un MAC requiere un secreto compartido, y un secreto que se comparte demasiadas veces ya no es realmente secreto. Cada titular secreto tiene el poder de recalcular MAC. Si cada cliente conoce el secreto, básicamente no es un secreto y es seguro asumir que el atacante también lo sabe. Por lo tanto, puede ir un paso más allá y utilizar las firmas digitales (las reales, aquellas que usan RSA, DSS, ECDSA ...) en las que el servidor usa una clave privada (que solo el servidor conoce) y los clientes saber solamente de la clave pública correspondiente. El conocimiento de la clave pública es suficiente para verificar firmas, pero no para producir nuevas, y la clave privada no puede ser recalculada desde la clave pública (aunque están matemáticamente vinculadas entre sí).Sin embargo, implementar una firma digital y usarla adecuadamente es mucho más difícil de lo que normalmente se supone; su mejor opción es usar un protocolo ya depurado, con implementaciones existentes, y ese protocolo se llama "SSL".

El punto aquí es que sin SSL, es probable que lo que haga no disuada a un atacante determinado; solo usará ciclos de CPU y ancho de banda de red, y le dará una sensación cálida y difusa.

+0

Tanto el cliente como el servidor comparte un _secret_. El MD5 es entonces _signed_ usando ese secreto (de hecho, yo también uso el algoritmo HMAC-SHA para la firma). No los especificaba porque no tenía que ver con la pregunta per se. –