ios - 无法以编程方式在 iOS 上安装 .der 证书

标签 ios objective-c ssl encryption

我正在尝试以编程方式在 iOS(iPhone/iPhone 模拟器)上安装 SSL 证书。 我已经使用 this small guide 成功创建并安装了 .crt ! 但是当我尝试将获得的 server.crt 转换为 server.der 并以编程方式安装它时,我得到 CFNetwork SSLHandshake failed (-9807)。请注意,我在 iOS 上使用此证书的 .der 版本,在 OS X openssl 服务器上使用 .crt 版本。

转换:

openssl x509 -outform der -in server.crt -out server.der

在 OS X 上接收:

openssl s_server -key server.key -cert server.crt -accept 1678

在iOS上安装和发送:

    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSData *iosTrustedCertDerData =
    [NSData dataWithContentsOfFile:[bundle pathForResource:@"certificate"
                                                    ofType:@"der"]];

    OSStatus            err = noErr;
    SecCertificateRef   cert;

    cert = SecCertificateCreateWithData(NULL, (CFDataRef) iosTrustedCertDerData);
    assert(cert != NULL);

    CFTypeRef result;

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

    err = SecItemAdd((CFDictionaryRef)dict, &result);
    assert(err == noErr || err == errSecDuplicateItem);

    printf("adding finished \n");


    if ((err == noErr) ||
        (err == errSecDuplicateItem)) {

        printf("success \n");

        CFReadStreamRef readStream;
        CFWriteStreamRef writeStream;
        CFStreamCreatePairWithSocketToHost(NULL,
                                           (CFStringRef)@"localhost",
                                           1678,
                                           &readStream,
                                           &writeStream);
        CFReadStreamSetProperty(readStream,
                                kCFStreamPropertySocketSecurityLevel,
                                kCFStreamSocketSecurityLevelTLSv1);
        CFReadStreamOpen(readStream);
        CFWriteStreamOpen(writeStream);

        UInt8 buf[] = "Hello from iOS\n";
        int bytesWritten = CFWriteStreamWrite(writeStream, buf, strlen((char*)buf));
}

那么,如何在iOS上正确转换和安装.der证书呢?

更新:

 NSBundle *bundle = [NSBundle bundleForClass:[self class]];

    NSString *resourcePath = [bundle pathForResource:@"certificate" ofType:@"der"];

    NSData *certData = [NSData dataWithContentsOfFile:resourcePath];

    NSString* publickKeyRef = @"certificate_der";

    NSData* headerStrippedData = [self stripPublicKeyHeader:certData];
    [self addPeerPublicKey:publickKeyRef keyBits:headerStrippedData];

    SecKeyRef ref= [self getPublicKeyReference:publickKeyRef];

    printf("adding finished \n");


    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL,
                                       (CFStringRef)@"localhost",
                                       1678,
                                       &readStream,
                                       &writeStream);
    CFReadStreamSetProperty(readStream,
                            kCFStreamPropertySocketSecurityLevel,
                            kCFStreamSocketSecurityLevelTLSv1);
    CFReadStreamOpen(readStream);
    CFWriteStreamOpen(writeStream);

异常发生在这一行:

[self addPeerPublicKey:publickKeyRef keyBits:headerStrippedData];

