iOS - 以编程方式安装 SSL 证书

24

我正在编写一个PhoneGap插件,用于在应用密钥链中安装CA根证书和用户证书。

以下是用于安装证书的代码:

NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:certpath];
CFDataRef inPKCS12Data = (CFDataRef)PKCS12Data;
CFStringRef password = (CFStringRef)certPassword;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
if (securityError == 0) {
    NSLog(@" *** Certificate install Success ***");
} else {
    NSLog(@" *** Certificate install Failure ***");
}

上面的代码运行良好(securityError等于0)。然而,我正在获得这些错误:

unknown apsd[59] <Warning>: <APSCourier: 0xee1ba80>: Stream error occurred for <APSTCPStream: 0x126940>: TLS Error Code=-9844 "peer dropped connection before responding"
unknown securityd[638] <Error>: CFReadStream domain: 12 error: 8

这表明设备不接受安装的证书,因此我想知道该证书是否与设备上安装的CA根证书进行验证。

我是否需要为应用程序安装CA根证书?

有什么想法吗?

备注:我是Objective-C和XCode环境的新手。

编辑:

下面的代码用于将CA根证书存储在钥匙串中:

NSString *rootCertPath = [[NSBundle mainBundle] pathForResource:@"rootca" ofType:@"cer"];
NSData *rootCertData = [NSData dataWithContentsOfFile:rootCertPath];

OSStatus err = noErr;
SecCertificateRef rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) rootCertData);

CFTypeRef result;

NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassCertificate, kSecClass,
rootCert, kSecValueRef,
nil];

err = SecItemAdd((CFDictionaryRef)dict, &result);

if( err == noErr) {
    NSLog(@"Install root certificate success");
} else if( err == errSecDuplicateItem ) {
    NSLog(@"duplicate root certificate entry");
} else {
    NSLog(@"install root certificate failure");
}

编辑:

看起来证书没有被发到服务器。我认为每次进行https请求时都必须手动发送证书... 我正在寻找一种在phonegap中捕获每个https调用的方法。


看一下这里,这是一个关于同一主题的较旧的SO答案。 - Frank
2
一般来说,如果一个应用程序可以在没有明确用户干预的情况下修改设备全局使用的受信任CA证书列表,则需要提出安全问题。 - Bruno
1
Bruno:它将把证书添加到您的应用程序的钥匙串沙盒中,即没有其他应用程序会信任您的证书。 - Frank
尝试从 CA 到服务器安装完整的证书链,这只是我的猜测,但根据我拥有的信息,这是最好的方法。 - Frank
1
http://blog.asolutions.com/2011/02/using-tls-with-self-signed-certificates-or-custom-root-certificates-in-ios/ - iPatel
显示剩余3条评论
3个回答

6

仅将证书导入到钥匙串是不够的,新的根证书还必须被用户或系统信任。

假设您仍然在变量certificate中拥有SecCertificateRef,请使用以下代码提高信任级别:

NSDictionary *newTrustSettings = @{(id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultTrustRoot]};
status = SecTrustSettingsSetTrustSettings(certificate, kSecTrustSettingsDomainUser, (__bridge CFTypeRef)(newTrustSettings));
if (status != errSecSuccess) {
    NSLog(@"Could not change the trust setting for a certificate. Error: %d", status);
    exit(0);
}

更改信任级别将在弹出窗口中询问用户是否接受更改。

1
未声明的标识符kSecTrustSettingsResult,我已经导入了Security类?我做错了什么? - user3892683
2
这似乎依赖于由苹果提供的开源安全库(http://opensource.apple.com//source/libsecurity_keychain/libsecurity_keychain-55050.2/),特别是在[SecTrustSettings.h]中给出的定义(http://opensource.apple.com//source/libsecurity_keychain/libsecurity_keychain-55050.2/lib/SecTrustSettings.h)。以上答案中的内容远不止表面看起来的那么简单! - Kevin Owens
4
有没有希望在不导入这个庞大(而且旧的)主要由C++编写的库的情况下,实现相同的信任设置?至少可以提供指向Security.framework函数的指针(可能是封装在其下面的)。 - Nicolas Miari
1
你怎么将libsecurity_keychain库添加到你的项目中?我无法构建它。有人能让它正常工作吗? - Austin

1
Swift 4.0
let rootCertPath = Bundle.main.path(forResource: "XXXXX", ofType: "der")
    let rootCertData = NSData(contentsOfFile: rootCertPath!)
    var err: OSStatus = noErr
    let rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, rootCertData!)

    //var result: CFTypeRef1
    let dict = NSDictionary.init(objects: [kSecClassCertificate, rootCert!], forKeys: [kSecClass as! NSCopying, kSecValueRef as! NSCopying])

    err = SecItemAdd(dict, nil)

    if(err == noErr) {
        NSLog("Install root certificate success");
    } else if( err == errSecDuplicateItem ) {
        NSLog("duplicate root certificate entry");
    } else {
        NSLog("install root certificate failure");
    }

0
我正在寻找的是,在系统钥匙串证书中设置“始终信任”。
// Trust always, as root certificated.
    NSDictionary *newTrustSettings = @{(id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultTrustRoot]};
    status = SecTrustSettingsSetTrustSettings(myCertificate, kSecTrustSettingsDomainAdmin, (__bridge CFTypeRef)(newTrustSettings));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接