iOS应用SSL .p12身份验证 - 证书错误(-9825)

njt*_*man 5 ssl objective-c ios cordova mknetworkkit

更新

编辑2/6/14:

我创建了一个本地Apache Tomcat服务器来测试带有证书身份验证的SSL.我成功了!使用下面的两种方法,一切都按预期工作.(MKNetworkKit和自定义代码).虽然这确实告诉我我的代码正在运行,但我的原始问题仍未解决.我更新了问题的标题,以更具体地反映问题.有谁知道SAP Portal是否需要特殊设置来接受来自iOS应用程序的证书?请记住,在将CA和.p12导入共享密钥链后,我能够使用Safari移动设备成功进行身份验证,我只是在代码中失败(我现在知道代码可以工作,而不是使用门户网站).



我正在创建一个非常简单的iOS7 Cordova 3.2应用程序,其中包含一个自定义插件,通过仅提供.p12证书进行身份验证(无需基本身份验证或其他用户凭据)从SSL Web服务获取数据.我在物理iPad(没有模拟器)上执行所有测试.Web服务使用自签名证书存在于开发SAP NetWeaver门户框中.目前,我将服务器的CA导入iOS钥匙串以避免证书信任错误.出于测试目的,我的.p12证书在应用程序内部捆绑在mainBundle的根目录中.

尝试连接到Web服务时,我在控制台中收到以下错误:

CFNetwork SSLHandshake failed (-9825)
NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9825)
Error Domain=NSURLErrorDomain Code=-1205 "The server “myhostremoved.com” did not accept the certificate." UserInfo=0x14e99000 {NSErrorFailingURLStringKey=https://myhostremoved.com/sslwebservice/, NSErrorFailingURLKey=https://myhostremoved.com/sslwebservice/, NSLocalizedDescription=The server “myhostremoved.com” did not accept the certificate., NSUnderlyingError=0x14d9b1d0 "The server “myhostremoved.com” did not accept the certificate.", NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x14d94720>}
Run Code Online (Sandbox Code Playgroud)

根据Apple的文档站点,-9825错误引用了错误的证书.

有很多关于SO的问题与我想要做的事情有关,但没有一个与我所看到的错误有关.我以两种不同的方式处理代码的开发.


首先,我尝试使用已经在SO上的代码,使其适应我的用例.见下面的代码:

- (void)startConnection:(CDVInvokedUrlCommand*)command {
    NSDictionary *options = [command.arguments objectAtIndex:0];
    NSURL *serverURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@", [options objectForKey:@"host"]]];//hostname provided by Cordova plugin, but could just as easily be hardcoded here
    NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:serverURL];
    NSURLConnection *connection = nil;
    connection = [[NSURLConnection alloc] initWithRequest:connectionRequest delegate:self startImmediately:YES];
}

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
        // gets a certificate from local resources
        NSString *thePath = [[NSBundle mainBundle] pathForResource:@"mycert" ofType:@"p12"];
        NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
        CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;

        SecIdentityRef identity;
        // extract the ideneity from the certificate
        [self extractIdentity :inPKCS12Data :&identity];

        SecCertificateRef certificate = NULL;
        SecIdentityCopyCertificate (identity, &certificate);

        const void *certs[] = {certificate};
        CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
        // create a credential from the certificate and ideneity, then reply to the challenge with the credential
        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];

        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];

}

- (OSStatus)extractIdentity:(CFDataRef)inP12Data :(SecIdentityRef*)identity {
    OSStatus securityError = errSecSuccess;

    CFStringRef password = CFSTR("MyCertPassw0rd");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };

    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    securityError = SecPKCS12Import(inP12Data, options, &items);

    if (securityError == 0) {
        CFDictionaryRef ident = CFArrayGetValueAtIndex(items,0);
        const void *tempIdentity = NULL;
        tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);
        *identity = (SecIdentityRef)tempIdentity;

    }

    if (options) {
        CFRelease(options);
    }

    return securityError;
}
Run Code Online (Sandbox Code Playgroud)


在我的第二种方法中,我尝试使用MKNetworkKit库,它抽象出许多与证书接口所需的代码.您需要做的就是提供证书和密码的路径.再次,我得到与上面相同的错误.这段代码如下.

- (void)startConnection:(CDVInvokedUrlCommand*)command {
    NSDictionary *options = [command.arguments objectAtIndex:0];
    NSURL *serverURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@", [options objectForKey:@"host"]]];//hostname provided by Cordova plugin, but could just as easily be hardcoded here

    MKNetworkEngine *engine = [[MKNetworkEngine alloc] initWithHostName:serverURL customHeaderFields:nil];
    MKNetworkOperation *op = [engine operationWithPath:nil params:nil httpMethod:@"GET" ssl:YES];

    NSString *thePath = [[NSBundle mainBundle] pathForResource:@"mycert" ofType:@"p12"];
    [op setShouldContinueWithInvalidCertificate:YES];
    op.clientCertificate = thePath;
    op.clientCertificatePassword = @"MyCertPassw0rd";

    [op addCompletionHandler:^(MKNetworkOperation *operation) {  
        NSLog(@"[operation responseData]-->>%@", [operation responseString]);  
    }errorHandler:^(MKNetworkOperation *errorOp, NSError* err) {  
        NSLog(@"MKNetwork request error : %@", [err localizedDescription]);  
    }];  

    [engine enqueueOperation:op];

}
Run Code Online (Sandbox Code Playgroud)

我使用这两种方法得到了同样的错误.有任何想法吗?

已知信息

  • 我知道该应用程序正在查找.p12证书,因为当我捏造.p12证书的路径时,我收到一条错误消息,说它无法找到我通常不会看到的证书.
  • 我知道我为证书文件提供的密码是正确的,因为当我捏造密码时,我收到一个关于我通常不会看到的密码的错误.
  • 我不认为证书信任是一个问题,因为如果我从iOS钥匙串中删除我的服务器的CA,我在控制台中得到一个错误,特别声明服务器不可信.添加CA后,不再出现此错误,但我得到与上面相同的错误(-9825)

其他测试

在代码中提供基本身份验证(绕过证书身份验证)时,一切都按预期工作,我没有收到任何错误.

我也尝试了上面所有相同的步骤,但使用.pfx文件而不是我的.p12证书,相同的错误.

SSL服务适用于移动Safari.我通过iPhone配置实用程序将.p12证书导入我的钥匙串,并通过移动safari测试了Web服务.一切都按预期工作,没有错误发生(也没有信任错误),我收到预期的输出.

我还能够使用rest-client实用程序在桌面上成功测试Web服务

TL; DR

我正在尝试使用objective-c中的.p12证书对SSL Web服务进行身份验证.我知道服务器和Web服务都使用auth的cert,但是当我尝试在objective-c中建立连接时,我一直收到错误,所以我的代码中必定存在错误.请帮忙!

njt*_*man 4

我终于能够解决这个问题了。我在这里找到了答案 http://oso.com.pl/?p=207&lang=en

看起来证书被发送了两次,导致服务器出现错误。

如果上面的链接失效了,我已经在 SO 上找到了相同的解决方案(但是针对不同的问题和错误)。

使用客户端证书身份验证时,为什么我不断收到 NSURLErrorDomain Code=-1206?

最终,改变这一点:

    NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:(__bridge NSArray*)certsArray persistence:NSURLCredentialPersistenceNone];
Run Code Online (Sandbox Code Playgroud)

对此:

    NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:nil persistence:NSURLCredentialPersistenceNone];
Run Code Online (Sandbox Code Playgroud)

解决了我的问题。如果有人能够对这个问题提供更多见解,那就太好了。