I have been stuck since two days translating a piece of code from Objective-C to Swift:
CFArrayRef keyref = NULL;
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyref, 0);
SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
Yes, that's all! I simply can't satisfy the compiler with those pointers. Please help :-)
This is the whole objective c code to translate:
// 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);
And this is what i got so far:
// 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)
The Problem starts at:
var identityDict: CFDictionary = CFArrayGetValueAtIndex(keyref, 0)
Error: "unmanaged CFArray not convertible to CFArray".
This is my best try so far.
Presumably the first line is a placeholder for an actual array? If you're actually working with a NULL
array pointer, the rest of your code does nothing.
Assuming you're starting from a real CFArrayRef
, you can take advantage of bridging: CoreFoundation types are automatically treated like Swift objects, so you don't need to work with CFArrayRef
and CFDictionaryRef
pointers. The same goes for any other C API that uses the CF type system, so it should also apply to SecIdentity
.
There seems to be some weirdness with automatic bridging of CF collections — you can implicitly bridge a CFArray
to an NSArray
and an NSArray
to a Swift Array<T>
, you can't just subscript a CFArray
.
So your conversion looks something like this (wrapped in a function that handles your assumed array):
func getIdentity(keychainArray: NSArray) -> SecIdentity? {
let dict = keychainArray[0] as Dictionary<String,AnyObject>
let key = kSecImportItemIdentity.takeRetainedValue()
return dict[key] as SecIdentity?
}
If you have a CFArray
you can pass it to this function and it'll automatically bridge/cast to NSArray
, which then automatically casts to a Swift array for subscripting. Treat item 0 as a Swift dictionary, and you can subscript the dictionary to get the identity out. For the key, you'll need to pull it out of an Unmanaged<CFString>
because the Security framework isn't set up for implicit bridging of that constant's declaration.
I've left this function returning an optional, since I don't know wether the array+dictionary you're passing in actually contains an identity. If you're sure it does, you could take out the two question marks.
(This compiles in playground, but I don't have an array containing a dictionary containing an identity handy for testing with, so caveat emptor.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With