将Objective-C中的4行代码翻译成Swift(指针)

6

我已经卡了两天,试图将一段Objective-C代码翻译成Swift:

CFArrayRef keyref = NULL;
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyref, 0);
SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict,      kSecImportItemIdentity);

没错,就是这些!我只是无法用那些指针满足编译器。 请帮忙 :)

这是需要翻译的整个 Objective-C 代码:

// Read .p12 file
NSString *path = [[NSBundle mainBundle] pathForResource:@"SSLKeyStoreClient" ofType:@"p12"];
NSData *pkcs12data = [[NSData alloc] initWithContentsOfFile:path];

// Import .p12 data
CFArrayRef keyref = NULL;
OSStatus sanityChesk = SecPKCS12Import((__bridge CFDataRef)pkcs12data,
                                       (__bridge CFDictionaryRef)[NSDictionary
                                                                  dictionaryWithObject:@"wed-zzz"
                                                                  forKey:(__bridge id)kSecImportExportPassphrase],
                                       &keyref);
if (sanityChesk != noErr) {
    NSLog(@"Error while importing pkcs12 [%d]", (int)sanityChesk);
} else
    NSLog(@"Success opening p12 certificate.");

// Identity
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyref, 0);
SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict,
                                                                  kSecImportItemIdentity);

// Cert
SecCertificateRef cert = NULL;
OSStatus status = SecIdentityCopyCertificate(identityRef, &cert);
if (status)
    NSLog(@"SecIdentityCopyCertificate failed.");

// the certificates array, containing the identity then the root certificate
NSArray *myCerts = [[NSArray alloc] initWithObjects:(__bridge id)identityRef, (__bridge id)cert, nil];

NSMutableDictionary *SSLOptions;
[SSLOptions setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsExpiredRoots];

NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
                          [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
                          [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
                          kCFNull,kCFStreamSSLPeerName,
                          myCerts,kCFStreamSSLCertificates,
                          nil];


CFReadStreamSetProperty((CFReadStreamRef)self.inputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty((CFWriteStreamRef)self.outputStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

这是我目前的成果:
// Read .p12 file
var path = NSBundle.mainBundle().pathForResource("SSLKeyStoreClient", ofType: "p12")
var pkcs12data: NSData = NSData.dataWithContentsOfFile(path, options: nil, error: nil)

// Import .p12 data
var keyref: Unmanaged<CFArray>?


var optionDict: NSMutableDictionary = NSMutableDictionary()
optionDict.setValue("wed-zzz", forKey: kSecImportExportPassphrase!.takeRetainedValue())

var sanityChesk = SecPKCS12Import(pkcs12data,optionDict,&keyref)
if sanityChesk != 0{ //noErr
   println("Error while importing pkcs12 \(sanityChesk)")
} else {
   println("Success opening p12 certificate.")
}


// Identity
var key = keyref!
var identityDict: CFDictionary = CFArrayGetValueAtIndex(keyref, 0)
var identityRef:COpaquePointer = CFDictionaryGetValue(identityDict,nil)


// Cert
var cert: Unmanaged<SecCertificate>?
var status: OSStatus  = SecIdentityCopyCertificate(identityRef, &cert)
if status == 0{
    println("SecIdentityCopyCertificate failed.")
}

// the certificates array, containing the identity then the root certificate
var sslOptions = Dictionary<NSObject, NSObject>()

sslOptions[kCFStreamSSLAllowsExpiredRoots] = NSNumber.numberWithBool(true)

var settings = Dictionary<NSObject, NSObject>()
settings[kCFStreamSSLAllowsExpiredCertificates] = NSNumber.numberWithBool(true)
settings[kCFStreamSSLAllowsAnyRoot] = NSNumber.numberWithBool(true)
settings[kCFStreamSSLValidatesCertificateChain] = NSNumber.numberWithBool(false)
settings[kCFStreamSSLPeerName] = kCFNull
//settings[kCFStreamSSLCertificates] = myCerts



    CFReadStreamSetProperty(self.inputStream, kCFStreamPropertySSLSettings, settings)
    CFReadStreamSetProperty(self.inputStream, kCFStreamPropertySSLSettings, settings)

问题始于:
var identityDict: CFDictionary = CFArrayGetValueAtIndex(keyref, 0)

错误: "未管理的CFArray无法转换为CFArray"。

这是我目前最好的尝试。


1
没有 [ ] 意味着它不是 Objective-C。 ;) 请发布您尝试过的代码(或者至少是您在这两天中尝试过的一些东西)。 - Matthias Bauch
2
keyref 的真实值是什么?显然它并不是 NULL。 - Abhi Beckert
这是C代码,不是ObjC代码。 - Bryan Chen
@user3211074 这个有什么新进展吗?你有可以分享的可运行代码示例吗? - Adrian
我有一个相关的问题。我正在使用类似的代码片段来进行我的SSL连接证书固定。不幸的是,.p12文件需要我的私钥才能在“keyref”数组中至少有一个元素。如果在.p12导入时没有私钥,则可以正常工作,但“keyref”中没有任何对象。由于密钥文件是应用程序的一部分并分发到商店,我不想包含私钥。我该如何处理这个问题? - geri-m
1个回答

4
假设第一行是一个实际数组的占位符?如果你实际上使用了一个空的数组指针,那么你的代码就什么也没做。
假设你从一个真正的CFArrayRef开始,你可以利用桥接:CoreFoundation类型自动被视为Swift对象,所以你不需要使用CFArrayRef和CFDictionaryRef指针。对于使用CF类型系统的任何其他C API,情况也是如此,因此它也适用于SecIdentity。
自动桥接CF集合似乎有些奇怪-你可以将CFArray隐式桥接到NSArray,将NSArray桥接到Swift Array,但你不能只是下标访问CFArray。
因此,您的转换看起来像这样(包装在处理您假定的数组的函数中):
func getIdentity(keychainArray: NSArray) -> SecIdentity? {
    let dict = keychainArray[0] as Dictionary<String,AnyObject>
    let key = kSecImportItemIdentity.takeRetainedValue()
    return dict[key] as SecIdentity?
}

如果您有一个CFArray,您可以将其传递给此函数,它将自动桥接/转换为NSArray,然后自动转换为Swift数组以进行下标操作。将第0项视为Swift字典,然后可以对字典进行下标操作以获取身份信息。对于密钥,您需要从Unmanaged<CFString>中提取它,因为安全框架没有设置该常量声明的隐式桥接。
我让这个函数返回一个可选项,因为我不知道您传入的数组+字典是否实际包含身份信息。如果您确定它确实包含,则可以去掉两个问号。
(这在playground中编译,但是我没有包含身份信息的字典数组方便测试,所以请注意。)

谢谢,我使用了您的强制类型转换提示。但现在我又遇到另一个问题(昨天已经遇到过)。编译器错误:i386 架构下的未定义符号: “OBJC_CLASS $ _SecIdentity”,引用自: _TFC9messenger10Connection11getIdentityfS0_FCSo7NSArrayGSqCSo11SecIdentity in Connection.o ld:找不到符号(s)与架构i386有关 clang:错误:链接器命令失败,退出代码为1(使用-v以查看调用) - Vincenzo
它似乎在 Playground 中可以编译,但在一个空项目中不行。你有什么想法吗? - Vincenzo
是的,我也链接了安全框架。可惜没有成功,又浪费了一天时间在这个烦人的小问题上。 - Vincenzo
可能存在一个问题,即是否/如何为隐式桥接设置了Security.framework您是否已经提交了一个错误报告? - rickster
1
我已经修改了你的代码,现在可以编译了,但是它以运行时错误结束。也许你能帮我解决这个问题: let dict = keychainArray[0] as Dictionary<NSString,SecIdentity> let key:NSString = kSecImportItemIdentity.takeRetainedValue() return dict[key]! 这会导致:"fatal error: value failed to bridge from Objective-C type to a Swift type"。看起来 SecIdentity 是一个 Swift 类型?!但是 Objective-C 类型在哪里呢? - Vincenzo
显示剩余5条评论

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