Java MD5签署HttpServletResponse

Java MD5签署HttpServletResponse,java,servlets,hash,Java,Servlets,Hash,我正在寻找一种方法来检查HttpServletResponse的内容,并使用MD5哈希对其进行签名 伪代码可能如下所示 process(Response response, Request request){ defaultProcessingFor(response,request); dispatcher.handle(response,request); // Here I want to read the contents of the Response object (now f

我正在寻找一种方法来检查
HttpServletResponse
的内容,并使用MD5哈希对其进行签名

伪代码可能如下所示

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

有可能吗?

是的,有可能。您需要在的帮助下对响应进行修饰,其中使用一个自定义实现替换,该实现将字节写入MD5摘要和“原始”输出流。最后提供一个访问器以获得最终MD5和

更新我只是为了好玩,玩了一圈,下面是一个开始的例子:

响应包装器:

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

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

}
如何使用它:

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

你想干什么

您最好使用标准的消息格式,将响应的内容包装在这样的消息中,并签名。我想到了OAuth

此外,如果启用SSL,客户端可以确保响应的内容没有被篡改。

技术上,术语“签名”保留给签名,而哈希函数不会计算这些内容

为了确保数据在传输过程中不被更改,使用散列函数,则必须使用安全的带外方式传输散列值;在HTTP头中添加哈希值是不行的,因为任何能够更改传输数据的人都可以随意重新计算哈希值,并根据需要更改HTTP头

使用加密技术,您可以将安全带外传输“集中”到可重用密钥中。如果客户端和服务器有一个共享的秘密值,假定攻击者不知道该值,则首字母缩写为MAC,如“消息身份验证码”;通常的MAC是

在许多实际情况下,MAC无法使用,因为MAC需要共享机密,而共享次数过多的机密不再是真正的机密。每个秘密持有者都有权重新计算MAC。如果每个客户端都知道这个秘密,那么基本上它不是一个秘密,可以安全地假设攻击者也知道它。因此,您可以更进一步,使用数字签名(真实签名,使用RSA、DSS、ECDSA等的签名),其中服务器使用私钥(只有服务器知道),客户端只知道相应的公钥。对公钥的了解足以验证签名,但不能生成新的签名,私钥不能从公钥重新计算(尽管它们在数学上相互关联)。然而,实现数字签名并正确使用它比通常假设的要困难得多;然后,您最好使用已经调试好的协议和现有的实现,该协议恰好被称为“SSL”


这里的要点是,如果没有SSL,那么无论您做什么都无法阻止坚定的攻击者;它只会占用CPU周期和网络带宽,给你一种温暖的模糊感觉。

@Pablo:你还需要在标题上签名吗?@Thilo只需要内容就可以了OK@BalusC我仍然不知道这是否是我将要采用的方法,但该类
HttpServletResponseWrapper
很好(+1)实际上,我使用了另一种解决方案:传递一个PrintWriter而不是HttpServletResponse,并让我的“操作”写在上面。但这是一个很好的解决方案,在大多数情况下都会更好。@BalusC,您能允许我们在没有属性的情况下使用此代码吗?CC0许可证将是理想的。我有一个工作应用程序。我想在响应中添加一个签名哈希,以确保内容在传输过程中未被修改。@Pablo:也许只需打开SSL?如果有什么东西正在修改内容,您确信它不会用新的内容替换您返回的MD5摘要吗?这里的经典方法需要一些只有你和接收者知道的秘密。例如,收件人提前私下建立的MD5摘要种子字符串。@Dilum我已经这样做了。这与实际问题无关,这就是为什么我在客户端和服务器共享一个共同的秘密之前没有说过这一点。然后使用该秘密对MD5进行签名(事实上,我也使用HMAC-SHA算法进行签名)。我没有具体说明这些,因为这与问题本身无关。