
Java 默认在 TLS 1.3 握手中优先提供 ECDSA 签名算法,但可通过正确配置 jdk.tls.client.SignatureSchemes 系统属性,强制客户端仅协商指定的 RSA 签名方案(如 RSA-PKCS1-SHA256),从而排除 ECDSA 及 RSA-PSS 等非预期算法。
java 默认在 tls 1.3 握手中优先提供 ecdsa 签名算法,但可通过正确配置 `jdk.tls.client.signatureschemes` 系统属性,强制客户端仅协商指定的 rsa 签名方案(如 rsa-pkcs1-sha256),从而排除 ecdsa 及 rsa-pss 等非预期算法。
在 Java 11+(特别是 JDK 11 及以上支持 TLS 1.3 的版本)中,jdk.tls.client.SignatureSchemes 是控制客户端 Hello 中签名算法列表的核心系统属性。但关键在于:必须使用 JVM 规范定义的标准名称(IANA 注册名),而非 OpenSSL 或旧版 JCA 别名(如 RSAWITHSHA256)。你之前使用的 "RSAWITHSHA256,RSAWITHSHA384,RSAWITHSHA512" 并非有效值,因此 JVM 忽略该设置并回退至默认列表(含 ECDSA 和 RSA-PSS)。
✅ 正确做法是使用 IANA 定义的签名方案标识符,例如:
// 启动时设置(推荐) java -Djdk.tls.client.SignatureSchemes=rsa_pkcs1_sha256,rsa_pkcs1_sha384,rsa_pkcs1_sha512 MyApp
或在代码中动态设置(需在 SSLContext 初始化前生效):
System.setProperty("jdk.tls.client.SignatureSchemes",
"rsa_pkcs1_sha256:rsa_pkcs1_sha384:rsa_pkcs1_sha512");
SSLContext context = SSLContext.getInstance("TLSv1.3");
context.init(null, null, null);
⚠️ 注意事项:
- 所有标识符必须小写,且用英文冒号 : 分隔(JDK 文档明确要求,逗号分隔在某些版本中可能被忽略);
- rsa_pkcs1_sha256 对应传统 PKCS#1 v1.5 签名(即 RSA+SHA256),而非 RSA-PSS 或 ECDSA;
- 该属性仅影响 TLS 1.3;TLS 1.2 及更早版本使用独立的密钥交换与签名机制,不受此参数控制;
- 若服务端不支持所指定的 RSA-PKCS1 算法,握手将失败(handshake_failure alert),建议配合服务端能力验证;
- 验证是否生效:启用 -Djavax.net.debug=ssl:handshake,观察 ClientHello 中 signature_algorithms 扩展字段是否仅含 rsa_pkcs1_sha256 等三项,且无 ecdsa_secp256r1_sha256 等 ECDSA 条目。
总结:限制 TLS 1.3 客户端签名算法的关键,在于准确使用 IANA 标准名称(小写、冒号分隔)并确保配置时机早于 SSLContext 创建。通过 rsa_pkcs1_sha256:rsa_pkcs1_sha384:rsa_pkcs1_sha512,可彻底排除 ECDSA 与 RSA-PSS,实现严格的 RSA-only 签名协商策略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











