[英]Swift keychain and provisioning profiles
We've created an app in swift that uses keychain. 我们在swift中创建了一个使用钥匙串的应用程序。 The app works fine when run on a device or in the simulator but can't access the keychain when provisioned via Testflight unless provisioned to a new device that's never had the app previously installed via Xcode 6.1.
该应用程序在设备或模拟器上运行时工作正常,但在通过Testflight配置时无法访问钥匙串,除非配置到以前从未安装过通过Xcode 6.1安装的应用程序的新设备。
Following is an excerpt of the keychain code: 以下是钥匙串代码的摘录:
import UIKit
import Security
let serviceIdentifier = "com.ourdomain"
let kSecClassValue = kSecClass as NSString
let kSecAttrAccountValue = kSecAttrAccount as NSString
let kSecValueDataValue = kSecValueData as NSString
let kSecClassGenericPasswordValue = kSecClassGenericPassword as NSString
let kSecAttrServiceValue = kSecAttrService as NSString
let kSecMatchLimitValue = kSecMatchLimit as NSString
let kSecReturnDataValue = kSecReturnData as NSString
let kSecMatchLimitOneValue = kSecMatchLimitOne as NSString
class KeychainManager {
class func setString(value: NSString, forKey: String) {
self.save(serviceIdentifier, key: forKey, data: value)
}
class func stringForKey(key: String) -> NSString? {
var token = self.load(serviceIdentifier, key: key)
return token
}
class func removeItemForKey(key: String) {
self.save(serviceIdentifier, key: key, data: "")
}
class func save(service: NSString, key: String, data: NSString) {
var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// Instantiate a new default keychain query
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, key, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])
// Delete any existing items
SecItemDelete(keychainQuery as CFDictionaryRef)
if data == "" { return }
// Add the new keychain item
var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
}
class func load(service: NSString, key: String) -> NSString? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, key, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
var dataTypeRef :Unmanaged<AnyObject>?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
let opaque = dataTypeRef?.toOpaque()
var contentsOfKeychain: NSString?
if let op = opaque? {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
// Convert the data retrieved from the keychain into a string
contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
} else {
return nil
}
return contentsOfKeychain
}
}
After the app was already installed on the device via Xcode 6.1 I noticed that the " serviceIdentifier " - " com.ourdomain " was incorrect and didn't match the app's bundle identifier as required with provisioning. 在应用程序已经通过Xcode 6.1安装在设备上之后,我注意到“ serviceIdentifier ” - “ com.ourdomain ”不正确,并且与配置所需的应用程序包标识符不匹配。
I then changed the " serviceIdentifier " value to match the bundle identifier - " com.ourdomain.appname " however the app just won't work on the device when provisioned via Testflight. 然后我更改了“ serviceIdentifier ”值以匹配包标识符 - “ com.ourdomain.appname ”,但是当通过Testflight进行配置时,应用程序将无法在设备上运行。 I'm positive this is because the device already has the keychain for the bundle id installed with the incorrect identifier but I can't fathom how to get around this to either remove the keychain when the app is removed or get the provisioning profile to use the existing keychain (with the incorrect identifier)
我很肯定这是因为设备已经安装了带有错误标识符的软件包ID的钥匙串,但是我无法理解如何解决这个问题,以便在移除应用程序时移除钥匙串或者使用配置文件来使用现有的钥匙串(标识符不正确)
Any help would be greatly appreciated. 任何帮助将不胜感激。 Thanks in advance
提前致谢
Use withUnsafeMutablePointer
function and UnsafeMutablePointer
struct to retrieving the data instead, like below: 使用
withUnsafeMutablePointer
函数和UnsafeMutablePointer
结构来检索数据,如下所示:
var result: AnyObject?
var status = withUnsafeMutablePointer(&result) { SecItemCopyMatching(keychainQuery, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
if let data = result as NSData? {
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) {
// ...
}
}
}
it works fine with release (Fastest optimization) build. 它在发布(最快优化)版本时运行良好。
Ok so everything in the provisioning profile and keychain code was fine. 好吧,供应配置文件和钥匙串代码中的所有内容都很好。 The problem was a setting in the Swift compiler!
问题是Swift编译器中的一个设置! Changing the Optimization Level for "Release" from "Fastest" to "None" seemed to resolve the issue.
将“释放”的优化级别从“最快”更改为“无”似乎解决了这个问题。
The change of the swift optimizer is working, but not a good way to solve the problem. swift优化器的更改正在起作用,但不是解决问题的好方法。 As stated in the comments, this seems to be a bug in the swift compiler with the optimization.
正如评论中所述,这似乎是swift编译器中的优化错误。
This is not iOS only, the exact same thing happens on OSX (maybe add a tag to the question). 这不仅仅是iOS,在OSX上完全相同(可能在问题中添加标签)。 The OSStatus is 0 (success) but the referencing pointer is nil as soon as the optimization is done.
OSStatus为0(成功),但一旦优化完成,引用指针就为零。
The best workaround is to implement to keychain communication in objective-c or using a wrapper like EMKeychain (OSX only, sorry don't know an iOS Wrapper at the moment) 最好的解决方法是在objective-c中实现keychain通信或使用像EMKeychain这样的包装器(仅限OSX,抱歉暂时不知道iOS Wrapper)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.