2014-05-12 522 views
14

我正在使用節點0.10.26並嘗試與客戶端驗證建立https連接。服務器端的客戶端證書驗證DEPTH_ZERO_SELF_SIGNED_CERT錯誤

服務器的代碼:

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 

我已經運行客戶端和服務器與客戶端和服務器的所有證書組合證書(塔t是:[(server1,client1),(server1,client2),(server2,client1),(server2,client2)],並且對這些服務器的每個組合都進行了測試,默認值爲「agent」 「設置爲」false「。

每次我運行client.js,res.req.connection.authorizationError被設置爲DEPTH_ZERO_SELF_SIGNED_CERT。

如何在客戶端證書身份驗證的節點中建立安全連接?

+0

+1你馬立克爲是一類的行爲給@rhashimoto一個+50賞金。 –

回答

16

我相信你有兩個問題,一個是你的代碼,另一個是你的證書。

代碼問題出現在您的服務器上。你是不是指定CA檢查客戶端證書與options屬性就像你在你的客戶端代碼有:

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

的第二個問題是一個真正導致該DEPTH_ZERO_SELF_SIGNED_CERT錯誤。您正在提供所有證書 - CA,服務器和客戶端 - 相同的專有名稱。當服務器從客戶端證書中提取頒發者信息時,它會看到頒發者DN與客戶端證書DN相同,並得出結論認爲客戶端證書是自簽名的。

嘗試重新生成您的證書,爲每個證書賦予唯一的通用名稱(以使DN也是唯一的)。例如,將您的CA證書命名爲「Foo CA」,您的服務器證書是您主機的名稱(本例中爲「localhost」),您的客戶端是其他名稱(例如「Foo Client 1」)。

+1

你先生,是個天才!我遇到了確切的問題,即我的CA具有與服務器證書相同的CN。我沒有意識到這是一個問題,但現在我讀了你的答案,我覺得自己像個大笨蛋。 –

10

對於那些想要使用自簽名證書,答案是將rejectUnauthorized: false添加到https.request選項。

+2

簡單而重要;非常感謝你。 – gustavohenke

+0

對我不起作用:( –

+0

downvote,因爲它不安全,因爲前面的答案給出了問題的原因 –

0

如上所述,使用rejectUnauthorized: false時,有一把大錘釘在你的指甲上。

從安全角度來看,更明智的選擇是詢問用戶是否願意接受和存儲自簽名服務器證書,就像瀏覽器(或SSH)一樣。

這就需要:

(1)的NodeJS拋出一個異常包含服務器證書,並

(2)在應用程序調用https.request與在ca:屬性存儲的證書(證書已被手動接受後,請參閱上面有關ca的說明)。

看來,NodeJS不做(1),使(2)不可能?

從安全角度來看,更好的做法是使用EFF的SSL觀察站對自簽名證書的有效性做出羣衆來源的判斷。同樣,這需要NodeJS完成(1)。

我認爲開發者需要改善相對於(1)...

+0

事實上,如果您使用rejectUnauthorized:false進行連接,則無法事件獲取服務器證書。您可以在請求,響應,連接等對象上一直使用util.inspect(),並且您將永遠無法獲得NodeJS收到的證書。現在,您只需通過代碼接受所有證書或CA簽名證書即可。 –

0

只需添加strictSSL: falseoptions的NodeJS。

1

儘管本頁面有很長的描述,但我仍然在客戶端使用這個配方得到'UNABLE_TO_VERIFY_LEAF_SIGNATURE'錯誤。也許我在遵循rhashimoto's comment的時候出了點問題。

通過參考書'Professional Node.js',我找到了另一種方法來成功測試HTTPS客戶端證書驗證。
這是我的故事。
通過在服務器端設置requestCert: true,服務器試圖驗證客戶端證書。但是默認CA不驗證客戶端的自簽名證書。我可以通過簡單的訣竅來成功完成測試 - 複製客戶端證書並說這是一個證書頒發機構。

我重複使用original code並稍微修改它以使其工作。 最大的區別在於創建證書文件。

創建證書文件:

# 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 

Server代碼:

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(); 
+0

如果您有葉節點錯誤,這是因爲您沒有將中間證書包含在服務器證書中。您必須將證書與鏈中的所有中間證書連接起來。 –

6

這一個工作:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 

注意:發佈答案,以便將來可以幫助他人。

+1

雖然有效,但它具有全球效應。根據需要拒絕未授權:錯誤是安全的。 –

+1

用於我的開發測試。 – sfanjoy

0

如果你只有一個。質子交換膜自簽名證書(如/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 
};