错误信息
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。消息末尾的类名会告诉你实际收到的是哪种密钥。
原因
算法与密钥类型不匹配
常见常见于这样的 JWT 代码:根据令牌头部推导 JCA 算法,却使用静态配置的密钥——在颁发者从 RS256 切换到 ES256 之后,或者两个颁发者的令牌到达同一个验证方时就会出现。
把 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; }