-
Notifications
You must be signed in to change notification settings - Fork 7.3k
SSL Client Certificates Mandatory/Flakey #1494
Comments
Can you post a standalone (pure node, no third-party libs) test case that showcases that behaviour? You can find test certs in |
Below is test code to illustrate the problem (or at least what I think is a problem). On the client side, just use whatever key and cert you have handy...on the server, I used a test key and cert from the node distribution. If requestCert is set to true, then it appears browsers will always ask the user for a client cert (that seems like the correct behavior). But, if requestCert is set to false, the browser doesn't request a certificate from the user, but getPeerCertificate() will return an empty object even if the client provided a certificate (as this example client does). Now, I think I've found another issue in the latest master...if I run the client on the latest master, geetPeerCertificate() on the server side returns an empty object even if requestCert is set to true. If I run the client with node 0.4.9 however, getPeerCertificate() yields the client certificate info (server was running the latest master in both cases). It seems to me like the correct behavior should be that getPeerCertificate() should return a peer certificate whenever one is provided regardless of whether requestCert was set to true or false. The server side: var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('../test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('../test/fixtures/keys/agent2-cert.pem'),
requestCert: true,
rejectUnauthorized: false,
};
https.createServer(options, function (req, res) {
console.log(req.connection.getPeerCertificate());
res.writeHead(200);
res.end("hello world\n");
}).listen(8000); The client side: var fs = require('fs'),
https = require('https');
var key = fs.readFileSync('./client.key');
var cert = fs.readFileSync('./client.cert');
var options = {
host: '127.0.0.1',
port: 8000,
path: '/',
key: key,
cert: cert,
};
var req = https.request(options, function(res) {
console.log("statusCode: ", res.statusCode);
console.log("headers: ", res.headers);
res.on('data', function(d) {
process.stdout.write(d);
});
});
req.end();
req.on('error', function(e) {
console.error(e);
}); |
I am no expert on SSL/TLS, but I think it is correct behavior.
Good catch! This is the problem of http2. |
No, I was wrong. var options = {
host: '127.0.0.1',
port: 8000,
path: '/',
key: key,
cert: cert,
agent: new https.Agent(options)
}; It is not bad because it cannot share connections with other HTTPS request which does not use client certification. |
This looks like the error I was getting in chrome when doing the tests with node on XP as reported at #1492 It seems that node does not respond to SSL type client hello's, as listed in wireshark network protocol analyser for Windows. The client hello must be type SSLv2 or TLSv1.0 for node to respond. Chrome 12 gives up after two tries and issues the PROTOCOL error message. There is a similar problem with Firefox (tested on FF5) where it tries many times with SSL type client hello's and then issues a "The connection was reset" error. IE8 is ok because after issuing an SSL type it then issues an SSLv2 type and node reponds. Opera 11.5 is ok too. The problem only happens if a 'requestCert: true' is included in the options object. If not included then I believe no SSL type client hello's are issued by browsers and all is well with node. All of this reported here is just what I saw in the wireshark analyser. |
So, how would I alter the client code listed here such that it works in 0.4.9, 0.4.10 and the latest master? I don't really understand the following code...seems like at the point when the new Agent is instantiated, options would be undefined: var options = {
host: '127.0.0.1',
port: 8000,
path: '/',
key: key,
cert: cert,
agent: new https.Agent(options)
}; |
Ooooops, correction: var options = {
host: '127.0.0.1',
port: 8000,
path: '/',
key: key,
cert: cert
};
options.agent = new https.Agent(options); It works both v0.4.10 and v0.5.4 (both http1 and http2). |
I think you'll find that it is node that is behaving poorly not Chrome. Chrome reports that error because node does not handshake the client hello properly if requestCert is true, as per my above comment. |
we should get this documented properly once everyone is happy with a solution. |
Regarding Chrome "Error 107 (net::ERR_SSL_PROTOCOL_ERROR): SSL protocol error)" reported above. This has now been identified as a node crypto problem and there is a workaround by koichik listed at:- The workaround is awaiting approval for inclusion in node proper. |
When I set requestCert to true and rejectUnauthorized to false, Safari reports that the server requires a client SSL certificate. And, Chrome appears to behave poorly (it doesn't require the user to provide a certificate, but for some connections it reports "Error 107 (net::ERR_SSL_PROTOCOL_ERROR): SSL protocol error."). If I don't set requestCert to true, then even if I connect with a client where a cert has been used, the cert isn't present on the server side (getPeerCertificate() returns an empty object). I'm not sure if the certificate is inaccessible because the session isn't being signed with the certificate, or if it is, but node is just ignoring it and not exposing it in JavaScript.
The behavior I am seeking is to allow clients without a certificate to connect and use a website, while at the same time providing API access to clients that authenticate using a client certificate. As a workaround, I'll run a second https server on a separate port for API access, but this is not ideal and it seems there should be a way to make this work.
The text was updated successfully, but these errors were encountered: