エラーメッセージ
java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPublicKeyImpl
別の表記
- java.security.InvalidKeyException: No installed provider supports this key: sun.security.ec.ECPublicKeyImpl
- java.security.InvalidKeyException: No installed provider supports this key: sun.security.ec.ed.EdDSAPublicKeyImpl
コロンの後のテキストは、鍵の実装クラスです(`sun.security.rsa.RSAPublicKeyImpl`、`sun.security.ec.ECPublicKeyImpl`、`sun.security.ec.ed.EdDSAPublicKeyImpl`、Bouncy Castle のクラスなど)。現在の JDK では、`null` の鍵はそれより前の段階で「Key must not be null」というエラーになります。
Java (JDK) — Signature.initVerify / initSign, Cipher.init
このエラーの意味
JCA は、要求されたアルゴリズムの実装でこの鍵オブジェクトを受け付けるプロバイダーを探しましたが、該当するものはありませんでした。デフォルトのプロバイダーを使っている場合、これはほぼ必ず、鍵とアルゴリズムの系統が異なることを意味します。たとえば、RSA 鍵を `SHA256withECDSA` に、EC 鍵を `SHA256withRSA` に、Ed25519 鍵を ECDSA に渡した場合です。末尾のクラス名を見れば、実際にどの種類の鍵が渡されたのかがわかります。
発生する理由
アルゴリズムと鍵の種類が一致しない
よくあるトークンのヘッダーから JCA のアルゴリズムを導き出す一方で、静的に設定した鍵を使う JWT のコードで典型的に起こります。発行者が RS256 から ES256 に切り替えた後や、2 つの発行者のトークンが 1 つの検証者に届く場合です。
Ed25519 または X25519 の鍵を EC 鍵として扱っている
ときどきEd25519 鍵は楕円曲線の鍵ですが、ECDSA 鍵ではありません。`Signature.getInstance("Ed25519")`(JDK 15 以降)が必要です。X25519 鍵は鍵共有専用で、署名にはまったく使えません。
登録されていないプロバイダーの鍵オブジェクト
ときどきHSM/PKCS#11 プロバイダーや Bouncy Castle が作成した鍵は、そのプロバイダーでしか使えません。そのプロバイダーが登録されていない場合(または別のプロバイダーにアルゴリズムを要求した場合)、その鍵を受け付けるプロバイダーはありません。
修正方法
- 1.
アルゴリズムを決定し、鍵と照合する
許可する JWS アルゴリズムを JCA の名前に明示的に対応付け、鍵の種類が合わない組み合わせは拒否してください。そうすれば、失敗はプロバイダー検索のエラーではなく、明確なポリシーエラーになります。
java static Signature verifierFor(String jwsAlg, PublicKey key) throws GeneralSecurityException { String jca = switch (jwsAlg) { case "ES256" -> "SHA256withECDSAinP1363Format"; case "ES384" -> "SHA384withECDSAinP1363Format"; case "RS256" -> "SHA256withRSA"; case "PS256" -> "RSASSA-PSS"; default -> throw new GeneralSecurityException("algorithm not allowed: " + jwsAlg); }; boolean keyFits = jwsAlg.startsWith("ES") ? key instanceof java.security.interfaces.ECPublicKey : key instanceof java.security.interfaces.RSAPublicKey; if (!keyFits) throw new GeneralSecurityException(jwsAlg + " does not match a " + key.getAlgorithm() + " key"); Signature signature = Signature.getInstance(jca); if (jwsAlg.equals("PS256")) { signature.setParameter(new java.security.spec.PSSParameterSpec("SHA-256", "MGF1", java.security.spec.MGF1ParameterSpec.SHA256, 32, 1)); } signature.initVerify(key); return signature; }