前言:
前段時(shí)間做RSA加密的時(shí)候,在調(diào)試的過(guò)程中,經(jīng)常失敗,后臺(tái)接收到的加密參數(shù)為空,但是這種不是經(jīng)常發(fā)生,而是隨機(jī)的。經(jīng)過(guò)測(cè)試,發(fā)現(xiàn),這種情況在單獨(dú)使用真機(jī)、模擬器的時(shí)候都不發(fā)生,只有在真機(jī)連接xcode進(jìn)行調(diào)試的時(shí)候才會(huì)發(fā)生。
追蹤:
在筆者打了N多斷點(diǎn)之后,終于找到了問(wèn)題發(fā)生的根源。在RSA加密的類中,有這么一段代碼:
CFTypeRef persistKey = nil;
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);
if (persistKey != nil){
CFRelease(persistKey);
}
if ((status != noErr) && (status != errSecDuplicateItem)) {
return nil;
}
[publicKey removeObjectForKey:(__bridge id)kSecValueData];
[publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
[publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
// Now fetch the SecKeyRef version of the key
SecKeyRef keyRef = nil;
status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef);
if(status != noErr){
return nil;
}
return keyRef;
在模擬器上測(cè)試的時(shí)候:

真機(jī)連接X(jué)code的時(shí)候:

問(wèn)題就出在:
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);
在stack overflow發(fā)現(xiàn)有的人也遇到了類似的問(wèn)題,現(xiàn)總問(wèn)題結(jié)如下:
*** 1.當(dāng)我在連接著Xcode的真機(jī)設(shè)備上運(yùn)行app的時(shí)候,我試圖獲取鑰匙串,卻由于-34018的錯(cuò)誤導(dǎo)致運(yùn)行失敗。缺乏鑰匙串錯(cuò)誤代碼開(kāi)發(fā)文檔,而且也很難重現(xiàn)此錯(cuò)誤(發(fā)生的概率大概30%,我也不知道為什么發(fā)生)。由于缺乏相關(guān)的文檔,所以調(diào)試這種錯(cuò)誤就變得非常困難。為什么會(huì)出現(xiàn)此錯(cuò)誤,如何解決?xcode5、ios7***
解決方案
方案一:
你不得不通過(guò)在你的test target中添加下面的代碼作為運(yùn)行腳本來(lái)簽名你的 .xcttest文件夾
codesign --verify --force --sign "$CODE_SIGN_IDENTITY" "$CODESIGNING_FOLDER_PATH"
方案二:
在查看源碼 之后,我注意到鑰匙串通過(guò)一種安全進(jìn)程(區(qū)別于app的進(jìn)程)獲取。
app和安全進(jìn)程通過(guò)一項(xiàng)稱之為XPC的技術(shù)進(jìn)行對(duì)話。安全進(jìn)程經(jīng)由XPC的啟動(dòng)命令啟動(dòng)。你可以發(fā)現(xiàn)真機(jī)(模擬器也是如此)app的安全進(jìn)程正在運(yùn)行,它的父進(jìn)程也已經(jīng)啟動(dòng)。
我猜測(cè),有可能某些未知的原因?qū)е?code>安全進(jìn)程啟動(dòng)失敗或者啟動(dòng)太慢,使得你使用它的時(shí)候還沒(méi)有準(zhǔn)備好。
或許,你可以考慮如何重啟安全進(jìn)程。
方案三,來(lái)自蘋果官方回復(fù):
1.我們終于能夠在ios8.3里重現(xiàn)-34018錯(cuò)誤,這是我們查找問(wèn)題根源和修復(fù)問(wèn)題的第一步。
可是,我們并不能給出確切的解決時(shí)間,但是由于已經(jīng)影響了很多的開(kāi)發(fā)者,我們也非常希望盡快解決。
作為變通方案,我建議在在didFinishLaunchingWithOptions 和applicationDidBecomeActive: 兩個(gè)方法之前添加一個(gè)小的延時(shí)給獲取字符串爭(zhēng)取一點(diǎn)時(shí)間。然而,好像并沒(méi)有明顯的作用,這就意味著除了重啟app確實(shí)沒(méi)有更好的解決方法。
這個(gè)問(wèn)題似乎和內(nèi)存壓力也有一定的關(guān)系,在解決這個(gè)問(wèn)題的時(shí)候,要積極處理內(nèi)存警告的問(wèn)題。
更新:
這個(gè)復(fù)雜的問(wèn)題有幾種可能的原因
- 一些情況下是由于不正確的app
簽名造成的,你可以很清楚的辨別這種原因,因?yàn)樗?00%可以重現(xiàn)。 - 有些情況下這種問(wèn)題的造成是由于iOS支持app開(kāi)發(fā)環(huán)境中的一個(gè)bug
造成的。由于os中的另一個(gè)bug掩蓋了它的影響,排查這個(gè)問(wèn)題非常困難,這就意味著這個(gè)問(wèn)題在內(nèi)存面臨巨大壓力的時(shí)候會(huì)突然出現(xiàn),我們相信這個(gè)問(wèn)題在ios9.3中已經(jīng)解決。 - 我們猜測(cè)可能有其他的原因造成了這個(gè)問(wèn)題
所以,如果你在運(yùn)行ios9.3及以后系統(tǒng)的用戶設(shè)備上(未和Xcode鏈接)遇到此問(wèn)題,請(qǐng)記錄此bug并報(bào)告給我們。)
方案四:
我的app(iOS8.4)現(xiàn)在極少遇到 -34018的錯(cuò)誤,在做過(guò)一些調(diào)查之后,我發(fā)現(xiàn)當(dāng)app頻繁的從鑰匙串請(qǐng)求數(shù)據(jù)的時(shí)候就會(huì)造成這種問(wèn)題。例如,在同一時(shí)間,在不同的加載模塊,兩次讀同一個(gè)specific key 的值。為了解決這個(gè)問(wèn)題,我在內(nèi)存中緩存了這個(gè)值。
方案五:
其他的方式對(duì)我都沒(méi)有用,我清空了我設(shè)備上所有的配置文件,以及一些通配符配置文件(這些文件似乎是重點(diǎn)),為了這么做,去window的Devices,然后右擊你的(連接)的iphone:

點(diǎn)擊"Show provisioning profiles"刪除相關(guān)的文件,尤其是team相關(guān)的配置文件包括帶*號(hào)的文件,然后重新導(dǎo)入。

方案六:
我在Xcode6.2 、iphone6、ios8.3環(huán)境下,在測(cè)試設(shè)備上也出現(xiàn)這種問(wèn)題。更清楚的說(shuō),在運(yùn)行Xcode tests的時(shí)候這種情況沒(méi)有發(fā)生,在我的真機(jī),模擬器上都是正常的。
以上的方式都對(duì)我無(wú)效。
我臨時(shí)的改變了獲取的方式從
kSecAttrAccessibleAfterFirstUnlock 到kSecAttrAccessibleAlwaysThisDeviceOnly,運(yùn)行app,完美運(yùn)行,且可以寫到鑰匙串,然后我再改回到kSecAttrAccessibleAfterFirstUnlock,問(wèn)題似乎永久性的解決了。
總結(jié):
非常遺憾,筆者這個(gè)問(wèn)題仍舊沒(méi)有解決,希望筆者摘錄的幾種解決方式給大家一點(diǎn)思路。