JDK24にしてからの挙動の変化
これまで、特定のホームページにアクセスしてデータをスクレイピングする際、JDKの中にある証明書ではアクセスできないことがあったが、サイトの証明書をkeytoolでインポートすることで解決できた。
しかし、JDK24にしてから、なぜか以下のエラーが。
keytool -v -import -cacerts -alias test_ca -file test.cer -storepass changeit
keytoolエラー: java.util.IllegalFormatConversionException: d != java.lang.String
java.util.IllegalFormatConversionException: d != java.lang.String
at java.base/java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4662)
at java.base/java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:3200)
at java.base/java.util.Formatter$FormatSpecifier.print(Formatter.java:3155)
at java.base/java.util.Formatter.format(Formatter.java:2767)
at java.base/java.util.Formatter.format(Formatter.java:2698)
at java.base/java.lang.String.format(String.java:4468)
at java.base/sun.security.tools.keytool.Main.withWeakConstraint(Main.java:3629)
at java.base/sun.security.tools.keytool.Main.printX509Cert(Main.java:3660)
at java.base/sun.security.tools.keytool.Main.addTrustedCert(Main.java:3454)
at java.base/sun.security.tools.keytool.Main.doCommands(Main.java:1228)
at java.base/sun.security.tools.keytool.Main.run(Main.java:419)
at java.base/sun.security.tools.keytool.Main.main(Main.java:400)
オプションが変わったのか、またはバグなのかわかりませんが、困っていたところ、こちらの記事を見つけました。https://megascus.hatenablog.com/entry/2018/12/07/095008
どうやら、Javaプログラムによって、証明書をインポートできるらしい。
Javaで書いてみました
ネットで情報を集めて、とりあえず以下のコードを書いてみました。
package keytooltest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class Main {
public static void main(String[] args) {
if (args.length < 3) {
String helpmsg = "java -jar KeytoolTest.jar 証明書ファイル キーストアのパスワード エイリアス名";
System.out.println(helpmsg);
} else {
System.out.println("証明書:" + args[0] + " エイリアス:" + args[2]);
String certPath = args[0]; // インポートする証明書のパス
char[] password = args[1].toCharArray(); // デフォルトパスワード
String alias = args[2]; // 証明書のエイリアス名
try {
// cacertsファイルのパス取得
String javaHome = System.getProperty("java.home");
String keystorePath = javaHome + File.separator + "lib" + File.separator
+ "security" + File.separator + "cacerts";
// 鍵ストアの読み込み
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream fis = new FileInputStream(keystorePath)) {
keystore.load(fis, password);
}
// 証明書の読み込み
CertificateFactory cf = CertificateFactory.getInstance("X.509");
try (FileInputStream certStream = new FileInputStream(certPath)) {
X509Certificate cert = (X509Certificate) cf.generateCertificate(certStream);
// 証明書の追加
keystore.setCertificateEntry(alias, cert);
}
// 変更を保存
try (FileOutputStream out = new FileOutputStream(keystorePath)) {
keystore.store(out, password);
}
System.out.println("証明書のインポートが成功しました");
} catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
System.err.println(e.getMessage());
}
}
}
}
実行結果と確認
以下のようにして実行します。Linuxの場合は、管理者権限で行います。Windowsはなぜか問題ないみたい。
java -jar KeytoolTest.jar ca.crt changeit test_ca
証明書:ca.crt エイリアス:test_ca
証明書のインポートが成功しました
確認は以下のように行います。(ユーザー権限でOK)
keytool -list -cacerts -alias test_ca
test_ca,2025/03/27, trustedCertEntry,
証明書のフィンガプリント(SHA-256): XX:XX:XX:XX .....
消すときは管理者権限で
keytool -delete -cacerts -alias test_ca