代码之家  ›  专栏  ›  技术社区  ›  Pablo Fernandez

MD5签名HttpServletResponse

  •  6
  • Pablo Fernandez  · 技术社区  · 14 年前

    我在找一种检查 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.
    }
    

    有可能吗?

    3 回复  |  直到 14 年前
        1
  •  6
  •   BalusC    14 年前

    是的,那是可能的。您需要借助于 HttpServletResponseWrapper 其中你替换了 ServletOutputStream 使用一个自定义实现,它将字节写入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.
    
        2
  •  1
  •   Thomas Pornin    14 年前

    从技术上讲,“签名”一词是留给签名的,而哈希函数不计算这些。

    为了确保数据在传输过程中没有被更改,使用hash函数,那么您必须有一种安全的带外传输散列值的方法;在http头中添加散列值将不起作用,因为任何能够更改传输数据的人都可以随意重新计算散列值,并根据需要更改http头。

    有了密码学,你可以“集中”到一个可重用密钥的安全带外传输。如果客户机和服务器有一个共享的秘密值,假定攻击者不知道,那么缩写词是mac,如“消息身份验证代码”;通常的mac是 HMAC .

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

    这里的要点是,如果没有ssl,无论您做什么都不会阻止确定的攻击者;它只会使用cpu周期和网络带宽,并给您一种温暖模糊的感觉。

        3
  •  0
  •   Thilo    14 年前

    你想干什么?

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

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