Как сделать ключ HMAC_SHA256 из секретной строки, чтобы использовать его с JWT в jose4j?

Я хочу произвести JWT и подписать их с HMAC_SHA256. Для этой задачи я должен использовать jose4j. Я попытался сгенерировать ключ на основе секрета с:

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

но он генерирует 40-битный ключ, в то время как 512-битный требуется для подписи с использованием HMAC_SHA256.

  • Основная проблема - как подписать токены с помощью HMAC_SHA512, используя jose4j?
  • Проблема, созданная моим подходом, решение проблемы выше - как сделать секретный ключ длиной 512 бит на основе секретной строки?

4 ответа

В разделе 3.2 JWA / RFC 7518 говорится, что ключ с таким же размером, что и выходной хэш-код, должен использоваться с алгоритмами JWS HMAC SHA-2 (т. Е. 256 бит для "HS256", 384 бит /"HS384" и 512). бит /"HS512"). Обычно рекомендуется следовать этим советам IETF и NIST. Грубо говоря, безопасность HMAC зависит от размера вывода хеша и длины ключа, в зависимости от того, что меньше. Таким образом, использование байтов "секретного" в качестве ключа дает вам ключ длиной всего 48 бит и на практике обеспечивает значительно меньшую безопасность, чем даже тот, потому что это словарное слово, независимо от силы алгоритма HMAC SHA-2, которую вы используете. выбрал.

По умолчанию jose4j применяет минимальные размеры ключей, предписанные JWA/RFC 7518. Однако, как указывает Ганс, есть способы сказать jose4j ослабить требование к длине ключа. Это может быть сделано с JwtConsumer позвонив .setRelaxVerificationKeyValidation() на JwtConsumerBuilder и на JsonWebSignature непосредственно с .setDoKeyValidation(false), Ниже приведен краткий пример производства и использования JWT с использованием HMAC SHA256, который показывает оба варианта.

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

Обычный подход заключается в хешировании секрета перед использованием его в качестве ключа подписи.

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

Для безопасного «секрет» слишком короткий и небезопасный в качестве секретного ключа. Вы можете использовать приведенный ниже код для создания безопасного секретного ключа в качестве личного секретного ключа.

      //Generating a safe HS256 Secret key
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
String secretString = Encoders.BASE64.encode(key.getEncoded());
logger.info("Secret key: " + secretString);

Это будет работать:

      import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;

...
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
String secretString =  Encoders.BASE64.encode(key.getEncoded());
System.out.println("Kex: " + secretString);
...
Другие вопросы по тегам