开发者

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

开发者_如何学Cprocess(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?


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.


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.


What are you trying to do?

You might be better off looking at a standard message format and wrap the contents of your response in such a message, and sign that. OAuth comes to mind.

Also, if you enable SSL, the client can be sure that the contents of the response have not been tampered with.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