12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Java]ES256アルゴリズムを用いたJWT生成方法 メモ

Posted at

JWTとは

  • JSON Web Tokenの略称。

  • 電子署名による改竄検知機能を持ったトークン。

  • 認証用途などで用いられる。

  • ピリオド(".")区切りのHeader部、Payload部、Signature部から構成される。

    Header.Payload.Signature
    
  • Header部:JWTの検証に必要な情報から構成される。署名に使用するアルゴリズム(alg)や署名に使用する鍵(公開鍵/秘密鍵)を識別する値(kid)などが含まれる。

  • Payload部:JWTに関する属性情報から構成される。JWTの発行者(aud)や発行日時(iat)などの情報が含まれる。

  • Signature部:Header部とPayload部を合わせた文字列に署名した値。Header部に指定したアルゴリズムと秘密鍵を使用して署名を行う。

  • 各部位はBase64URLエンコードされる。

  • 署名アルゴリズムには、ES256やRSA-SHA256などが用いられる。

    • ES256:「P-256 および SHA-256 を使⽤した ECDSA」の別称。
      * ECDSA:Elliptic Curve Digital Signature Algorithm, 楕円曲線デジタル署名アルゴリズム
      * P-256:使⽤されるアルゴリズムのバージョン。

サンプルコード

  • Sign in with Apple のJWT例を構成情報とした生成コード。
    • デベロッパーコンソールから取得した鍵は使用せず、自前で生成した鍵を使用して署名を行う。
    • エラーハンドリングなどは省略。
package jwt_test;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JWTTest {
	private static ECPublicKey PUBLIC_KEY;
	private static ECPrivateKey PRIVATE_KEY;

	public static void main(String[] args) {
		try {
			String jwt = generateJWT();
			verifyJWT(jwt, PUBLIC_KEY);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidAlgorithmParameterException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (SignatureException e) {
			e.printStackTrace();
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
	}

	// JWT生成
	private static String generateJWT()
			throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, SignatureException,
			InvalidKeyException, JsonProcessingException {

		// 署名鍵生成
		// ※Sign in with Appleの場合は、コンソールから生成した秘密鍵を利用する。https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
		final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
		keyPairGenerator.initialize(new ECGenParameterSpec("secp256r1"));
		final KeyPair keyPair = keyPairGenerator.generateKeyPair();
		PUBLIC_KEY = (ECPublicKey) keyPair.getPublic();
		PRIVATE_KEY = (ECPrivateKey) keyPair.getPrivate();
		byte[] publicKeyEncodedBytes = Base64.encodeBase64(PUBLIC_KEY.getEncoded());
		byte[] privateKeyEncodedBytes = Base64.encodeBase64(PRIVATE_KEY.getEncoded());
		System.out.println("ES256 Public Key:" + new String(publicKeyEncodedBytes));
		System.out.println("ES256 Private Key:" + new String(privateKeyEncodedBytes));

		// ヘッダー部設定
		final ObjectMapper objectMapper = new ObjectMapper();
		final Map<String, Object> jwtHeader = new LinkedHashMap();
		jwtHeader.put("kid", "ABC123DEFG");
		jwtHeader.put("alg", "ES256");
		String jwtHeaderStr = Base64.encodeBase64URLSafeString(objectMapper.writeValueAsBytes(jwtHeader));

		// ペイロード部設定
		final Map<String, Object> jwtPayload = new LinkedHashMap();
		jwtPayload.put("iss", "DEF123GHIJ");
		jwtPayload.put("iat", 1437179036);
		jwtPayload.put("exp", 1493298100);
		jwtPayload.put("aud", "https://appleid.apple.com");
		jwtPayload.put("sub", "com.mytest.app");
		String jwtPayloadStr = Base64.encodeBase64URLSafeString(objectMapper.writeValueAsBytes(jwtPayload));

		// 署名設定
		final Signature jwtSignature = Signature.getInstance("SHA256withECDSAinP1363Format");
		jwtSignature.initSign(PRIVATE_KEY);
		jwtSignature.update((jwtHeaderStr + "." + jwtPayloadStr).getBytes());
		byte[] jwtSignatureBytes = jwtSignature.sign();
		final String jwtSignatureStr = Base64.encodeBase64URLSafeString(jwtSignatureBytes);
		final String jwt = jwtHeaderStr + "." + jwtPayloadStr + "." + jwtSignatureStr;
		System.out.println("jwt:" + jwt);
		return jwt;
	}

	// 署名検証
	public static void verifyJWT(String jwt, ECPublicKey publicKey) throws NoSuchAlgorithmException,
			InvalidKeyException, SignatureException {
		final String[] splitJwt = jwt.split("\\.");
		final String jwtHeaderStr = splitJwt[0];
		final String jwtPayloadStr = splitJwt[1];
		final String jwtSignatureStr = splitJwt[2];
		final Signature jwtSignature = Signature.getInstance("SHA256withECDSAinP1363Format");
		jwtSignature.initVerify(publicKey);
		jwtSignature.update((jwtHeaderStr + "." + jwtPayloadStr).getBytes());

		if( jwtSignature.verify(Base64.decodeBase64(jwtSignatureStr))) {
			System.out.println("Verifying Signature Success");
		}else {
			System.out.println("Verifying Signature Failure");
		}
	}
}

動作確認

ES256 Public Key:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaGzLv1quqEfO2YfyeMKJ5y72hVYP+NgH2tSqyKtW3MoxZShcY/p3aSw9af+N4bDwutbrTtLmEn4I6GbV71/bcQ==
ES256 Private Key:MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCBS+5M9cXf4BPwM8g8JTR4Dma26Etno8lAXg7EgsWht4A==
jwt:eyJraWQiOiJBQkMxMjNERUZHIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJERUYxMjNHSElKIiwiaWF0IjoxNDM3MTc5MDM2LCJleHAiOjE0OTMyOTgxMDAsImF1ZCI6Imh0dHBzOi8vYXBwbGVpZC5hcHBsZS5jb20iLCJzdWIiOiJjb20ubXl0ZXN0LmFwcCJ9.WxE4w1JSf1dxcHJiDaFdRHstMveiwfqn0ZcwW_7PEJji0kRuJWCczt_AM3-RW6lgp8CDREfTnQTMRegnHohRKg
Verifying Signature Success

※生成されたjwtの中身をjwt.ioで確認した結果

  • Header部

    {
      "kid": "ABC123DEFG",
      "alg": "ES256"
    }
    
  • Payload部

    {
      "iss": "DEF123GHIJ",
      "iat": 1437179036,
      "exp": 1493298100,
      "aud": "https://appleid.apple.com",
      "sub": "com.mytest.app"
    }
    

参考情報

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?