Java 8 で Box の JWT によるサーバー認証を試してみる
まとめ記事ができました
このスクラップについて
Java 17 で Box API Java SDK を使って JWT によるサーバー認証には成功したが、Java 8 では今のところ成功していない。
Box API Java SDK 自体は Java 8 をサポートしているので折角であれば Java 8 で JWT 認証ができるようになりたい。
このスクラップは未完で終わる可能性があるがベストは尽くしたいと思う。
関連スクラップ
Java 17 版の Box の JWT によるサーバー認証については下記のスクラップにまとめた。
クライアント資格情報許可によるサーバー認証については下記のスクラップにまとめた。
Box の Business プランを契約する手続きについては下記のスクラップにまとめた。
Box の Individual プラン(無料)で試したことについては下記のスクラップにまとめた。
プロジェクト作成
まずは問題を再現するために IntelliJ IDEA で新規プロジェクトを作成する。
build.gradle.kts 修正
jdkVersion を 11 → 8 にする。
kotlin {
jvmToolchain(8)
}
main() 関数の実行
この時点で Ctrl + R を押すなどして main() 関数を実行できるか確認する。
Hello World!
Box API Java SDK 追加
依存関係を追加する。
dependencies {
testImplementation(kotlin("test"))
implementation("com.box:box-java-sdk:4.1.0")
}
依存関係を追加したら Gradle ツールウィンドウのリロードボタン 🔃 を押す。
環境変数の追加
環境変数 BOX_CONFIG_FILE を追加して構成ファイルのパスを設定する。
コーディング
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")
}
問題の再現
下記の例外が発生することを確認する。
Exception in thread "main" com.box.sdk.BoxAPIException: Error parsing PKCS private key for Box Developer Edition.
気になるドキュメント
GitHub ページの 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 秘密鍵のパースに関する例外が発生すると書かれており、まさに今の状況になっている。
IntelliJ IDEA での JAR 追加
下記の記事が参考になりそうだった。
JAR のダウンロード
下記の URL にアクセスする。
UnlimitedJCEPolicyJDK7.zip を押す。
同意チェックを入れてからダウンロードボタンを押す。
ログインページが現れた、面倒だが仕方がないのでアカウントを作成する。
Oracle アカウントの作成
こんなにいっぱい入力項目がある
やっとダウンロードできた
- local_policy.jar
- README.txt
- US_export_policy.jar
どちらの JAR ファイルを使えば良いのだろう。
まずは README を読んでみよう。
今更だけど
Java 8 だが JDK 7 用の JAR で大丈夫なのだろうか?
インストール手順
README によると JAVA_HOME の lib/security に入れるらしい。
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]
JAVA_HOME の確認
Project Structure(Command + ;)で確認できる。
同じ名前のファイルを見つけた
僕の場合は下記のパスだった。
cd /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/security
バックアップ作成
まずは名称を変更してバックアップをとっておく。
sudo mv local_policy.jar local_policy.jar.bak
sudo mv US_export_policy.jar US_export_policy.jar.bak
コピー
続いてダウンロードした JAR ファイルをコピーする。
sudo cp ~/Downloads/UnlimitedJCEPolicy/local_policy.jar ./
sudo cp ~/Downloads/UnlimitedJCEPolicy/US_export_policy.jar ./
この状態で実行
動いた!
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 の警告は出ているけどこれは依存関係を追加すれば解決するはず。
ユーザー代理アクセス
環境変数 BOX_USER_ID を追加してアカウント ID を設定する。
コードも少しだけ変更する。
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")
}
この状態で実行
ユーザーの代理アクセスもできた!
Box Reports
It works!
Exit
今日はここまで
Box API Java SDK の GitHub ページが素晴らしいおかげで無事に Java 8 でもユーザーの代理アクセスを試すことができた。
もっと苦労するかと思ったけどすぐに解決できてよかった。
次回は何をやろうかな。
今日はここから
今まで秘密鍵をファイルから読み込んでいたが Cloud Run などのコンテナ運用環境を想定して環境変数から読み込むことを試したい。
Box CLI も面白そう
サービスアカウント → ユーザーへのフォルダー共有
ユーザーからサービスアカウントへのフォルダー共有は成功したが、反対側もできたら面白そう。
あまり認証が関係ないけど検証してみたい。
Webhook も面白そう
スクラップを分けよう
環境変数を利用した秘密鍵の読み込み以外はあまり関連が無いのでスクラップを分けようと思う。
環境変数の読み込み
FileReader の代わりに文字列を渡すことで文字列からも読み込める。
BoxConfig.readFrom(jsonString)
構成ファイル 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
環境変数に設定
BOX_CONFIG_JSON という名前で環境変数を追加する。
コーディング
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")
}
動作確認
環境変数を使っても問題なくアクセスすることができた。
Box Reports
It works!
Exit
まとめ
今回のスクラップ作成を通じて学んだことは下記のとおり。
- Java 8 で JWT 認証を行うには Oracle から JCE Unlimited Strength Jurisdiction Policy Files 7 をダウンロードして JAVA_HOME/lib/security に JAR ファイル 2 件をインストールする必要がある。
- IntelliJ IDEA では Project Structure(Command +
;
)で JAVA_HOME の場所を確認できる。 - 秘密鍵はファイルからだけではなく文字列からも読み込むこともできる。
おわりに
意外とすぐに解決できて良かった。
Box API Java SDK の GitHub ページのドキュメントが素晴らしいおかげだ。
解決はできたけど JAVA_HOME 内の JAR を置き換えるというのはなかなかの荒技な気がする。
本番環境ではできないケースがあるかも知れない。
そのような場合は下記のいずれかの手段を選択する必要がありそう。
- JWT 認証を使わず、クライアント資格情報を使う。
- Java 8 を使わず、Java 17 を使う。
ソースは怪しいが Java 8 は 2030 年 12 月までサポートされるようなのであと 7 年くらいある。
不思議なことに Java 11 や Java 17 の方が早くサポートが終了するみたいなので Java 8 を使いたいニーズも少なくないのだろう。
とはいえ Java 17 も 2029 年 9 月までは使えるので思い切って Java 17 を使うのもありな気がする。