私は自分のアプリケーションにJWT
を実装することを考えています。そのため、以下から参照を取得することにより、R&Dを行っています https://stormpath.com/blog/jwt-Java-create -verify 。クレームセットを抽出してgenerateToken()
を試行しているときに、verifyToken()
メソッドを正常に実装できました。 apiKey.getSecret()
がどこから来たのかわかりません。これについて私を案内していただけませんか?
参考のために以下のコード:
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);
}
}
以下のエラーが表示されます:
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)
Mavenの依存関係:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<jjwt.version>0.7.0</jjwt.version>
ブログ記事のapiKey.getSecret()
は、Stormpathがすべての顧客に提供するAPIキーに割り当てられた、ランダムに生成され、Base64でエンコードされた安全な秘密キー(パスワードなど)への参照です。 Stormpathのお客様は、このAPIキーを使用してStormpathへのすべてのリクエストを認証しますREST API。すべてのStormpathのお客様はAPIキーを持っているため(そしてキーはアプリケーションからアクセス可能です)、APIキーシークレットはアプリケーションに固有のJWTに署名および検証するための理想的な「デフォルト」。
Stormpath APIキーがない場合、JWTの署名と検証には、十分に強力なセキュアランダムバイト配列で十分です。
上記の例では、以下がテストキーとして表示されます。
private static final String secret = "MySecrete";
これは有効な(JWT準拠)キーではなく、JWT HMACアルゴリズムには使用できません。
JWT RFCが必要[[##〜] must [〜#〜] ハッシュ出力以上のバイト配列キー長を使用する必要がある長さ。
つまり、HS256、HS384、またはHS512を使用する場合、キーのバイト配列はそれぞれ256ビット(32バイト)、384ビット(48バイト)、または512ビット(64バイト)でなければなりません。 別のStackOverflowの回答 でこれについて詳しく説明します。そこでデータを確認し、仕様に準拠した安全なキーを生成できるMacProvider
の例をご覧ください。
これに基づいて、次のコードサンプルは、a)有効なキーを生成し、b)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);
}
}
Base64でエンコードされたバイト配列は暗号化されていない暗号化されていない(テキストエンコード!=暗号化)ので、秘密鍵のバイトをBase64でエンコードした場合でも、そのBase64文字列を安全に保つようにしてください。 /非表示。
最後に、上記の静的な最終定数(secret
、secretBytes
およびbase64SecretBytes
という名前)は、この単純なテストのデモンストレーションのためだけにあります。キーをソースコードにハードコードすることはできません。簡単に逆コンパイルできるので、それらを静的定数にします。
Les Hazlewoodに100%同意します。ただし、現在のログインユーザーを特定するために、常にSubject
、Issuer
、Audience
を送信する必要があります。コードは以下のように変更できます。
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);
}
}