web-dev-qa-db-fra.com

java.lang.IllegalArgumentException: une clé de signature doit être spécifiée si le fichier JWT spécifié est signé numériquement.

Je cherche à implémenter JWT dans mon application car je fais de la R & D dessus en prenant une référence à partir de: https://stormpath.com/blog/jwt-Java-create-verify . J'ai réussi à implémenter la méthode generateToken() lorsque j'essaie de verifyToken() en extrayant des ensembles de revendications. Je ne comprends pas d'où est venu apiKey.getSecret(). Pourriez-vous me guider s'il vous plaît?

Le code ci-dessous pour référence:

public class JJWTDemo {

    private static final String secret = "MySecrete";

    private static String generateToken(){
        String id = UUID.randomUUID().toString().replace("-", "");
        Date now = new Date();
        Date exp = new Date(System.currentTimeMillis() + (1000*30)); // 30 seconds

        String token = Jwts.builder()
                .setId(id)
                .setIssuedAt(now)
                .setNotBefore(now)
                .setExpiration(exp)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();

        return token;
    }

    private static void verifyToken(String token){
        Claims claims = Jwts.parser().
                setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret()))
                .parseClaimsJws(token).getBody();
        System.out.println("----------------------------");
        System.out.println("ID: " + claims.getId());
        System.out.println("Subject: " + claims.getSubject());
        System.out.println("Issuer: " + claims.getIssuer());
        System.out.println("Expiration: " + claims.getExpiration());
    }

    public static void main(String[] args) {
        System.out.println(generateToken());
        String token = generateToken();
        verifyToken(token);
    }
}

Je vois que l'erreur ci-dessous arrive:

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4N2E5NmYwNTcyN2M0ZDY0YjZmODlhNDAyOTQ2OTZiNyIsImlhdCI6MTQ4NDQ4NjYyNiwibmJmIjoxNDg0NDg2NjI2LCJleHAiOjE0ODQ0ODY2NTZ9.ycS7nLWnPpe28DM7CcQYBswOmMUhBd3wQwfZ9C-yQYs
Exception in thread "main" Java.lang.IllegalArgumentException: A signing key must be specified if the specified JWT is digitally signed.
    at io.jsonwebtoken.lang.Assert.notNull(Assert.Java:85)
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.Java:331)
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.Java:481)
    at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.Java:541)
    at io.jsonwebtoken.jjwtfun.service.JJWTDemo.verifyToken(JJWTDemo.Java:31)
    at io.jsonwebtoken.jjwtfun.service.JJWTDemo.main(JJWTDemo.Java:41)

Dépendance Maven:

<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
<jjwt.version>0.7.0</jjwt.version>
6
user5778069

apiKey.getSecret() dans l'article de blog fait référence à la clé secrète sécurisée, générée aléatoirement et codée en Base64 (comme un mot de passe) attribuée à la clé d'API fournie par chaque client à Stormpath. Les clients Stormpath utilisent cette clé API pour authentifier chaque demande dans l'API Stormpath REST. Étant donné que chaque client Stormpath possède une clé API (et que celle-ci est accessible à votre application), le secret de la clé d'API est un «paramètre par défaut» idéal pour la signature et la vérification des fichiers JWT spécifiques à votre application.

Si vous ne possédez pas de clé API Stormpath, tout tableau d'octets sécurisé-aléatoire suffisamment puissant conviendra parfaitement pour la signature et la vérification des fichiers JWT.

Dans l'exemple ci-dessus, ce qui suit est présenté comme clé de test:

private static final String secret = "MySecrete";

Cette clé n'est pas valide (compatible JWT) et ne peut pas être utilisée pour les algorithmes JWT HMAC.

Le RFC JWT requiert que vous DEVEZ utiliser une longueur de clé de tableau d'octets égale ou supérieure à la longueur de sortie de hachage.

Cela signifie que si vous utilisez HS256, HS384 ou HS512, vos tableaux d'octets de clé doivent avoir respectivement 256 bits (32 octets), 384 bits (48 octets) et 512 bits (64 octets). Je vais plus en détail à ce sujet dans une autre réponse StackOverflow - voir les données ici et les exemples MacProvider qui peuvent vous générer une clé conforme aux spécifications et sécurisée.

