web-dev-qa-db-ja.com

サーバー側でのクライアント証明書の検証、DEPTH_ZERO_SELF_SIGNED_CERTエラー

ノード0.10.26を使用していて、クライアント検証でhttps接続を確立しようとしています。

サーバーのコード:

var https = require('https');
var fs = require('fs');

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var options = {
    key: fs.readFileSync('ssl/server1.key'),
    cert: fs.readFileSync('ssl/server1.pem'),
    requestCert: true,
    rejectUnauthorized: false,
};

var server = https.createServer(options, function (req, res) {
    if (req.client.authorized) {
        res.writeHead(200, {"Content-Type":"application/json"});
        res.end('{"status":"approved"}');
        console.log("Approved Client ", req.client.socket.remoteAddress);
    } else {
        console.log("res.connection.authroizationError:  " + res.connection.authorizationError);
        res.writeHead(403, {"Content-Type":"application/json"});
        res.end('{"status":"denied"}');
        console.log("Denied Client " , req.client.socket.remoteAddress);
    }
});

server.on('error', function(err) {
    console.log("server.error: "  + err);
});

server.on("listening", function () {
    console.log("Server listeining");
});

server.listen(5678);

クライアントのコード:

var https = require('https');
var fs = require('fs');

var options = {
    Host: 'localhost',
    port: 5678,
    method: 'GET',
    path: '/',
    headers: {},
    agent: false,
    key: fs.readFileSync('ssl/client2.key'),
    cert: fs.readFileSync('ssl/client2.pem'),
    ca: fs.readFileSync('ssl/ca.pem')
};

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var req = https.request(options, function(res) {
    console.log(res.req.connection.authorizationError);
});

req.on("error", function (err) {
    console.log('error: ' + err);
});

req.end();

次のコマンドを使用して証明書を作成しました。毎回「uname -n」の結果を「共通名」として提供しています。

openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -days 999 -out ca.pem

openssl genrsa -out server1.key  1024
openssl req -new -key server1.key -out server1.csr
openssl x509 -req -days 999 -in server1.csr -CA ca.pem  -CAkey ca.key -set_serial 01 -out server1.pem

openssl genrsa  -out client1.key 1024
openssl req -new -key client1.key  -out client1.csr
openssl  x509  -req -days 999 -in client1.csr  -CA ca.pem  -CAkey ca.key  -set_serial 01     -out client1.pem

openssl genrsa  -out server2.key 1024
openssl req -new -key server2.key  -out server2.csr
openssl  x509  -req -days 999 -in server2.csr -CA server1.pem -CAkey server1.key -     set_serial 02 -out server2.pem

openssl  genrsa -out client2.key 1024
openssl req -new -key client2.key -out client2.csr
openssl x509 -req -days 999 -in client2.csr -CA client1.pem -CAkey client1.key  -set_serial 02 -out client2.pem

クライアントとサーバーの証明書のすべての組み合わせ(つまり:((server1、client1)、(server1、client2)、(server2、client1)、(server2、client2)))を使用してクライアントとサーバーを実行し、それらの組み合わせごとにサーバーは、「agent」フィールドのデフォルト値と「false」に設定された「agent」の両方でテストされました。

Client.jsを実行するたびに、res.req.connection.authorizationErrorがDEPTH_ZERO_SELF_SIGNED_CERTに設定されました。

クライアントの証明書認証を使用してノードで安全な接続を確立するにはどうすればよいですか?

17
Marek

私はあなたが2つの問題を抱えていると信じています。

コードの問題はサーバーにあります。クライアントコードにあるように、optionsプロパティを使用してクライアント証明書をチェックするようにCAを指定していません。

ca: fs.readFileSync('ssl/ca.pem'),

2番目の問題は、実際にDEPTH_ZERO_SELF_SIGNED_CERTエラーを引き起こす問題です。すべての証明書(CA、サーバー、およびクライアント)に同じ識別名を付けます。サーバーがクライアント証明書から発行者情報を抽出すると、発行者DNがクライアント証明書DNと同じであることがわかり、クライアント証明書が自己署名されていると結論付けます。

