戻る

2026/10/23

2026年10月20日、インドの CA である eMudhra が運営する 8 つの中間認証局(CA)の秘密鍵を、誰でも復元できることが公表されました。ブラウザーは、問題が報告されたその日のうちにこれらの証明書をブロックしました。しかし多くの Java アプリケーションは現在もこれらを信頼しており、誰かが JDK を更新するかトラストストアを変更するまで信頼し続けます。

2026年10月21日、BellSoft は、多くの Java ランタイムがこれらの証明書をいまだに受け入れることを示すテスト結果を公開しました。本記事では、私たちが独自のテストでも確認したその結果をまとめ、ブラウザーは影響を受けないのに Java が影響を受ける理由、システムの確認方法、そして対処方法を説明します。

要点

  • eMudhra の中間 CA 鍵 8 つが侵害されています。鍵を持つ者は誰でも任意のホスト名の証明書を発行でき、そのチェーンを信頼するクライアントはそれを受け入れてしまいます。
  • ブラウザーはこれらをブロックしていますが、Java は独自のトラストストアを使用し、デフォルトでは失効も Certificate Transparency もチェックしません。
  • 複数のベンダーの JDK ビルドが、コンテナーイメージも含め、影響を受けるチェーンの一部またはすべてを今も信頼しています。
  • ベンダーが修正済みの JDK を提供するまでは、テストを行ったうえで、影響を受ける 2 つのルートをトラストストアから削除してください。失効チェックの有効化が 2 つ目の選択肢です。

何が起きたのか

eMudhra は 8 つの中間 CA 証明書の秘密鍵を誤った方法で生成したため、誰でも公開されている証明書から秘密鍵を復元できます。これは、中間証明書が発行された 2025年4月から可能な状態でした。

2026年10月17日、報告者は 8 つの鍵それぞれで「We have a problem here」というメッセージに署名し、その署名をブラウザーのルートプログラムに送ることで問題を証明しました。詳細は 10月20日に公開されました。

  • 6 つの中間証明書は TLS 証明書を発行します:eMudhra の DV、OV、EV TLS CA で、世代は G3B と G3C です。
  • 2 つの中間証明書はコード署名証明書を発行します。
  • 各中間証明書は 2 通り存在します:eMudhra の emSign ルートが署名したものと、Sectigo の AAA Certificate Services ルートがクロス署名したものです。JDK のトラストストアでは、チェーンは 2 つのルートのいずれか、すなわち emSign ECC Root CA - G3(クロス署名経由)または AAA Certificate Services で終わります。

Firefox と Chrome は 10月17日から、Apple は 10月19日から、Microsoft は 10月20日から、これらの中間証明書をブロックしています。eMudhra は 10月18日と19日に、失効理由 CA_COMPROMISE で 8 つすべてを失効させました。Linux ディストリビューションは、更新された CA 証明書パッケージをリリースしています。

影響

これらの鍵のいずれかを持つ者は、銀行、クラウドプロバイダー、自社の API ゲートウェイなど、任意のホスト名の証明書を作成できます。そのチェーンを信頼するクライアントは、その証明書を受け入れます。侵害されたネットワークや改ざんされた DNS レコードなど、通信を傍受する手段と組み合わせれば、攻撃者は次のことが可能になります。

  • Java アプリケーションと、それが呼び出すサービスとの間の通信を読み取り、改ざんする(パスワード、API キー、トークンを含む)
  • パッケージリポジトリ、アップデートサーバー、パートナーの API になりすます
  • 2 つのコード署名用中間証明書を使って、信頼された発行元からのものに見えるコードに署名する

これらの中間証明書は Certificate Transparency(CT)ログに一度も記録されていないため、CT を強制するブラウザーがこれらを通じて攻撃されることはあり得ませんでした。CT をチェックしないクライアントに対して鍵が使われたかどうかは、まだわかっていません。Java はそうしたクライアントの 1 つです。

Java がこれらの証明書を今も信頼している理由

Mozilla、Google、Apple、Microsoft は、OneCRL、CRLite、CRLSets といった独自の仕組みを通じてブロックを配信しました。JDK のデフォルトの TLS 実装は、そのいずれも読み込みません。次の 3 つのデフォルト設定により、Java は危険にさらされたままになっています。

  • 独自のトラストストア。アプリケーションが javax.net.ssl.trustStore を設定していない限り、Java は $JAVA_HOME/lib/security/jssecacerts が存在すればそのファイルのルートを、存在しなければ $JAVA_HOME/lib/security/cacerts のルートを信頼します。ベンダーが同梱する cacerts には、ブラウザーのブロックリストも OS の CA 更新も反映されません。変更されるのは、管理者が編集したときか、JDK の更新で置き換えられたときだけです。
  • 失効チェックなし。Java は有効にしない限り失効をチェックしません(com.sun.net.ssl.checkRevocation はデフォルトで false)。そのため、デフォルト設定では CA による失効が認識されません。
  • Certificate Transparency なし。JDK は CT を強制しないため、ブラウザーのユーザーを守った保護は適用されません。