Sur cette base, voici cet exemple de code, réécrit en a) génère une clé valide et b) référence cette clé en tant que chaîne codée en Base64:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.MacProvider;

import Java.security.Key;
import Java.util.Base64;
import Java.util.Date;
import Java.util.UUID;

public class JJWTDemo {

    private static final Key secret = MacProvider.generateKey(SignatureAlgorithm.HS256);
    private static final byte[] secretBytes = secret.getEncoded();
    private static final String base64SecretBytes = Base64.getEncoder().encodeToString(secretBytes);

    private static String generateToken() {
        String id = UUID.randomUUID().toString().replace("-", "");
        Date now = new Date();
        Date exp = new Date(System.currentTimeMillis() + (1000 * 30)); // 30 seconds

        String token = Jwts.builder()
            .setId(id)
            .setIssuedAt(now)
            .setNotBefore(now)
            .setExpiration(exp)
            .signWith(SignatureAlgorithm.HS256, base64SecretBytes)
            .compact();

        return token;
    }

    private static void verifyToken(String token) {
        Claims claims = Jwts.parser()
            .setSigningKey(base64SecretBytes)
            .parseClaimsJws(token).getBody();
        System.out.println("----------------------------");
        System.out.println("ID: " + claims.getId());
        System.out.println("Subject: " + claims.getSubject());
        System.out.println("Issuer: " + claims.getIssuer());
        System.out.println("Expiration: " + claims.getExpiration());
    }

    public static void main(String[] args) {
        System.out.println(generateToken());
        String token = generateToken();
        verifyToken(token);
    }
}

Notez que les tableaux d'octets codés en Base64 sont pas cryptés (encodage de texte! = Crypté), assurez-vous donc que si vous codez en Base64 vos octets de clé secrète, vous gardez toujours cette chaîne Base64 en toute sécurité/masquée.

Enfin, les constantes finales statiques ci-dessus (nommées secret, secretBytes et base64SecretBytes) ne sont disponibles que pour cette démonstration de test simple - il ne faut jamais coder en dur les clés dans le code source, encore moins les transformer en constantes statiques, car elles peuvent être facilement décompilées.

6
Les Hazlewood

Je suis 100% d'accord sur Les Hazlewood. Mais nous devrions toujours envoyer les Subject, Issuer et Audience pour identifier plus de détails pour les utilisateurs connectés actuels. Le code peut être modifié comme ci-dessous:

import Java.security.Key;
import Java.util.Base64;
import Java.util.Date;
import Java.util.UUID;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.MacProvider;

public class TokenUtil {
    private static final Key secret = MacProvider.generateKey(SignatureAlgorithm.HS256);
    private static final byte[] secretBytes = secret.getEncoded();
    private static final String base64SecretBytes = Base64.getEncoder().encodeToString(secretBytes);

    private static String generateToken(String subject, String issuer, String audience) {
        String id = UUID.randomUUID().toString().replace("-", "");
        Date now = new Date();
        Date exp = new Date(System.currentTimeMillis() + (1000 * 30)); // 30 seconds

        String token = Jwts.builder()
                .setId(id)
                .setIssuedAt(now)
                .setNotBefore(now)
                .setExpiration(exp)
                .setSubject(subject)
                .setIssuer(issuer)
                .setAudience(audience)
                .signWith(SignatureAlgorithm.HS256, base64SecretBytes)
                .compact();

        return token;
    }

    private static void verifyToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(base64SecretBytes)
                .parseClaimsJws(token).getBody();

        System.out.println("----------------------------");
        System.out.println("ID: " + claims.getId());
        System.out.println("Subject: " + claims.getSubject());
        System.out.println("Issuer: " + claims.getIssuer());
        System.out.println("Expiration : " + claims.getExpiration());
        System.out.println("Not Before : "+claims.getNotBefore());
        System.out.println("Audience :: "+claims.getAudience());
    }

    public static void main(String[] args) {
        String token = generateToken("MySubject", "AH", "MyAudience");
        System.out.println("TOKEN :: "+token);
        verifyToken(token);
    }
}
0
PAA