Closed37

Java 8 で Box の JWT によるサーバー認証を試してみる

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

このスクラップについて

Java 17 で Box API Java SDK を使って JWT によるサーバー認証には成功したが、Java 8 では今のところ成功していない。

Box API Java SDK 自体は Java 8 をサポートしているので折角であれば Java 8 で JWT 認証ができるようになりたい。

このスクラップは未完で終わる可能性があるがベストは尽くしたいと思う。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

関連スクラップ

Java 17 版の Box の JWT によるサーバー認証については下記のスクラップにまとめた。

https://zenn.dev/tatsuyasusukida/scraps/85b8cd22880306

クライアント資格情報許可によるサーバー認証については下記のスクラップにまとめた。

https://zenn.dev/tatsuyasusukida/scraps/92690df756d575

Box の Business プランを契約する手続きについては下記のスクラップにまとめた。

https://zenn.dev/tatsuyasusukida/scraps/42155b55231f4b

Box の Individual プラン(無料)で試したことについては下記のスクラップにまとめた。

https://zenn.dev/tatsuyasusukida/scraps/0293205105d96c

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Box API Java SDK 追加

依存関係を追加する。

build.gradle.kts(抜粋)
dependencies {
    testImplementation(kotlin("test"))
    implementation("com.box:box-java-sdk:4.1.0")
}

依存関係を追加したら Gradle ツールウィンドウのリロードボタン 🔃 を押す。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

コーディング

src/main/kotlin/Main.kt
import com.box.sdk.BoxConfig
import com.box.sdk.BoxDeveloperEditionAPIConnection
import com.box.sdk.BoxFolder
import java.io.FileReader

fun main(args: Array<String>) {
    val filePath = System.getenv("BOX_CONFIG_FILE")
    val fileReader = FileReader(filePath)
    val boxConfig = BoxConfig.readFrom(fileReader);
    val api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig)
    val rootFolder = BoxFolder.getRootFolder(api)

    for (info in rootFolder) {
        println(info.name)
    }

    println("Exit")
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

気になるドキュメント

GitHub ページの JVM セクションに気になることが書いてある。

https://github.com/box/box-java-sdk#jvm

Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 If you don't install this, you'll get an exception about key length or exception about parsing PKCS private key for Box Developer Edition. This is not a Box thing, this is a U.S. Government requirement concerning strong encryption. The listed jar is for Oracle JRE. There might be other similar JARs for different JRE

JCE Unlimited Strength Jurisdiction Policy Files 7 をインストールしないと Box 開発者エディション用 PKCS 秘密鍵のパースに関する例外が発生すると書かれており、まさに今の状況になっている。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

やっとダウンロードできた

  • local_policy.jar
  • README.txt
  • US_export_policy.jar

どちらの JAR ファイルを使えば良いのだろう。

まずは README を読んでみよう。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

インストール手順

README によると JAVA_HOME の lib/security に入れるらしい。

  1. Install the unlimited strength policy JAR files.

    In case you later decide to revert to the original "strong" but
    limited policy versions, first make a copy of the original JCE
    policy files (US_export_policy.jar and local_policy.jar). Then
    replace the strong policy files with the unlimited strength
    versions extracted in the previous step.

    The standard place for JCE jurisdiction policy JAR files is:

    <java-home>/lib/security           [Unix]
    <java-home>\lib\security           [Windows]
    
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

同じ名前のファイルを見つけた

僕の場合は下記のパスだった。

パス
cd /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/security
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

バックアップ作成

まずは名称を変更してバックアップをとっておく。

コマンド
sudo mv local_policy.jar local_policy.jar.bak
sudo mv US_export_policy.jar US_export_policy.jar.bak
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

コピー

続いてダウンロードした JAR ファイルをコピーする。

コマンド
sudo cp ~/Downloads/UnlimitedJCEPolicy/local_policy.jar ./
sudo cp ~/Downloads/UnlimitedJCEPolicy/US_export_policy.jar ./
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

この状態で実行

動いた!

実行結果
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exit

SLF4J の警告は出ているけどこれは依存関係を追加すれば解決するはず。

https://github.com/box/box-java-sdk/blob/v4.1.0/doc/logging.md#putting-sdk-logs-into-application-logs

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

ユーザー代理アクセス

環境変数 BOX_USER_ID を追加してアカウント ID を設定する。

