Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MD5 Signing a HttpServletResponse

I'm looking for a way to inspect the contents of a HttpServletResponse to sign them with a MD5 hash.

The pseudocode might look like this

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.
}

Is that possible?

like image 856
Pablo Fernandez Avatar asked Feb 02 '10 00:02

Pablo Fernandez


2 Answers

Yes, that's possible. You need to decorate the response with help of HttpServletResponseWrapper wherein you replace the ServletOutputStream with a custom implementation which writes the bytes to both the MD5 digest and the "original" outputstream. Finally provide an accessor to obtain the final MD5 sum.

Update I just for fun played a bit round it, here's a kickoff example:

The response wrapper:

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();
    }

}

The MD5 outputstream:

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();
    }

}

How to use it:

// 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.
like image 151
BalusC Avatar answered Sep 22 '22 02:09

BalusC


Technically, the term "signature" is reserved to, well, signatures, and hash functions do not compute those.

To ensure that data was not altered in transit, with a hash function, then you must have a secure out-of-band way of transmitting the hash value; adding the hash value within the HTTP headers will not do, because anybody able to alter the transmitted data may recompute the hash at will, and alter the HTTP headers as it sees fit.

With cryptography, you can "concentrate" that secure out-of-band transmission into a reusable key. If client and server have a shared secret value, unknown to the supposed attacker, then the acronym is MAC, as in "Message Authentication Code"; a usual MAC is HMAC.

In many practical situations, a MAC cannot be used, because a MAC requires a shared secret, and a secret which shared too many times is not really secret anymore. Every secret holder has the power to recompute MAC. If every client knows the secret, then basically it is not a secret and it is safe to assume that the attacker also knows it. Hence, you can go a step further and use digital signatures (real ones, those which use RSA, DSS, ECDSA...) in which the server uses a private key (which only the server knows) and the clients know only of the corresponding public key. Knowledge of the public key is enough to verify signatures, but not to produce new ones, and the private key cannot be recomputed from the public key (although they are mathematically linked to each other). However, implementing a digital signature and using it properly is much more difficult than it is usually assumed; your best bet is then to use an already debugged protocol, with existing implementations, and that protocol happens to be called "SSL".

The point here is that without SSL, chances are that whatever you do will not deter a determined attacker; it will just use CPU cycles and network bandwidth, and give you a warm fuzzy feeling.

like image 45
Thomas Pornin Avatar answered Sep 21 '22 02:09

Thomas Pornin