質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.98%

jsonからRSAPublicKeyのインスタンスを生成する方法

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 86

ios-develper

score 5

現在、Sign in with Apple の実装を行っています。
サーバサイドは、Javaを使用しています。

その中で、Appleが返却してきたJWTを、
https://appleid.apple.com/auth/keys
の、公開鍵で検証してやる必要があるのですが、
https://appleid.apple.com/auth/keys
のjsonから、どうやって、
java.security.interfaces.RSAPublicKey
のインスタンスを生成して良いか分かりません。

どなたか教えて頂けないでしょうか?

jwtのライブラリは、

https://github.com/auth0/java-jwt

を利用しようとしています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

解決しました。

同じ問題で躓いている人のために、
動作検証済みのコードを載せておきます。

ライブラリは
https://github.com/auth0/java-jwt
と、その依存ライブラリおよび
https://github.com/stleary/JSON-java
を入れました。

package jp.sample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;

import org.json.JSONArray;
import org.json.JSONObject;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.JWTVerifier;

public class Sample01JwtVerify {

    /**
     *
     * SIWAのJWT検証サンプル
     *
     */
    public void test() {

        // テスト対象のトークン
        // ここに、iOSの ASAuthorizationAppleIDCredentialのインスタンスの
        // 「identityToken」をutf8として解釈した文字列を入れる。
        String token ="ヘッダー.ペイロード.署名";

        //----------------------------------------------------------
        // ■STEP1
        //   Appleサーバより公開鍵を取得
        //----------------------------------------------------------
        // Appleの公開鍵のURL
        String strUrl = "https://appleid.apple.com/auth/keys";

        HttpURLConnection  urlConn = null;
        InputStream in = null;
        BufferedReader reader = null;
        String jsonString = null;

        try {

            URL url = new URL(strUrl);
            urlConn = (HttpURLConnection) url.openConnection();
            urlConn.setRequestMethod("GET");
            urlConn.connect();
            int status = urlConn.getResponseCode();
            System.out.println("HTTPステータス:" + status);

            if (status == HttpURLConnection.HTTP_OK) {

                in = urlConn.getInputStream();
                reader = new BufferedReader(new InputStreamReader(in));
                StringBuilder output = new StringBuilder();
                String line;

                while ((line = reader.readLine()) != null) {
                    output.append(line);
                }

                jsonString = output.toString();
                System.out.println("jsonString=" + jsonString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
                if (urlConn != null) {
                    urlConn.disconnect();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // JSON解析
        JSONObject jsonRoot = new JSONObject(jsonString);

        // keys
        JSONArray array = jsonRoot.getJSONArray("keys");

        // key
        JSONObject key = array.getJSONObject(0);

        // n
        String n = key.getString("n");
        System.out.println("n=" + n);

        // e
        String e = key.getString("e");
        System.out.println("e=" + e);

        //----------------------------------------------------------
        // ■STEP2
        //   n(modulus)と、e(publicExponent)を、Base64UrlSafeでデコード
        //----------------------------------------------------------
        Base64.Decoder decorder = Base64.getUrlDecoder();
        byte[] modulusBytes = decorder.decode(n);
        byte[] publicExponentBytes = decorder.decode(e);

        //----------------------------------------------------------
        // ■STEP3
        //   4ビットずつ16進表記に変換
        //----------------------------------------------------------
        String modulusHex = bytesToHex2(modulusBytes);
        String publicExponentHex = bytesToHex2(publicExponentBytes);
        System.out.println("modulusHex=" + modulusHex);
        System.out.println("publicExponentHex=" + publicExponentHex);

        //----------------------------------------------------------
        // ■STEP4
        //   BigInteger化する。
        //----------------------------------------------------------
        BigInteger modulesBigInt = new BigInteger(modulusHex, 16);
        BigInteger exponentBigInt = new BigInteger(publicExponentHex, 16);

        //----------------------------------------------------------
        // ■STEP5
        //   RSAPublicKeySpec化する。
        //----------------------------------------------------------
        RSAPublicKeySpec rsaPubKS = new RSAPublicKeySpec(modulesBigInt , exponentBigInt);

        //----------------------------------------------------------
        // ■STEP6
        //   RSAPublicKey化する。
        //----------------------------------------------------------
        KeyFactory kf = null;
        RSAPublicKey publicKey = null;
        try {
            kf = KeyFactory.getInstance("RSA");
            publicKey = (RSAPublicKey) kf.generatePublic(rsaPubKS);

        } catch (NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (InvalidKeySpecException e2) {
            e2.printStackTrace();
        }
        //----------------------------------------------------------
        // ■STEP7
        //   認証する。
        //----------------------------------------------------------
        try {
            Algorithm algorithm = Algorithm.RSA256(publicKey, null);
            JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("https://appleid.apple.com")
                .build();

            verifier.verify(token);

        } catch (JWTVerificationException e3){

            System.out.println("認証NG");

        } catch (Exception e4) {
            e4.printStackTrace();
        }
        System.out.println("認証OK");
    }
    /**
     *
     * byte配列を16進数の文字列に変換
     *
     * @param hashInBytes
     * @return
     */
    private String bytesToHex2(byte[] hashInBytes) {

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < hashInBytes.length; i++) {
            String hex = Integer.toHexString(0xff & hashInBytes[i]);
            if (hex.length() == 1) sb.append('0');
            sb.append(hex);
        }
        return sb.toString();
    }

}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.98%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる