相互SSL/TLS身份验证:从iOS返回Node.js将进行身份验证的证书
我创建了一个概念验证Node.js HTTPS服务器,其中包含来自可信CA的服务器证书(my-server.crt和my-server.key),并创建了自己的CA以创建客户端证书(CA.crt、client.key、client.crt和client.p12)。使用cURL和客户机PEM证书,我可以成功地使用这些客户机证书进行身份验证。但是,由于iOS客户端代码给出得更深,使用主捆绑包中嵌入的client.p12证书,我无法进行身份验证 这是我的简单Node.js服务器相互SSL/TLS身份验证:从iOS返回Node.js将进行身份验证的证书,ios,node.js,authentication,ssl,Ios,Node.js,Authentication,Ssl,我创建了一个概念验证Node.js HTTPS服务器,其中包含来自可信CA的服务器证书(my-server.crt和my-server.key),并创建了自己的CA以创建客户端证书(CA.crt、client.key、client.crt和client.p12)。使用cURL和客户机PEM证书,我可以成功地使用这些客户机证书进行身份验证。但是,由于iOS客户端代码给出得更深,使用主捆绑包中嵌入的client.p12证书,我无法进行身份验证 这是我的简单Node.js服务器 #!/usr/bin/
#!/usr/bin/env node
'use strict';
var https = require('https');
var http = require('http');
var fs = require('fs');
var port = process.argv[2] || 8043;
var insecurePort = process.argv[3] || 4080;
//
// Certificates
//
//Sync file reads- not for production
var options = {
key: fs.readFileSync("certs/my-server.key")
, cert: fs.readFileSync("certs/my-server.crt")
, ca: fs.readFileSync("certs/ca.crt")
, requestCert: true
, rejectUnauthorized: true
};
//
// HTTPS
//
var server = https.createServer(options, function (req, res) {
if (req.client.authorized) {
res.writeHead(200, {"Content-Type": "application/json"});
res.end('{"status":"approved"}');
} else {
res.writeHead(401, {"Content-Type": "application/json"});
res.end('{"status":"denied"}');
}
});
server.listen(port, function(){
console.log("\nListening for HTTPS traffic on port: " + port);
});
//
// HTTP
//
var insecureServer = http.createServer();
insecureServer.on('request', function (req, res) {
res.writeHead(200, {"Content-Type": "application/json"});
res.write('{"status":"insecure"}');
res.end();
});
insecureServer.listen(insecurePort, function(){
console.log("\nListening for HTTP traffic on port: " + insecurePort);
});
然后用这个cURL语句:
curl -v -s -k --key certs/client.key --cert certs/client.crt https://127.0.0.1:8043
我已成功通过身份验证(来自cURL的输出):
以下是我在iOS应用程序中的代码片段:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSString *p12Path = [[NSBundle mainBundle] pathForResource:@"client" ofType:@"p12"];
NSData *p12Data = [[NSData alloc] initWithContentsOfFile:p12Path];
CFStringRef password = CFSTR("DoubleTrouble");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef p12Items;
OSStatus result = SecPKCS12Import((__bridge CFDataRef)p12Data, optionsDictionary, &p12Items);
if(result == noErr) {
NSLog(@"Certificate Imported OK");
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0);
SecIdentityRef identityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);
SecCertificateRef certRef;
SecIdentityCopyCertificate(identityApp, &certRef);
SecCertificateRef certArray[1] = { certRef };
CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
CFRelease(certRef);
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:(__bridge NSArray *)myCerts persistence:NSURLCredentialPersistencePermanent];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
CFRelease(myCerts);
}
}
以下是我在XCode控制台中得到的信息:
2015-08-30 10:19:13.213 MutualTLS[3117:298681] Certificate Imported OK
2015-08-30 10:19:13.332 MutualTLS[3117:298763] CFNetwork SSLHandshake failed (-9806)
2015-08-30 10:19:13.394 MutualTLS[3117:298763] CFNetwork SSLHandshake failed (-9806)
2015-08-30 10:19:13.454 MutualTLS[3117:298763] CFNetwork SSLHandshake failed (-9806)
2015-08-30 10:19:13.455 MutualTLS[3117:298763] NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9806)
2015-08-30 10:19:13.457 MutualTLS[3117:298681] Error retrieving data, An SSL error has occurred and a secure connection to the server cannot be made.
当然,如果我在Node.js中创建HTTPS服务器的选项中设置rejectUnauthorized=false,则请求被接受,但仍然没有经过身份验证
2015-08-30 10:25:57.558 MutualTLS[3117:298681] Certificate Imported OK
2015-08-30 10:25:57.749 MutualTLS[3117:298681] {"status":"denied"}
我知道我可以保持拒绝=false,然后简单地返回
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
但这并非不受MITM利用的影响。如果您有任何建议,我们将不胜感激。为什么您认为这会导致MITM攻击?我怀疑iOS应用程序没有发送客户端证书。Wireshark将帮助您确认这一点。我并没有说它会导致MITM攻击,我说它不会对MITM攻击无动于衷。相互SSL/TLS身份验证的全部目的是帮助防止MITM攻击,并建议在需要额外安全级别的情况下,例如金融交易。在Wireshark中,我看到iOS客户端TLS1.2(在服务器的HELLO消息之后)响应证书、客户端密钥交换、更改密码规范,和2条加密握手信息。似乎缺少一条证书验证消息。我对使用Wireshark调试SSL相当陌生,因此不确定该如何处理。@MrTree您是否怀疑服务器接收到空请求-未收到证书-我在服务器上登录时发现了这一点。我看不出我创建证书的方式有任何错误,并且在提交证书时没有收到返回的错误。想法?为什么你认为这会导致MITM攻击?我怀疑iOS应用程序没有发送客户端证书。Wireshark将帮助您确认这一点。我并没有说它会导致MITM攻击,我说它不会对MITM攻击无动于衷。相互SSL/TLS身份验证的全部目的是帮助防止MITM攻击,并建议在需要额外安全级别的情况下,例如金融交易。在Wireshark中,我看到iOS客户端TLS1.2(在服务器的HELLO消息之后)响应证书、客户端密钥交换、更改密码规范,和2条加密握手信息。似乎缺少一条证书验证消息。我对使用Wireshark调试SSL相当陌生,因此不确定该如何处理。@MrTree您是否怀疑服务器接收到空请求-未收到证书-我在服务器上登录时发现了这一点。我看不出我创建证书的方式有任何错误,并且在提交证书时没有收到返回的错误。思想?
2015-08-30 10:25:57.558 MutualTLS[3117:298681] Certificate Imported OK
2015-08-30 10:25:57.749 MutualTLS[3117:298681] {"status":"denied"}
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];