这是"使用自签名证书和SSLEngine(JSSE)的SSL握手"的后续问题。
我已经实现了一个NIO Web服务器,可以在同一端口上处理SSL和非SSL消息。为了区分SSL和非SSL消息,我检查入站请求的第一个字节,看看它是否是SSL/TLS消息。例:
byte a = read(buf);
if (totalBytesRead==1 && (a>19 && a<25)){
parseTLS(buf);
}
在parseTLS()方法中,我实例化SSLEngine,发起握手,包装/解包消息等,对于大多数现代Web浏览器(Firefox 10,IE 9,Safari 5等)来说,一切似乎都很好。
问题在于,像IE 6这样的旧Web浏览器和像Java的URLConnection类这样的库似乎以不同的方式启动SSL/TLS握手。例如,IE 6 的前几个字节如下所示(十六进制值):
80 4F 01 03 00 ...
如果我将消息传递给 SSLEngine,它似乎无法识别该消息并引发异常。
javax.net.ssl.SSLException: Unsupported record version Unknown-0.0
那么IE 6和Java的URLConnection类究竟在发送什么呢?这是JSSE SSLEngine可以支持的有效SSL/TLS消息吗?我是否必须进行一些预处理或与客户协商才能发送不同的消息?
提前感谢!
更新
感谢 Bruno 和 EJP 以及一些进一步的调试,我对正在发生的事情有了更好的理解。正如Bruno正确指出的那样,IE6和Java 6客户端正在通过SSLv2 ClientHello发送。与我之前的评论相反,Java 1.6 中的 SSLEngine 实际上可以解开 SSLv2 消息并生成有效的响应以发送回客户端。我之前报告的SSLException是我的一个错误,与SSLEngine无关(我错误地认为客户端已经完成了发送数据,当SSLEngine期望更多数据解包时,我最终得到了一个空的ByteBuffer)。
这看起来像一个 SSLv2 客户端 你好(请参阅 TLS 规范):
支持 SSL 版本 2.0 服务器的 TLS 1.1 客户端必须发送 SSL版本 2.0 客户端问候消息 [SSL2]。 TLS 服务器应接受客户端问候格式(如果他们希望在 SSL 2.0 客户端上支持 SSL 2.0)相同的连接端口。 与2.0版的唯一偏差规范是指定值为三,并支持在密码规范中更多的加密类型。
-
80 4F
是长度,高位必须设置为 1(请参阅msg_length
说明)。 -
01
是消息类型(客户端你好) -
03 00
是支持的最高版本(此处为 SSLv3)
从 Java 7 开始,现在默认禁用此功能。
编辑:
澄清一下,这不是真正的SSLv2客户端Hello,这是SSLv2格式的SSLv3客户端Hello。在这种情况下,服务器将使用(正确的)SSLv3 Server Hello(对应于请求的版本号03 00
)进行回复。这同样适用于 TLS 1.0、1.1 和 1.2,尽管这种格式的使用已逐渐弃用。
JSSE 7 SSLServerSocket
仍然会理解这样的客户端问候,并使用 SSLv3/TLS1.x 服务器问候进行适当的回复。