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?

18
demandé sur Golo Roden 2012-12-30 10:24:20

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 returning (pas throwing) une erreur s'il y a un problème et undefined si il est valide.

15
répondu Mitar 2016-02-29 23:37:17

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.

9
répondu Golo Roden 2012-12-30 09:57:25

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.

7
répondu Bruno 2017-05-23 11:54:56

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.

4
répondu Dzenly 2016-01-28 05:14:07

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.

-1
répondu Mike Scott 2012-12-30 08:45:17