使用字符串哈希码作为 etag 是否安全
is it safe to use string hashcode as etag
给定某个输入参数,休息一下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();
}
考虑到对于所有字符串,您只有 40 亿个可能的哈希码,因此您最终会遇到 ETag 冲突的可能性很大。
查看 String.hashCode()
的实现方式:
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
-- 你甚至可以自己想出可能发生的碰撞。例如,""
(空字符串)和 "[=15=]"
(仅包含 [=16=]
字符的字符串)将为您提供相同的 hashCode
0.
我建议您使用 SHA1 哈希(或 MD5,但请先参阅 security and CPU 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()
快了好几倍。
如果你会用jdk8+和GoogleGuava,你可以试试
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!" )
给定某个输入参数,休息一下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();
}
考虑到对于所有字符串,您只有 40 亿个可能的哈希码,因此您最终会遇到 ETag 冲突的可能性很大。
查看 String.hashCode()
的实现方式:
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
-- 你甚至可以自己想出可能发生的碰撞。例如,""
(空字符串)和 "[=15=]"
(仅包含 [=16=]
字符的字符串)将为您提供相同的 hashCode
0.
我建议您使用 SHA1 哈希(或 MD5,但请先参阅 security and CPU 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()
快了好几倍。
如果你会用jdk8+和GoogleGuava,你可以试试
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!" )