コードも少しだけ変更する。

src/main/kotlin/Main.kt
import com.box.sdk.BoxConfig
import com.box.sdk.BoxDeveloperEditionAPIConnection
import com.box.sdk.BoxFolder
import java.io.FileReader

fun main(args: Array<String>) {
    val filePath = System.getenv("BOX_CONFIG_FILE")
    val userID = System.getenv("BOX_USER_ID") // この行を追加しました。

    val fileReader = FileReader(filePath)
    val boxConfig = BoxConfig.readFrom(fileReader);
    val api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig)
    api.asUser(userID) // この行を追加しました。
    
    val rootFolder = BoxFolder.getRootFolder(api)

    for (info in rootFolder) {
        println(info.name)
    }

    println("Exit")
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

今日はここまで

Box API Java SDK の GitHub ページが素晴らしいおかげで無事に Java 8 でもユーザーの代理アクセスを試すことができた。

もっと苦労するかと思ったけどすぐに解決できてよかった。

次回は何をやろうかな。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

今日はここから

今まで秘密鍵をファイルから読み込んでいたが Cloud Run などのコンテナ運用環境を想定して環境変数から読み込むことを試したい。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

サービスアカウント → ユーザーへのフォルダー共有

ユーザーからサービスアカウントへのフォルダー共有は成功したが、反対側もできたら面白そう。

あまり認証が関係ないけど検証してみたい。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

スクラップを分けよう

環境変数を利用した秘密鍵の読み込み以外はあまり関連が無いのでスクラップを分けようと思う。

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

環境変数の読み込み

FileReader の代わりに文字列を渡すことで文字列からも読み込める。

コード例
BoxConfig.readFrom(jsonString)
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

構成ファイル JSON を 1 行に変換

コマンド
cd ~/Downloads/
cat 1028976524_eiwdh011_config.json | node -e '
(async () => {
  const chunks = [];
  for await (const chunk of process.stdin) {
    chunks.push(chunk);
  }
  const buffer = Buffer.concat(chunks);
  const body = JSON.parse(buffer);
  const text = JSON.stringify(body);
  process.stdout.write(text);
})();
' > box_config_1_line.json
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

コーディング

src/main/kotlin/Main.kt
import com.box.sdk.BoxConfig
import com.box.sdk.BoxDeveloperEditionAPIConnection
import com.box.sdk.BoxFolder
import java.io.FileReader

fun main(args: Array<String>) {
    val jsonString = System.getenv("BOX_CONFIG_JSON")
    val userID = System.getenv("BOX_USER_ID")

    val boxConfig = BoxConfig.readFrom(jsonString);
    val api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig)
    api.asUser(userID)

    val rootFolder = BoxFolder.getRootFolder(api)

    for (info in rootFolder) {
        println(info.name)
    }

    println("Exit")
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

まとめ

今回のスクラップ作成を通じて学んだことは下記のとおり。

  • Java 8 で JWT 認証を行うには Oracle から JCE Unlimited Strength Jurisdiction Policy Files 7 をダウンロードして JAVA_HOME/lib/security に JAR ファイル 2 件をインストールする必要がある。
  • IntelliJ IDEA では Project Structure(Command + ;)で JAVA_HOME の場所を確認できる。
  • 秘密鍵はファイルからだけではなく文字列からも読み込むこともできる。
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

おわりに

意外とすぐに解決できて良かった。

Box API Java SDK の GitHub ページのドキュメントが素晴らしいおかげだ。

解決はできたけど JAVA_HOME 内の JAR を置き換えるというのはなかなかの荒技な気がする。

本番環境ではできないケースがあるかも知れない。

そのような場合は下記のいずれかの手段を選択する必要がありそう。

  • JWT 認証を使わず、クライアント資格情報を使う。
  • Java 8 を使わず、Java 17 を使う。

ソースは怪しいが Java 8 は 2030 年 12 月までサポートされるようなのであと 7 年くらいある。

https://ja.wikipedia.org/wiki/Javaバージョン履歴#各版リリース日/サポート期限一覧

不思議なことに Java 11 や Java 17 の方が早くサポートが終了するみたいなので Java 8 を使いたいニーズも少なくないのだろう。

とはいえ Java 17 も 2029 年 9 月までは使えるので思い切って Java 17 を使うのもありな気がする。

このスクラップは2023/04/28にクローズされました