使用SSHy与sshpk-browser实现SSH公钥认证时签名无效问题排查
我在浏览器端使用SSHy库实现SSH设备连接,正在添加公钥认证支持,但服务器始终返回签名无效错误。不带签名的第一个SSH_MSG_USERAUTH_REQUEST能收到SSH_MSG_USERAUTH_PK_OK响应,但发送带签名的请求时,总是得到SSH_MSG_USERAUTH_FAILURE。我用sshpk-browser库进行签名,基于RFC4252规范构造签名,代码如下,求排查构造过程中的潜在问题。
const decodedPublicKey = config.privateKey.toPublic().toString('ssh', { hashAlgo: 'sha512' }).split(' ')[1]; const publicKey = atob(decodedPublicKey); var m = new SSHyClient.Message(); m.add_bytes(String.fromCharCode(SSHyClient.MSG_USERAUTH_REQUEST)); m.add_string(this.termUsername); m.add_string('ssh-connection'); m.add_string('publickey'); m.add_boolean(true); // has signature m.add_string('rsa-sha2-512'); // public key algorithm name m.add_string(publicKey); // public key // Create signature var sigMsg = new SSHyClient.Message(); sigMsg.add_string(SSHyClient.kex.sessionId); sigMsg.add_bytes(String.fromCharCode(SSHyClient.MSG_USERAUTH_REQUEST)); sigMsg.add_string(this.termUsername); sigMsg.add_string('ssh-connection'); sigMsg.add_string('publickey'); sigMsg.add_boolean(true); // has signature sigMsg.add_string('rsa-sha2-512'); sigMsg.add_string(publicKey); const sigMsgString = sigMsg.toString(); // Sign signature const sign = config.privateKey.createSign('sha512'); sign.update(sigMsgString); const signature = sign.sign(); m.add_string(atob(signatureToString)); // signature this.parceler.send(m);
潜在问题及修正方案
1. 未定义变量引用错误
代码最后一行使用了未定义的signatureToString变量,实际签名结果存储在signature变量中,这会导致传入atob的参数无效,生成错误的签名内容。
修正:
根据sshpk的返回值类型调整:
// 若sign.sign()返回Buffer,转base64后再解码为二进制字符串 m.add_string(atob(signature.toString('base64'))); // 若sign.sign()直接返回base64字符串 m.add_string(atob(signature));
2. 签名的SSH格式封装缺失
SSH协议要求签名字段必须是包含算法名和签名二进制的结构化字符串(格式为string(算法名) + string(签名二进制)),直接传入原始签名数据不符合规范。
修正:
先构造符合SSH规范的签名封装结构,再添加到请求中:
// 构造签名的SSH格式包装 const sigWrapper = new SSHyClient.Message(); sigWrapper.add_string('rsa-sha2-512'); sigWrapper.add_string(signature); // signature需为二进制格式 // 将包装后的签名加入主请求 m.add_string(sigWrapper.toString());
3. 签名数据的编码损坏
sigMsg.toString()会将二进制消息转换为UTF-8字符串,这会破坏非ASCII字节,导致签名内容与服务器预期的原始二进制流不符。
修正:
使用SSHy Message提供的原始二进制获取方法(如toBuffer()或getBytes(),取决于SSHy API):
// 替换sigMsg.toString()为原始二进制数据 const sigMsgBinary = sigMsg.toBuffer(); const sign = config.privateKey.createSign('sha512'); sign.update(sigMsgBinary); const signature = sign.sign();
4. 签名算法指定不精确
仅指定sha512可能无法触发rsa-sha2-512组合算法,部分库需要完整的算法标识符来确保签名逻辑匹配。
修正:
改用完整的算法名称:
const sign = config.privateKey.createSign('rsa-sha2-512');
5. 公钥处理的冗余步骤
手动拆分SSH格式公钥的base64部分再解码的步骤可以简化,直接使用sshpk提供的二进制公钥获取方法,避免手动解析出错。
修正:
// 直接获取公钥的二进制数据 const publicKey = config.privateKey.toPublic().toBuffer().toString('binary');
内容的提问来源于stack exchange,提问作者andahan

