我创建了一个概念验证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/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输出):

* Rebuilt URL to: https://127.0.0.1:8043/
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Adding handle: conn: 0x7fa115009a00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fa115009a00) send_pipe: 1, recv_pipe: 0
* Connected to 127.0.0.1 (127.0.0.1) port 8043 (#0)
* error setting certificate verify locations, continuing anyway:
*   CAfile: /opt/local/share/curl/curl-ca-bundle.crt
  CApath: none
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Request CERT (13):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS handshake, CERT verify (15):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES256-GCM-SHA384
* Server certificate:
*    subject: OU=Domain Control Validated; CN=secure.wyldresearch.net
*    start date: 2015-08-25 00:32:39 GMT
*    expire date: 2016-08-25 00:32:39 GMT
*    issuer: C=US; ST=Arizona; L=Scottsdale; O=GoDaddy.com, Inc.; OU=http://certs.godaddy.com/repository/; CN=Go Daddy Secure Certificate Authority - G2
*    SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET / HTTP/1.1
> User-Agent: curl/7.34.0
> Host: 127.0.0.1:8043
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Sun, 30 Aug 2015 08:22:15 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
< 
* Connection #0 to host 127.0.0.1 left intact
{"status":"approved"}

最后,我创建了一个PoC iOS应用程序,并使用下面的语句将我使用导出密码“DoubleTrouble”创建的client.p12证书包含到应用程序包中 .

pkcs12 -export -in certs/client.crt -inkey keys/client.key -name "test cert" -out client.p12

这是我在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"}

我知道我可以拒绝承认=假,并简单地返回

[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

但这不会受到MITM漏洞的影响 . 任何建议将不胜感激 .