Nom d'hôte / IP ne correspond pas au nom du certificat
j'essaie de créer une configuration serveur / client TLS en utilisant le noeud.js 0.8.8 avec un certificat auto-signé.
le code du serveur essentiel ressemble à
var tlsServer = tls.createServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
}, function (connection) {
// [...]
});
tlsServer.listen(3000);
Maintenant, quand j'essaie de me connecter à ce serveur, j'utilise le code suivant:
var connection = tls.connect({
host: '192.168.178.31',
port: 3000,
rejectUnauthorized: true,
ca: [ fs.readFileSync('server-cert.pem') ]
}, function () {
console.log(connection.authorized);
console.log(connection.authorizationError);
console.log(connection.getPeerCertificate());
});
Si je supprime la ligne
ca: [ fs.readFileSync('server-cert.pem') ]
dans le code côté client, Nœud.js lance une erreur en me disant DEPTH_ZERO_SELF_SIGNED_CERT
. Autant je comprends ce que c'est dû au fait que c'est une auto-signé cert et il n'y a aucune autre partie qui fait confiance à ce certificat.
Si je supprime
rejectUnauthorized: true,
ainsi, l'erreur a disparu - mais connection.authorized
est égal à false
ce qui signifie que ma connexion n'est pas chiffrée. De toute façon, à l'aide de getPeerCertificate()
je peux accéder au certificat envoyé par le serveur. Comme je veux imposer une connexion cryptée, je comprends que je ne peux pas supprimer cette ligne.
maintenant je lis que je peux utiliser le ca
propriété pour spécifier toute CA que je voulez Nœud.JS à faire confiance. documentation du module TLS implique qu'il suffit d'ajouter le certificat de serveur pour l' ca
array, et alors tout devrait bien se passer.
Si je fais ça, cette erreur est allé, mais j'en ai un nouveau:
Hostname/IP doesn't match certificate's altnames
pour moi cela signifie que L'AC est maintenant fondamentalement fiable, donc c'est ok maintenant, mais le certificat a été fait pour un autre hôte que celui que j'utilise.
j'ai créé le certificat en utilisant
$ openssl genrsa -out server-key.pem 2048
$ openssl req -new -key server-key.pem -out server-csr.pem
$ openssl x509 -req -in server-csr.pem -signkey server-key.pem -out server-cert.pem
comme l'indique la documentation. Lors de la création du CSR on me pose les questions habituelles, comme pour le pays, l'état, ... et le nom commun (CN). Comme il vous est dit "sur le web" pour un certificat SSL vous faites indiquez votre nom comme CN, mais le nom d'hôte que vous souhaitez utiliser.
Et c'est probablement là où j'échoue.
je essayé
localhost
192.168.178.31
eisbaer
eisbaer.fritz.box
où les deux derniers sont le nom local et le complet le nom local de ma machine.
une idée de ce que je fais de mal ici?
5 réponses
Récemment, il y avait un ajout au noeud.js qui permet de modifier la vérification du nom d'hôte avec une fonction personnalisée. Il a été ajouté à v0.11.14 et sera disponible dans la prochaine version stable (0.12). Maintenant, vous pouvez faire quelque chose comme:
var options = {
host: '192.168.178.31',
port: 3000,
ca: [ fs.readFileSync('server-cert.pem') ],
checkServerIdentity: function (host, cert) {
return undefined;
}
};
options.agent = new https.Agent(options);
var req = https.request(options, function (res) {
//...
});
cette option accepte désormais toute identité de serveur, mais encrypte toujours la connexion et vérifie les clés.
Remarque: que dans les versions précédentes (par exemple v0.11.14
),checkServerIdentity
retour boolean
indiquant la validité du serveur. Qui a été changé (avant v4.3.1
) à la fonction return
ing (pas throw
ing) une erreur s'il y a un problème et undefined
si il est valide.
tls.js, lignes 112-141, vous pouvez voir que si le nom d'hôte utilisé lors de l'appel de connect
est une adresse IP, la CN du certificat est ignorée et seuls les SANs sont utilisés.
comme mon certificat n'utilise pas SANs, la vérification échoue.
si vous utilisez un nom d'hôte pour vous connecter, le nom d'hôte sera vérifié par rapport aux autres noms de sujet du type DNS, le cas échéant, et retombera sur le CN dans le nom de sujet distingué autrement.
si vous utilisez une adresse IP pour vous connecter, l'adresse IP sera cochée avec le SANs de adresse IP type, sans retomber sur le CN.
C'est au moins ce que les implémentations compatibles avec le HTTP over TLS spécification (i.e. HTTPS) do. Certains navigateurs sont un peu plus tolérants.
C'est exactement le même problème que dans cette réponse en Java, qui donne aussi une méthode pour mettre custom SANs via OpenSSL (voir ce document aussi).
en général, à moins que ce soit pour un CA test, il est assez difficile de gérer les certificats qui dépendent des adresses IP. Se connecter avec un nom d'hôte est mieux.
Mitar avait une fausse hypothèse que checkseridentity devrait retourner 'true' au succès, mais en fait il devrait retourner 'undefined' au succès. Toutes les autres valeurs sont traitées comme des descriptions d'erreur.
un tel code est donc correct:
var options = {
host: '192.168.178.31',
port: 3000,
ca: [ fs.readFileSync('server-cert.pem') ],
checkServerIdentity: function (host, cert) {
// It can be useful to resolve both parts to IP or to Hostname (with some synchronous resolver (I wander why they did not add done() callback as the third parameter)).
// Be carefull with SNI (when many names are bound to the same IP).
if (host != cert.subject.CN)
return 'Incorrect server identity';// Return error in case of failed checking.
// Return undefined value in case of successful checking.
// I.e. you could use empty function body to accept all CN's.
}
};
options.agent = new https.Agent(options);
var req = https.request(options, function (res) {
//...
});
j'ai juste essayé de faire edit dans la réponse du Mitar mais l'edit a été rejeté, donc j'ai créé une réponse séparée.
ce que vous faites de mal, c'est d'utiliser une adresse IP au lieu d'un nom de domaine. Créer un nom de domaine et le coller dans un serveur DNS (ou simplement dans un fichier hosts), créer un certificat auto-signé avec le nom de domaine comme nom commun, et se connecter au nom de domaine plutôt que l'adresse IP.