証明書を再生成して、それぞれに一意の共通名を付けてください(DNも一意にするため)。たとえば、CA証明書に「Foo CA」、サーバー証明書にホストの名前(この場合は「localhost」)、クライアントに別の名前(「Foo Client 1」など)を付けます。

23
rhashimoto

wantを使用して自己署名証明書を使用するには、https.requestオプションにrejectUnauthorized: falseを追加してください。

14
B T

これは私のために働きました:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

注:回答を投稿することで、他の人を助けることができます。

8
Manwal

このページの説明は長いものの、このレシピを使用したクライアント側で「UNABLE_TO_VERIFY_LEAF_SIGNATURE」エラーが発生しました。 rhashimotoのコメント をフォローするときに何か問題があったのかもしれません。

' Professional Node.js 'の本を参照することで、クライアント証明書検証を使用してHTTPSをテストする別の方法を見つけました。
これが私の話です。
設定することにより requestCert: trueサーバー側では、サーバーはクライアント証明書の検証を試みます。ただし、デフォルトのCAは、クライアントの自己署名証明書を検証しません。私は簡単なトリックでテストを成功させることができます-クライアント証明書をコピーして、それが認証局だと言います。

元のコード を再利用して、動作するように少し変更しました。大きな違いは、証明書ファイルの作成です。

証明書ファイルの作成:

# create client private key
openssl genrsa -out client2.key
openssl req -new -key client2.key -out client2.csr
# create client certificate
openssl x509 -req -in client2.csr -signkey client2.key -out client2.pem

# create server private key and certificate
openssl genrsa -out server1.key
openssl req -new -key server1.key -out server1.csr
openssl x509 -req -in server1.csr -signkey server1.key -out server1.pem

# * Important *: create fake CA with client certificate for test purpose
cp client2.pem fake_ca.pem

サーバーコード:

var options = {
    key: fs.readFileSync('ssl/server1.key'),
    cert: fs.readFileSync('ssl/server1.pem'),
    ca: [fs.readFileSync('ssl/fake_ca.pem')], // Line added
    requestCert: true,
    rejectUnauthorized: false,
};

クライアントコード:

    key: fs.readFileSync('ssl/client2.key'),
    cert: fs.readFileSync('ssl/client2.pem'),
    //ca: fs.readFileSync('ssl/ca.pem') // Line commented
};

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var req = https.request(options, function(res) {
    //console.log(res.req.connection.authorizationError);
    console.log("statusCode:", res.statusCode);
    res.on('data', function(d) {
      console.log('data:', d.toString());
    });
});
req.on("error", function (err) {
    console.log('error: ' + err);
});
req.end();
2
Hill

optionsstrictSSL: falseを追加するだけです。

1
taco

上記のように、rejectUnauthorized: falseを使用して、釘をハンマーで叩くためのハンマーがあります。

セキュリティの観点から、より賢明なオプションは、ブラウザ(またはSSH)と同じように、ユーザーに自己署名サーバー証明書を受け入れて保存するかどうかを尋ねることです。

それには以下が必要です:

(1)そのNodeJSがサーバー証明書を含むという例外をスローし、

(2)証明書が手動で受け入れられた後、アプリケーションはhttps.requestプロパティに格納された証明書を使用してca:を呼び出します(caの説明については上記を参照)。

NodeJSは(1)を行わず、(2)を不可能にしたようですか?

セキュリティの観点からさらに良いのは、EFFのSSLオブザーバトリーを使用して、自己署名証明書の有効性についてクラウドソースの判断を下すことです。この場合も、NodeJSが実行する必要があります(1)。

開発者は(1)に関してNodeJSを改善する必要があると思います...

0
Jim Shark

.pem自己署名証明書(/tmp/ca-keyAndCert.pemなど)しかない場合は、次のオプションが機能します。

var options = {
      url: 'https://<MYHOST>:<MY_PORT>/',
      key: fs.readFileSync('/tmp/ca-keyAndCert.pem'),
      cert: fs.readFileSync('/tmp/ca-keyAndCert.pem'),
      requestCert: false,
      rejectUnauthorized: false
};
0
AR1