15

我想生成 JWT 并使用 HMAC_SHA256 对其进行签名。对于该任务,我必须使用jose4j。我试图根据秘密生成密钥:

SecretKeySpec key = new SecretKeySpec(("secret").getBytes("UTF-8"), AlgorithmIdentifiers.HMAC_SHA512);

但它生成 40 位密钥,而使用 HMAC_SHA256 进行签名需要 512 位密钥。

  • 主要问题 - 如何使用 jose4j 使用 HMAC_SHA512 签署令牌?
  • 我的方法解决上述问题所产生的问题 - 如何根据秘密字符串制作 512 位长的密钥?
4

3 回答 3

20

Section 3.2 of JWA / RFC 7518 says that a key of the same size as the hash output or larger must be used with the JWS HMAC SHA-2 algorithms (i.e, 256 bits for "HS256", 384bits/"HS384", & 512 bits/"HS512"). It's generally a good idea to follow this advice from the IETF and NIST. Roughly speaking the security of an HMAC comes from the size of the hash output and the key length, whichever is smaller. So using the bytes of "secret" as the key gives you a key that's only 48 bits long and, in practice, provides considerably less security than even that because it's a dictionary word, regardless of the strength of the HMAC SHA-2 algorithm you chose.

By default jose4j enforces the minimum key sizes mandated by JWA/RFC 7518. However, as Hans points out, there are ways to tell jose4j to relax the key length requirement. This can be done with JwtConsumer by calling .setRelaxVerificationKeyValidation() on JwtConsumerBuilder and on JsonWebSignature directly with .setDoKeyValidation(false). Below is a quick example producing and consuming a JWT using HMAC SHA256 that shows both.

JwtClaims claims = new JwtClaims();
claims.setExpirationTimeMinutesInTheFuture(5);
claims.setSubject("foki");
claims.setIssuer("the issuer");
claims.setAudience("the audience");

String secret = "secret";
Key key = new HmacKey(secret.getBytes("UTF-8"));

JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
jws.setKey(key);
jws.setDoKeyValidation(false); // relaxes the key length requirement

String jwt = jws.getCompactSerialization();
System.out.println(jwt);

JwtConsumer jwtConsumer = new JwtConsumerBuilder()
        .setRequireExpirationTime()
        .setAllowedClockSkewInSeconds(30)
        .setRequireSubject()
        .setExpectedIssuer("the issuer")
        .setExpectedAudience("the audience")
        .setVerificationKey(key)
        .setRelaxVerificationKeyValidation() // relaxes key length requirement 
        .build();

JwtClaims processedClaims = jwtConsumer.processToClaims(jwt);
System.out.println(processedClaims);
于 2015-08-23T14:00:50.993 回答
9

一种常见的方法是在将秘密用作签名密钥之前对其进行哈希处理。

MessageDigest md = MessageDigest.getInstance("SHA-256");
String secret = "secret";
md.update(secret.getBytes("UTF-8"));
byte[] key = md.digest();

另一种方法是放宽对密钥长度的要求,例如:

JwtConsumer jwtConsumer = new JwtConsumerBuilder()
     .setVerificationKey(new HmacKey(secret.getBytes())) 
     .setRelaxVerificationKeyValidation() // allow shorter HMAC keys when used w/ HSxxx algs 
     .build();
于 2015-08-16T19:07:49.307 回答
0

为了安全起见,“秘密”作为密钥太短且不安全。您可以使用以下代码生成一个安全的密钥作为您的个人密钥。

//Generating a safe HS256 Secret key
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
String secretString = Encoders.BASE64.encode(key.getEncoded());
logger.info("Secret key: " + secretString);
于 2022-02-16T21:38:36.217 回答