浏览器端JS发起安全WebSocket连接失败:证书未知告警问题
问题描述
我的系统包含两台服务器:
- 一台基于boost::beast的自定义安全WebSocket服务器,监听887端口;
- 一台标准Apache2 HTTPS服务器,监听443端口,提供以下资源:
- 引用JavaScript文件的HTML页面;
- 客户端证书与密钥(已知该方式不安全,但非当前问题重点);
- 该JavaScript文件尝试使用客户端证书和密钥,连接887端口的安全WebSocket。
预期流程是用户访问该HTML页面后,浏览器自动发起安全WebSocket连接。我已通过openssl s_client验证了安全WebSocket服务器及证书信任链,确认服务器可正确验证客户端证书。但当浏览器运行下方JavaScript代码时,服务器返回‘sslv3 alert certificate unknown’握手错误,拒绝连接。
客户端代码
'use strict' window.onload = async function () { // N.B. all DOM element stuff is purely for debugging purposes. const div = document.getElementById('mainContainer'); div.innerHTML += 'Beginning of life<br />'; const cert_response = await fetch('wss-client.crt'); const key_response = await fetch('wss-client.key'); const certVal = await cert_response.text(); const keyVal = await key_response.text(); div.innerHTML += certVal + '<p />'; div.innerHTML += keyVal + '<p />'; const options = { key: keyVal, cert: certVal }; let socket = new WebSocket( 'wss://' + window.location.hostname + ':887', ["soap", "wamp", "sip", "amqp", "mqtt", "xmpp"], // I'm guessing here options); socket.onopen = function(e) { div.innerHTML += 'opened socket<br />'; }; };
我参考了WebSocket类文档(说明options可兼容https.request的参数)及证书指定文档,但本人主要从事C/C++开发,对JavaScript不够精通,可能遗漏了基础要点。
解决方案
你犯了一个关键错误:浏览器端的WebSocket API不支持通过options参数传入客户端证书和密钥。你参考的文档是Node.js环境下的ws库(服务端或Node.js客户端),而非浏览器原生的WebSocket接口。
浏览器处理客户端证书的逻辑完全不同:
- 浏览器只会在服务器明确要求客户端证书时,弹出选择框让用户从系统证书存储中选择,无法通过JavaScript代码直接注入证书/密钥。
- 你通过
fetch获取的证书和密钥文本,无法被浏览器用于TLS握手——浏览器不允许JS直接操作本地证书存储或自定义TLS参数。
修复方案
方案1:使用浏览器证书存储
- 将客户端证书导入到用户的浏览器(或系统)证书存储中。
- 确保你的WebSocket服务器配置为请求客户端证书(你已经通过
openssl s_client验证过这一点)。 - 移除代码中的
options参数,直接创建WebSocket连接:
let socket = new WebSocket( 'wss://' + window.location.hostname + ':887', ["soap", "wamp", "sip", "amqp", "mqtt", "xmpp"] );
当浏览器发起连接时,会自动弹出证书选择框,用户选择正确的客户端证书后即可完成握手。
方案2:代理转发(如果必须自动注入证书)
- 在Node.js环境中搭建一个WebSocket代理服务器,该代理使用
ws库加载客户端证书和密钥,转发浏览器的WebSocket请求到目标服务器。 - 浏览器连接代理服务器(无客户端证书要求),代理服务器与目标服务器建立带客户端证书的WSS连接。
补充说明
你提到的文档是Node.js生态的工具,和浏览器原生API差异极大。浏览器为了安全,严格限制了JS对TLS层的操作权限,这也是你无法通过代码注入证书的核心原因。
内容的提问来源于stack exchange,提问作者Paul Grinberg
相关产品推荐
相关产品推荐