公開ページには、中間証明書とルートの組み合わせごとに 1 つずつ、計 12 のテストホストが掲載されています。BellSoft は 10月21日に Linux 上でこれらをテストし、私たちも 10月23日に同じテストを行いました。

テストしたランタイム信頼されたテストホスト
Liberica JDK 21.0.1012 件中 12 件
Liberica JDK 8u504(コンテナーイメージ)12 件中 12 件
Liberica JDK 21.0.12.1(コンテナーイメージ)12 件中 12 件
Eclipse Temurin 25.0.4.112 件中 6 件(eMudhra ルートのみ)
Eclipse Temurin 21.0.12.1(コンテナーイメージ、当社テスト)12 件中 6 件(eMudhra ルートのみ)
Arch Linux OpenJDK 27(システム CA ストア)12 件中 6 件(eMudhra ルートのみ)

違いは Sectigo のルートにあります。Mozilla はすでに AAA Certificate Services を削除しており、Temurin と Arch Linux のトラストストアは Mozilla のリストから作られているため、開いているのは eMudhra 経由の経路だけです。Liberica JDK のトラストストアにはまだそのルートが含まれているため、両方の経路が開いています。

確認すべきシステム

  • ベンダーのアーカイブから展開した、またはベンダーのインストーラーでインストールしたスタンドアロンの JDK。これらは独自の cacerts を使用します。
  • JDK を同梱するコンテナーイメージ。BellSoft がテストしたイメージでは、OS の ca-certificates パッケージを更新しても JDK の cacerts は変わりませんでした。新しいベースイメージを取得しても効果はなく、テストした 10月21日付の Liberica イメージは依然として 12 のテストホストすべてを信頼していました。
  • javax.net.ssl.trustStore、アプリケーションサーバーの設定、またはアプリケーション内部で指定された独自のトラストストアを持つアプリケーション。2 つのルートが含まれていないか確認してください。
  • ディストリビューションがパッケージ化した OpenJDK。cacerts がどこを指しているかを確認してください。システムの CA ストアにリンクされている場合(Arch Linux では /etc/ca-certificates/extracted/java-cacerts.jks へのシンボリックリンク)、ディストリビューションの ca-certificates の更新で修正されます。修正済みのパッケージバージョンは公開ページに掲載されています。

JDK の確認方法

まず、トラストストアに 2 つのルートがあるかを調べます。エイリアス名はベンダーによって異なる(Liberica JDK は emsigneccrootcag3 [jdk]、Temurin は cn_emsign_ecc_root_ca__g3,...)ため、証明書のサブジェクトで検索します。emSign のルートは他にもいくつかありますが、影響を受けるのは emSign ECC Root CA - G3 だけです。JDK 9 以降では次のとおりです。

keytool -list -v -cacerts -storepass changeit | grep -E '^Alias name:|^Owner:' | grep -B1 -E '^Owner: CN=(emSign ECC Root CA - G3|AAA Certificate Services),'

Java 8 の keytool には -cacerts オプションがなく、トラストストアは jre/ の下にあります。

keytool -list -v -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit | grep -E '^Alias name:|^Owner:' | grep -B1 -E '^Owner: CN=(emSign ECC Root CA - G3|AAA Certificate Services),'

一致するたびに、エイリアス名とそれに続く証明書のサブジェクトが出力されます。エイリアス名は修正の際に必要になるので、控えておいてください。

次に、実際の接続をテストします。この小さなプログラムは 12 のテストホストすべてに接続し、Java 8 以降のすべてのリリースで動作します。

import java.net.URI;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
public class Check {
    public static void main(String[] args) {
        for (String level : new String[]{"dv", "ov", "ev"})
            for (String gen : new String[]{"g3b", "g3c"})
                for (String via : new String[]{"", "-aaa"}) {
                    String host = level + "-" + gen + via + "-emudhra.we-have-a-problem.com";
                    try {
                        HttpsURLConnection c = (HttpsURLConnection) URI.create("https://" + host).toURL().openConnection();
                        c.setConnectTimeout(10000);
                        c.setReadTimeout(10000);
                        c.connect();
                        c.disconnect();
                        System.out.println("TRUSTED       " + host);
                    } catch (SSLHandshakeException e) {
                        System.out.println("TLS_REJECTED  " + host);
                    } catch (Exception e) {
                        System.out.println("ERROR         " + host + " (" + e + ")");
                    }
                }
    }
}

JDK 11 以降では java Check.java で実行します。Java 8 の場合や JRE しかインストールされていない場合は、まず javac Check.java でコンパイルし(必要なら JDK のあるマシンで)、java Check を実行します。TRUSTED の行は、この JDK が侵害された中間証明書を受け入れることを意味します。ERROR はホストに到達できなかったことを意味し、どちらの判断の根拠にもなりません。チェックは、コンテナー内も含め、アプリケーションと同じ JDK とトラストストアの設定で実行してください。

対処方法

