我已经设置了一个 HTTPS 服务器(nodejs v14.16.0)和来自letsEncrypt 的证书(在使用 https.createServer 的应用程序的当前版本中工作)。不幸的是,curl 无法成功连接到我的 HTTPS 服务器。我收到以下错误
routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
这是我的服务器的最低可重现版本
const https = require('https')
const tls = require('tls');
const fs = require('fs');
const constants = require('constants');
tls.DEFAULT_ECDH_CURVE = "auto"
require('dotenv').config()
const certOpts = {
key: `${process.env.KEY_PATH}/privkey.pem`,
cert: `${process.env.KEY_PATH}/cert.pem`,
ca: `${process.env.KEY_PATH}/chain.pem`,
};
/**
*
* @param {Record<string, string>} filePathMap
* @returns {Record<string, Buffer>}
*/
function getBuffersFromFilePathMap(filePathMap) {
const bufferMap = {}
for (const path in filePathMap) {
if (Object.hasOwnProperty.call(filePathMap, path)) {
bufferMap[path] = fs.readFileSync(filePathMap[path]);
}
}
return bufferMap;
}
const buffers = getBuffersFromFilePathMap(certOpts);
function createContext() {
return tls.createSecureContext({
...buffers,
secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_TLSv1 | constants.SSL_OP_NO_TLSv1_1,
maxVersion:'TLSv1.2'
});
}
const server = https.createServer({ secureContext: createContext() }, (req, res) => {
// eslint-disable-next-line no-console
console.log('connect');
res.writeHead(200);
res.write('Hello World!\n');
res.end('Goodbye World!\n');
});
server.listen(9999, () => console.log('Server up: ', server.address()));
这是curl --version
curl 7.61.1 (x86_64-redhat-linux-gnu) libcurl/7.61.1 OpenSSL/1.0.2k zlib/1.2.8 libidn2/2.3.0 libpsl/0.6.2 (+libicu/50.1.2) libssh2/1.4.2 nghttp2/1.31.1
Release-Date: 2018-09-05
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz HTTP2 UnixSockets HTTPS-proxy PSL
据我所知,ssl3 可能不受支持。
curl 输出
$ curl -k https://localhost:9999
curl: (35) error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
卷曲痕迹
== Info: Rebuilt URL to: https://localhost:9999/
== Info: Trying 127.0.0.1...
== Info: TCP_NODELAY set
== Info: Connected to localhost (127.0.0.1) port 9999 (#0)
== Info: ALPN, offering h2
== Info: ALPN, offering http/1.1
== Info: Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
== Info: successfully set certificate verify locations:
== Info: CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
== Info: TLSv1.2 (OUT), TLS header, Certificate Status (22):
=> Send SSL data, 5 bytes (0x5)
0000: .....
== Info: TLSv1.2 (OUT), TLS handshake, Client hello (1):
=> Send SSL data, 512 bytes (0x200)
0000: .......*.c....}.5.H>^..e\;)}.zEW.....d....0.,.(.$.............k.
0040: j.i.h.9.8.7.6.........2...*.&.......=.5.../.+.'.#.............g.
0080: @.?.>.3.2.1.0.........E.D.C.B.1.-.).%.......<./...A.............
00c0: ............3.........localhost......................... .......
0100: ..............................3t.........h2.http/1.1............
0140: ................................................................
0180: ................................................................
01c0: ................................................................
== Info: TLSv1.2 (IN), TLS header, Unknown (21):
<= Recv SSL data, 5 bytes (0x5)
0000: .....
== Info: TLSv1.2 (IN), TLS alert, handshake failure (552):
<= Recv SSL data, 2 bytes (0x2)
0000: .(
== Info: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
答案 0 :(得分:0)
好的,尽管 tls.createSecureContext
的文档说结果“可用作多个 tls API(例如 tls.createServer)的参数”,但实际上并非如此。它被接受server.addContext
(对于虚拟主机或更确切地说是 SNI 值处理程序)tls.connect
(对于客户端)tls.createSecurePair
(已弃用)和 {{ 1}}(低级),但 new TLSSocket
只采用与相同的选项,createServer
不是实际的 createSecureContext
。由于您没有以可用的形式提供所需的密钥和证书,并且默认情况下 OpenSSL 禁用匿名密码套件(大多数客户端无论如何都不提供),因此所有握手都会因 no_shared_cipher 失败。试试:
SecureContext
顺便说一句,当您收到 request 而不是连接时,会调用 req,res=> 函数;一个合适的客户端只会在需要发出请求时才连接,但可能在不发出请求的情况下进行连接,相反,在一个连接上执行多个请求也可以。