将字符串哈希码用作etag是否安全

时间:2015-03-10 08:59:09

标签: java jersey probability

给定一个输入参数,对于rest api,我想将哈希码用作etag。 json响应发生变化的概率是多少,而哈希码也是一样的?

或者有更好的方法吗?

@GET
    public Response getConfigurationForView(@PathParam("in1") String in1, @Context Request request) throws Exception {
        String jsonResponse = getJsonResponse(in1);
        EntityTag etag = new EntityTag(Integer.toString(in1.hashCode()) + "-" + Integer.toString(jsonResponse.hashCode()));
        ResponseBuilder builder = request.evaluatePreconditions(etag);


         if(builder == null){
             builder = Response.ok(jsonResponse, MediaType.APPLICATION_JSON);
             builder.tag(etag);
         }

        return builder.build();
    }

2 个答案:

答案 0 :(得分:3)

鉴于你只有40亿个可能的哈希码用于各种各样的字符串,你有可能最终遇到ETag冲突。

了解String.hashCode()的实施方式:

        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;

- 你甚至可以自己想出可能的碰撞。例如,""(空字符串)和"\0"(仅包含\0字符的字符串)将为您提供相同的hashCode 0。

我建议您使用SHA1哈希(或MD5,但请先在securityCPU running time上查看这些说明。)

如果您继续使用SHA1哈希,您的代码可能如下所示:

public static String calculateEtag(final String s) throws java.security.NoSuchAlgorithmException {
    final java.nio.ByteBuffer buf = java.nio.charset.StandardCharsets.UTF_8.encode(s);
    final java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA1");
    buf.mark();
    digest.update(buf);
    buf.reset();
    return String.format("W/\"%s\"", javax.xml.bind.DatatypeConverter.printHexBinary(digest.digest()));
}

这将产生与sha1sum实用程序相同的输出。 您也可以使用BigInteger将字节缓冲区转换为十六进制字符串:

new BigInteger(1, digest.digest()).toString(16)

- 但javax.xml.bind.DatatypeConverter.printHexBinary()要快几倍。

答案 1 :(得分:0)

如果你可以使用jdk8 +和Google Guava,你可以试试

final String myETag = ( (Set<Object>) this.setOfChangeableProperties ).stream()
  .filter( Objects::nonNull )
  .map( Objects::toString )
  .reduce( (a,b) -> a.concat(b) )
  .map( s -> Hashing.md5().hashUnencodedChars( s ).toString() )
  .orElse( "nothing of interest to hash!" )