1. 2 つのルートを削除する(修正済み JDK が提供されるまでの推奨策)

これらは、公開ページが Linux と macOS のユーザーに信頼を取り消すよう案内しているのと同じ 2 つのルートです。JDK を所有するユーザーとして、確認時に見つけたエイリアス名を使ってコマンドを実行してください。たとえば Liberica JDK では次のとおりです。

keytool -delete -cacerts -storepass changeit -alias "comodoaaaca [jdk]"
keytool -delete -cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"

Java 8 では、-cacerts を -keystore "$JAVA_HOME/jre/lib/security/cacerts" に置き換えてください。Temurin のようにトラストストアに 2 つのルートのうち 1 つしか含まれていない場合は、そのルートだけを削除します。

JDK のインストールを変更できない場合は、コピーを編集し、JVM がそれを使うように指定します。

cp "$JAVA_HOME/lib/security/cacerts" ./trimmed-cacerts
keytool -delete -keystore ./trimmed-cacerts -storepass changeit -alias "comodoaaaca [jdk]"
keytool -delete -keystore ./trimmed-cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"
java -Djavax.net.ssl.trustStore=./trimmed-cacerts -Djavax.net.ssl.trustStorePassword=changeit -jar app.jar

私たちのテストでは、どちらの方法でも Liberica JDK 8 と 21 が 12 のテストホストすべてを拒否するようになり、sectigo.com や github.com などの通常のサイトには引き続き接続できました。

コンテナーイメージの場合は、ビルド時にルートを削除して、新しく作成するすべてのイメージが修正済みになるようにします。

FROM bellsoft/liberica-runtime-container:jdk-21-musl
RUN keytool -delete -cacerts -storepass changeit -alias "comodoaaaca [jdk]" \
 && keytool -delete -cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"

Distroless イメージや非 root イメージでは、2 つの変更が必要です。シェルがないため、RUN の exec 形式を使って 1 行に 1 コマンドずつ記述します。また、非特権ユーザーで実行されるため、編集時は USER root に切り替え、その後イメージの通常のユーザーに戻します。

ヒント:ルートの削除は、8 つの中間証明書をブロックするよりも影響範囲が広くなります。チェーンがこれらのルートのいずれかでしか終わらない正規のサーバーは、動作しなくなります。展開する前にアプリケーションが呼び出すサービスをテストし、JDK を更新するたびにチェックを再実行してください。更新や再インストールによって元の cacerts が復元されることがあるためです。

2. 失効チェックを有効にする(任意)

eMudhra は 8 つの中間証明書をすべて失効させているため、失効をチェックする JVM はトラストストアを変更しなくてもこれらを拒否します。CRL 配布ポイントと OCSP を有効にします。

echo 'ocsp.enable=true' > revocation.security
java -Dcom.sun.net.ssl.checkRevocation=true \
     -Dcom.sun.security.enableCRLDP=true \
     -Djava.security.properties=revocation.security \
     -jar app.jar

私たちのテストでは、これにより Liberica JDK 8 と 21、および Temurin 21 で 12 のテストホストすべてが拒否されました。ただし、この設定はすべての証明書の検証方法を変えます。JVM はパス検証の際に OCSP または CRL の照会を行い、失効状態を判断できない場合はハンドシェイクが失敗することがあります。全体で有効にする前に、サーバーからレスポンダーに到達できるか、どの程度の遅延が加わるか、障害時にどう動作するかをテストしてください。すでに失効チェックを有効にして運用している場合や、ルートの削除と併用する場合に使用してください。

3. 修正済みビルドが出たら JDK を更新する

BellSoft は、これらのルートを削除した Liberica JDK のアップデートを準備中であると表明しています。ご利用の JDK ベンダーのセキュリティアドバイザリーを注視してください。アップデートが提供されたら、リリースノートを読んで eMudhra の証明書が対象になっていることを確認したうえでインストールし、コンテナーイメージを再ビルドして、チェックを再実行してください。バージョン番号が新しいというだけでは証拠になりません。

修正後:影響範囲を確認する

鍵は約 18 か月にわたって復元可能だったため、修正によって扉は閉じられても、誰かがそれを使ったかどうかはわかりません。認証情報や機密データを送信するシステムについては、TLS インスペクション、プロキシ、エンドポイントのログで、8 つの eMudhra 中間証明書から発行された証明書を検索してください。不審なものが見つかった場合は、影響を受ける認証情報をローテーションしてください。

長期的には、トラストストアを依存関係と同じように扱ってください。それぞれがどこにあり、誰が更新し、変更がどれくらいの速さで本番環境に反映されるかを把握しておくことです。今回のインシデントは、サーバーが独自の信頼リストのコピーを使っている場合、ブラウザーの更新だけでは不十分であることを示しています。

お困りですか?LOGIQ は、セキュリティパッチの適用を含め、OpenJDK と Linux のサポートを提供しています。環境内のすべての JDK とトラストストアの洗い出しや、修正の安全な展開についてサポートが必要な場合は、お問い合わせください。

関連情報