【Java初心者】keytoolを使わずに証明書を追加する

# 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で書いてみました

ネットで情報を集めて、とりあえず以下のコードを書いてみました。

```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
```