异常(exception):

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** setObjectForKey: object cannot be nil (key: v_Data)'
*** First throw call stack:
(
    0   CoreFoundation                      0x00a81a14 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x00540e02 objc_exception_throw + 50
    2   CoreFoundation                      0x0096a174 -[__NSDictionaryM setObject:forKey:] + 948
    3   ssl2                                0x0006d8eb -[ViewController addPeerPublicKey:keyBits:] + 491
    4   ssl2                                0x0006e2dd -[ViewController viewDidLoad] + 397
    5   UIKit                               0x010932ae -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 44
    6   UIKit                               0x01097dce -[UIViewController loadViewIfRequired] + 1384
    7   UIKit                               0x010981ed -[UIViewController view] + 35
    8   UIKit                               0x00f45f94 -[UIWindow addRootViewControllerViewIfPossible] + 69
    9   UIKit                               0x00f466b1 -[UIWindow _setHidden:forced:] + 304
    10  UIKit                               0x00f46a67 -[UIWindow _orderFrontWithoutMakingKey] + 49
    11  UIKit                               0x00f5a118 -[UIWindow makeKeyAndVisible] + 80
    12  UIKit                               0x00ec26e7 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4190
    13  UIKit                               0x00ec9cd6 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1989
    14  UIKit                               0x00eeeee5 __84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke3218 + 68
    15  UIKit                               0x00ec6966 -[UIApplication workspaceDidEndTransaction:] + 163
    16  FrontBoardServices                  0x0373fc76 __37-[FBSWorkspace clientEndTransaction:]_block_invoke_2 + 71
    17  FrontBoardServices                  0x0373f74d __40-[FBSWorkspace _performDelegateCallOut:]_block_invoke + 54
    18  FrontBoardServices                  0x0375d173 -[FBSSerialQueue _performNext] + 184
    19  FrontBoardServices                  0x0375d5aa -[FBSSerialQueue _performNextFromRunLoopSource] + 52
    20  FrontBoardServices                  0x0375c8a6 FBSSerialQueueRunLoopSourceHandler + 33
    21  CoreFoundation                      0x0099b6ff __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    22  CoreFoundation                      0x0099138b __CFRunLoopDoSources0 + 523
    23  CoreFoundation                      0x009907a8 __CFRunLoopRun + 1032
    24  CoreFoundation                      0x009900e6 CFRunLoopRunSpecific + 470
    25  CoreFoundation                      0x0098fefb CFRunLoopRunInMode + 123
    26  UIKit                               0x00ec6206 -[UIApplication _run] + 540
    27  UIKit                               0x00ecbbfa UIApplicationMain + 160
    28  ssl2                                0x0006e91a main + 138
    29  libdyld.dylib                       0x0310ba21 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

最佳答案

在 iOS 中,它有点不同,您需要将此 .der 文件添加到您的钥匙串(keychain),然后从您的钥匙串(keychain)引用它。我正在提供下面的代码以添加钥匙串(keychain)并从钥匙串(keychain)中取回

- (void)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKeyData {

    OSStatus sanityCheck = noErr;
    CFTypeRef persistPeer = NULL;
    [self removePeerPublicKey:peerName];

    NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
    NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];
    [peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
    [peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    [peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag];
    [peerPublicKeyAttr setObject:publicKeyData forKey:(id)kSecValueData];
    [peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
    sanityCheck = SecItemAdd((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&persistPeer);

    if(sanityCheck == errSecDuplicateItem)
    {
        NSLog(@"HanselErrorCode: -1");
    }

    persistPeer = NULL;
    [peerPublicKeyAttr removeObjectForKey:(id)kSecValueData];
    sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef*)&persistPeer);

    if (persistPeer) CFRelease(persistPeer);
}

-(SecKeyRef)getPublicKeyReference:(NSString*)peerName
{
    OSStatus sanityCheck = noErr;

    SecKeyRef pubKeyRefData = NULL;
    NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
    NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];

    [peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
    [peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    [peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag];
    [peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:       (id)kSecReturnRef];
    sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef*)&pubKeyRefData);

    if(pubKeyRefData)
    {
        return pubKeyRefData;
    }
    else
    {
        return nil;
    }
}

- (NSData *)stripPublicKeyHeader:(NSData *)d_key
{
    // Skip ASN.1 public key header
    if (d_key == nil) return(nil);

    unsigned int len = (unsigned int)[d_key length];
    if (!len) return(nil);

    unsigned char *c_key = (unsigned char *)[d_key bytes];
    unsigned int  idx    = 0;

    if (c_key[idx++] != 0x30) return(nil);

    if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
    else idx++;

    // PKCS #1 rsaEncryption szOID_RSA_RSA
    static unsigned char seqiod[] =
    { 0x30,   0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
        0x01, 0x05, 0x00 };
    if (memcmp(&c_key[idx], seqiod, 15)) return(nil);

    idx += 15;

    if (c_key[idx++] != 0x03) return(nil);

    if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
    else idx++;

    if (c_key[idx++] != '\0') return(nil);

    // Now make a new NSData from this buffer
    return([NSData dataWithBytes:&c_key[idx] length:len - idx]);

}


- (void)removePeerPublicKey:(NSString *)peerName
{
    OSStatus sanityCheck = noErr;

    NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
    NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];

    [peerPublicKeyAttr setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [peerPublicKeyAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [peerPublicKeyAttr setObject:peerTag forKey:(__bridge id)kSecAttrApplicationTag];

    sanityCheck = SecItemDelete((__bridge CFDictionaryRef) peerPublicKeyAttr);
}

要使用此 key ,您必须执行以下操作

NSString *resourcePath = [bundle pathForResource:@"publickey" ofType:@"der"];
    NSData *certData = [NSData dataWithContentsOfFile:resourcePath];

    NSData* headerStrippedData = [self stripPublicKeyHeader:certData];
    [self addPeerPublicKey:publickKeyRef keyBits:headerStrippedData];

    SecKeyRef ref= [self getPublicKeyReference:publickKeyRef];

最后使用 ref 实现所有目的。

NSString* publickKeyRef = @"key_name_against_which_you_want_to_save_your_der";

此代码向您展示了添加、取回和使用它的方法。几个月前我从互联网上获取了这段代码——如果你找到了,请添加源代码链接。在 Xcode8 中,您必须添加钥匙串(keychain)的名称(publicKeyRef),您将在功能部分中使用此 der 文件。

关于ios - 无法以编程方式在 iOS 上安装 .der 证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39776417/

相关文章:

ios - 在没有崩溃日志文件的情况下符号化崩溃

ios - 使用未弃用代码的部署目标时,弃用代码是否会产生负面影响?

objective-c - 如何将多种语言加载到 IVONA SDK - 文本到语音

ios - IBAction 未注册按钮单击

python - 使用 ssl 访问 kafka 代理时出错

ios - 在 Swift 中模拟单例进行单元测试时遇到问题

ios - 没有已知的类- iOS

iphone - 可达性代码阻塞主线程

http - 开类 : getting rid of HTTP"S"for good

android - 改造:Android 应用程序在 API21+ 设备上启动时无法通过 SSL 握手