使用Bouncy Castle进行DTLS握手时遇未知HashAlgorithm错误求助
我之前用Bouncy Castle做DTLS开发时也踩过类似的坑,这个错误本质是服务器发送的签名哈希算法在你当前使用的Bouncy Castle版本中没有对应的枚举映射,导致解析失败触发内部错误。结合你提到的Wireshark抓包发现Certificate Request里有rsa_开头的算法,给你几个具体的解决思路:
1. 优先升级Bouncy Castle版本
旧版本的Bouncy Castle(比如1.70之前)对一些较新的签名哈希算法(比如RSASSA-PSS系列的rsa_pss_rsae_sha256、rsa_pss_rsae_sha384等)支持不全。建议直接升级到最新的稳定版(目前是1.77+),新版本已经补充了大部分主流算法的枚举定义,很多时候升级后这个问题就直接解决了。
2. 自定义签名哈希算法支持
如果升级版本后还是有问题,或者你不能升级版本,可以通过重写DefaultTlsClient的方法来手动添加对缺失算法的支持:
方法一:主动声明支持服务器需要的算法
重写getSignatureAndHashAlgorithms()方法,把服务器发送的未知算法加入到客户端支持列表中:
@Override public SignatureAndHashAlgorithm[] getSignatureAndHashAlgorithms() { // 获取默认支持的算法列表 List<SignatureAndHashAlgorithm> supportedAlgos = new ArrayList<>( Arrays.asList(super.getSignatureAndHashAlgorithms()) ); // 手动添加服务器要求的rsa_开头算法,比如rsa_pss_rsae_sha256 // 对应的哈希算法是sha256,签名算法是rsa_pss_rsae supportedAlgos.add(new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.rsa_pss_rsae)); // 如果还有其他未知算法,同理添加 // supportedAlgos.add(new SignatureAndHashAlgorithm(HashAlgorithm.sha384, SignatureAlgorithm.rsa_pss_rsae)); return supportedAlgos.toArray(new SignatureAndHashAlgorithm[0]); }
方法二:过滤服务器发送的未知算法
如果服务器发送了一些客户端确实不支持的算法,可以在收到Certificate Request时过滤掉这些算法,避免解析报错:
@Override public void notifyCertificateRequest(CertificateRequest certificateRequest) throws IOException { // 过滤掉客户端不识别的签名哈希算法 SignatureAndHashAlgorithm[] filteredAlgos = Arrays.stream(certificateRequest.getSignatureAndHashAlgorithms()) .filter(algo -> { try { // 验证哈希算法是否已知 HashAlgorithm.getName(algo.getHash()); // 验证签名算法是否已知 SignatureAlgorithm.getName(algo.getSignature()); return true; } catch (IllegalArgumentException e) { // 忽略未知算法,打印日志方便排查 System.out.println("忽略未知的签名哈希算法: hash=" + algo.getHash() + ", signature=" + algo.getSignature()); return false; } }) .toArray(SignatureAndHashAlgorithm[]::new); // 调用父类方法,传入过滤后的算法列表 super.notifyCertificateRequest(new CertificateRequest( certificateRequest.getCertificateTypes(), certificateRequest.getSupportedSignatureAlgorithms(), certificateRequest.getCertificateAuthorities(), filteredAlgos )); }
3. 确认密钥对与算法的兼容性
如果服务器要求的是RSASSA-PSS这类特殊签名算法,你之前生成的普通RSA密钥对可能不兼容。需要生成支持PSS的密钥对,或者在签名时指定对应的PSS参数。比如:
// 生成RSASSA-PSS密钥对 KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC"); kpg.initialize(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4)); KeyPair keyPair = kpg.generateKeyPair(); // 签名时使用PSS参数 Signature signature = Signature.getInstance("RSASSA-PSS", "BC"); signature.initSign(keyPair.getPrivate(), new PSSParameterSpec( "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1 ));
4. 调试定位具体未知算法
如果以上方法都没解决,可以在报错的地方添加日志,打印服务器发送的所有签名哈希算法的编码值:
// 在notifyCertificateRequest方法中打印所有算法 for (SignatureAndHashAlgorithm algo : certificateRequest.getSignatureAndHashAlgorithms()) { System.out.println("服务器发送的算法: hash=" + algo.getHash() + ", signature=" + algo.getSignature()); }
然后对照Bouncy Castle源码中的HashAlgorithm和SignatureAlgorithm枚举类,看哪个编码没有对应的定义,再针对性地添加支持。
内容的提问来源于stack exchange,提问作者Rashed

