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

标签 ios objective-c ssl cordova mknetworkkit

更新

2/6/14 编辑:

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



我正在使用自定义插件创建一个非常简单的 iOS7 Cordova 3.2 应用程序,通过仅提供 .p12 证书进行身份验证(不需要基本身份验证或其他用户凭据)来从 SSL Web 服务获取数据。我在物理 iPad(无模拟器)上执行所有测试。 Web 服务位于使用自签名证书的开发 SAP NetWeaver 门户框上。现在,我将服务器的 CA 导入 iOS 钥匙串(keychain)以避免证书信任错误。出于测试目的,我的 .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>}

根据Apple's documentation site ,-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;
}

在我的第二种方法 ,我尝试使用 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];

}

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

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

  • 其他测试

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

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

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

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

    TL;DR

    我正在尝试仅使用 Objective-c 中的 .p12 证书对 SSL Web 服务进行身份验证。我知道服务器和 Web 服务都可以使用证书进行身份验证,但是当我尝试在 Objective-C 中建立连接时,我不断收到错误消息,所以我的代码中一定有问题。请帮忙!

    最佳答案

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

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

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

    When using Client Certificate Authentication, why do I keep getting NSURLErrorDomain Code=-1206?

    最终,改变这一点:

        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:(__bridge NSArray*)certsArray persistence:NSURLCredentialPersistenceNone];
    

    对此:
        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:nil persistence:NSURLCredentialPersistenceNone];
    

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

    关于iOS 应用 SSL .p12 身份验证 - 证书错误错误 (-9825),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21512761/

    相关文章:

    ios - AFNetworking 检查可用网络

    ios - UiTableview 每第 n 个结果插入行

    ios - 是否可以通过编程方式创建 unwind segue?

    objective-c - NSMutableArray : getObject method returning null

    iphone - 如何将按钮的 void 语句与按钮的所有操作联系起来。

    jquery - Phonegap - 在导出为已签名的 apk 后访问 HTTPS 请求时,ajax 不再有效

    sql-server-2008 - jtds 驱动程序无法在 ssl=require 模式下连接到 SQL Server 2008 R2

    ios - 使用动画减少 View 的宽度

    iphone - 你如何确定 Objective-C 中的最大 long 值?

    python - Python 的 SSL 错误,但服务器测试给出所有 A+ 评级