KMDSignatureManager.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. //
  2. // KMDSignatureManager.m
  3. // SignFlow
  4. //
  5. // Created by 丁林圭 on 2021/8/2.
  6. //
  7. #import "KMDSignatureManager.h"
  8. #import "CPDFListView+Private.h"
  9. //#import "KMDSignatureAppearanceManager.h"
  10. //#import <KMPDFKit/KMPDFSignature.h>
  11. //#import "NSString_SKExtensions.h"
  12. #import <PDF_Reader_Pro-Swift.h>
  13. @implementation KMDSignatureModel
  14. - (void)dealloc
  15. {
  16. }
  17. @end
  18. #pragma mark - KMDSignatureManager
  19. @interface KMDSignatureManager()
  20. @property (nonatomic,retain) NSMutableArray *signatures;
  21. @end
  22. @implementation KMDSignatureManager
  23. - (void)dealloc {
  24. }
  25. static CFAbsoluteTime getExpiredDate(SecCertificateRef cert) {
  26. CFErrorRef error;
  27. CFTypeRef key = kSecOIDX509V1ValidityNotAfter;
  28. CFArrayRef keySelection = CFArrayCreate(NULL, &key, 1, &kCFTypeArrayCallBacks);
  29. CFDictionaryRef values = SecCertificateCopyValues(cert, keySelection, &error);
  30. CFDictionaryRef dict = CFDictionaryGetValue(values, key);
  31. CFNumberRef notAfterRef = (CFNumberRef) CFDictionaryGetValue(dict, kSecPropertyKeyValue);
  32. CFAbsoluteTime notAfter;
  33. CFNumberGetValue(notAfterRef, kCFNumberDoubleType, &notAfter);
  34. return notAfter;
  35. }
  36. - (NSString *)subNameFromCerificateWithLongDescription:(SecCertificateRef)certificateRef {
  37. if (certificateRef == NULL)
  38. return nil;
  39. #if TARGET_OS_MAC
  40. const void *keys[] = { kSecOIDX509V1SubjectName};
  41. CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks);
  42. CFErrorRef error;
  43. CFDictionaryRef vals = SecCertificateCopyValues(certificateRef, keySelection, &error);
  44. NSString *string = nil;
  45. for(int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) {
  46. CFDictionaryRef dict_values = CFDictionaryGetValue(vals, keys[i]);
  47. CFArrayRef values = CFDictionaryGetValue(dict_values, kSecPropertyKeyValue);
  48. if (values == NULL)
  49. continue;
  50. string = [KMDSignatureManager stringFromDNwithSubjectName:values];
  51. }
  52. CFRelease(vals);
  53. #endif
  54. return string;
  55. }
  56. + (KMDSignatureManager *)defaultManager
  57. {
  58. static KMDSignatureManager *singleton = nil;
  59. static dispatch_once_t pred;
  60. dispatch_once(&pred, ^{
  61. singleton = [[KMDSignatureManager alloc] init];
  62. });
  63. return singleton;
  64. }
  65. - (id)init {
  66. self = [super init];
  67. if (self) {
  68. self.signatures = [NSMutableArray array];
  69. }
  70. return self;
  71. }
  72. - (void)loadAllKeyChainCertificates {
  73. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  74. [[NSNotificationCenter defaultCenter] postNotificationName:DSignatureDidStartLoadNotification object:nil];
  75. self.signatures = [NSMutableArray array];
  76. NSMutableDictionary *keychainQuery = [NSMutableDictionary dictionary];
  77. [keychainQuery setObject:(__bridge id)kSecClassIdentity forKey:(__bridge id)kSecClass];
  78. [keychainQuery setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
  79. [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
  80. [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnRef];
  81. OSStatus keychainErr = noErr;
  82. CFArrayRef outDictionary;
  83. keychainErr = SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&outDictionary);
  84. NSArray* array = (__bridge NSArray *)outDictionary;
  85. for (NSInteger i=0; i<array.count; i++) {
  86. SecIdentityRef identityRef = (__bridge SecIdentityRef)(array[i][(__bridge NSString *)kSecValueRef]);
  87. KMDSignatureModel * model = [[KMDSignatureModel alloc] init];
  88. model.identityRef = identityRef;
  89. model.isFormKeyChain = YES;
  90. SecCertificateRef certificateRef = nil;
  91. OSStatus securityError = SecIdentityCopyCertificate(identityRef, &certificateRef);
  92. if (errSecSuccess == securityError) {
  93. const CSSM_X509_NAME *issuer = NULL;
  94. securityError = SecCertificateGetIssuer(certificateRef, &issuer);
  95. if (errSecSuccess == securityError) {
  96. model.issusName = [KMDSignatureManager certificateSubjectCommonName:issuer];
  97. }
  98. const CSSM_X509_NAME *subject = NULL;
  99. securityError = SecCertificateGetSubject(certificateRef, &subject);
  100. if (errSecSuccess == securityError) {
  101. model.name = [KMDSignatureManager certificateSubjectCommonName:subject];
  102. }
  103. model.DN = [self subNameFromCerificateWithLongDescription:certificateRef];
  104. CFAbsoluteTime notAfter = getExpiredDate(certificateRef);
  105. CFDateRef cfDate = CFDateCreate(kCFAllocatorDefault, notAfter);
  106. NSDate * date = (__bridge NSDate *)cfDate;
  107. model.expiresDate = date;
  108. CFRelease(cfDate);
  109. }
  110. [self.signatures addObject:model];
  111. }
  112. if ([[NSFileManager defaultManager] fileExistsAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC]) {
  113. NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:Swift_oc_Tool.kDigitalSignaturePlistPath_OC];
  114. for (NSString *key in [dictionary allKeys]) {
  115. NSDictionary *dic = [dictionary objectForKey:key];
  116. NSString *filePath = dic[Swift_oc_Tool.SAVEFILEPATH_KEY_OC];
  117. NSString *password = dic[Swift_oc_Tool.PASSWORD_KEY_OC];
  118. if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
  119. continue;
  120. }
  121. SecIdentityRef identity = [KMDSignatureManager privateKeyUsingSecItemImportFromP12File:filePath password:password];
  122. KMDSignatureModel *model = [[KMDSignatureModel alloc] init];
  123. model.identityRef = identity;
  124. model.isFormKeyChain = NO;
  125. model.filePath = filePath;
  126. model.password = password;
  127. SecCertificateRef certificateRef = nil;
  128. OSStatus securityError = SecIdentityCopyCertificate(identity, &certificateRef);
  129. if (errSecSuccess == securityError) {
  130. const CSSM_X509_NAME *issuer = NULL;
  131. securityError = SecCertificateGetIssuer(certificateRef, &issuer);
  132. if (errSecSuccess == securityError) {
  133. model.issusName = [KMDSignatureManager certificateSubjectCommonName:issuer];
  134. }
  135. const CSSM_X509_NAME *subject = NULL;
  136. securityError = SecCertificateGetSubject(certificateRef, &subject);
  137. if (errSecSuccess == securityError) {
  138. model.name = [KMDSignatureManager certificateSubjectCommonName:subject];
  139. }
  140. model.DN = [self subNameFromCerificateWithLongDescription:certificateRef];
  141. CFAbsoluteTime notAfter = getExpiredDate(certificateRef);
  142. CFDateRef cfDate = CFDateCreate(kCFAllocatorDefault, notAfter);
  143. NSDate * date = (__bridge NSDate *)cfDate;
  144. model.expiresDate = date;
  145. }
  146. [self.signatures addObject:model];
  147. }
  148. }
  149. [[NSNotificationCenter defaultCenter] postNotificationName:DSignatureDidFinishLoadNotification object:nil];
  150. });
  151. }
  152. #pragma mark - Setter and Getter
  153. - (NSString *)getUniqueFilePath:(NSString *)filePath {
  154. int i = 0;
  155. BOOL isDirectory = NO;
  156. NSString* uniqueFilePath = filePath;
  157. NSFileManager* filemanager = [NSFileManager defaultManager];
  158. [filemanager fileExistsAtPath:uniqueFilePath isDirectory:&isDirectory];
  159. if (isDirectory) {
  160. while ([filemanager fileExistsAtPath:uniqueFilePath]) {
  161. i++;
  162. uniqueFilePath = [NSString stringWithFormat:@"%@(%d)",filePath,i];
  163. }
  164. } else {
  165. while ([filemanager fileExistsAtPath:uniqueFilePath]) {
  166. i++;
  167. NSString* path = [NSString stringWithFormat:@"%@(%d)",[filePath stringByDeletingPathExtension],i];
  168. uniqueFilePath = [path stringByAppendingPathExtension:[filePath pathExtension]];
  169. }
  170. }
  171. return uniqueFilePath;
  172. }
  173. + (SecIdentityRef)privateKeyUsingSecItemImportFromP12File:(NSString *)filePath password:(NSString *)password {
  174. SecIdentityRef key = NULL;
  175. NSString *p12Path = filePath.stringByExpandingTildeInPath;
  176. if (!password) {
  177. password = @"";
  178. }
  179. NSData *p12Data = [NSData dataWithContentsOfFile:p12Path];
  180. if (!p12Data) {
  181. NSLog(@"Could not read p12 data from file: \"%@\".", p12Path);
  182. return key;
  183. }
  184. OSStatus status;
  185. SecKeychainRef keychain = NULL;
  186. NSString *temporaryDirectory = NSTemporaryDirectory();
  187. NSString *keychainPath = [[temporaryDirectory stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]] stringByAppendingPathExtension:@"keychain"];
  188. status = SecKeychainCreate(keychainPath.UTF8String, (UInt32)password.length, password.UTF8String, FALSE, NULL, &keychain);
  189. if (status != errSecSuccess) {
  190. if (keychain) {
  191. SecKeychainDelete(keychain);
  192. CFRelease(keychain);
  193. }
  194. return key;
  195. }
  196. NSMutableDictionary *options = [NSMutableDictionary dictionary];
  197. if (password) {
  198. [options setObject:password forKey:(id)kSecImportExportPassphrase];
  199. }
  200. [options setObject:(__bridge id)keychain forKey:(id)kSecImportExportKeychain];
  201. CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
  202. status = SecPKCS12Import((CFDataRef)p12Data, (CFDictionaryRef)options, &items);
  203. if ((status == errSecSuccess) && (CFArrayGetCount(items) > 0)) {
  204. CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
  205. SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
  206. key = identity;
  207. }
  208. return key;
  209. }
  210. + (NSString *)certificateSubjectCommonName:(CSSM_X509_NAME *)name {
  211. NSString *result = nil;
  212. for (uint32 rdn = 0; rdn < name->numberOfRDNs; ++rdn) {
  213. CSSM_X509_RDN rdnRef = name->RelativeDistinguishedName[rdn];
  214. for (uint32 pair = 0; pair < rdnRef.numberOfPairs; ++pair) {
  215. CSSM_DATA type = rdnRef.AttributeTypeAndValue[pair].type;
  216. if (CSSMOID_CommonName.Length == type.Length &&
  217. memcmp(CSSMOID_CommonName.Data, type.Data,
  218. CSSMOID_CommonName.Length) == 0) {
  219. CSSM_DATA value = rdnRef.AttributeTypeAndValue[pair].value;
  220. NSData *certCnData = [NSData dataWithBytes:value.Data
  221. length:value.Length];
  222. NSString *certCnString
  223. = [[NSString alloc] initWithData:certCnData
  224. encoding:NSUTF8StringEncoding];
  225. if (!result) {
  226. result = certCnString;
  227. } else {
  228. result = [result stringByAppendingFormat:@", %@", certCnString];
  229. }
  230. }
  231. }
  232. }
  233. return result;
  234. }
  235. - (NSArray *)sortContens:(NSArray *)contens {
  236. NSMutableArray *tContens = [NSMutableArray array];
  237. CPDFSignatureConfigItem *nameItem = nil;
  238. CPDFSignatureConfigItem *dnItem = nil;
  239. CPDFSignatureConfigItem *reaItem = nil;
  240. CPDFSignatureConfigItem *locaItem = nil;
  241. CPDFSignatureConfigItem *dateItem = nil;
  242. CPDFSignatureConfigItem *verItem = nil;
  243. for (CPDFSignatureConfigItem *item in contens) {
  244. if ([item.key isEqual:Swift_oc_Tool.NAME_KEY_OC]) {
  245. nameItem = item;
  246. } else if ([item.key isEqual:Swift_oc_Tool.DN_KEY_OC]) {
  247. dnItem = item;
  248. } else if ([item.key isEqual:Swift_oc_Tool.REASON_KEY_OC]) {
  249. reaItem = item;
  250. } else if ([item.key isEqual:Swift_oc_Tool.LOCATION_KEY_OC]) {
  251. locaItem = item;
  252. } else if ([item.key isEqual:Swift_oc_Tool.DATE_KEY_OC]) {
  253. dateItem = item;
  254. } else if ([item.key isEqual:Swift_oc_Tool.VERSION_KEY_OC]) {
  255. verItem = item;
  256. }
  257. }
  258. if (nameItem) {
  259. [tContens addObject:nameItem];
  260. }
  261. if (dateItem) {
  262. [tContens addObject:dateItem];
  263. }
  264. if (reaItem) {
  265. [tContens addObject:reaItem];
  266. }
  267. if (dnItem) {
  268. [tContens addObject:dnItem];
  269. }
  270. if (verItem) {
  271. [tContens addObject:verItem];
  272. }
  273. if (locaItem) {
  274. [tContens addObject:locaItem];
  275. }
  276. return tContens;
  277. }
  278. #pragma mark - Add
  279. - (void)addSignatureCertPath:(NSString *)filePath passWord:(NSString *)password {
  280. if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
  281. return;;
  282. }
  283. SecIdentityRef identity = [KMDSignatureManager privateKeyUsingSecItemImportFromP12File:filePath password:password];
  284. [self addSecIdentityRef:identity certPath:filePath passWord:password];
  285. }
  286. - (void)addSecIdentityRef:(SecIdentityRef)identity certPath:(NSString *)filePath passWord:(NSString *)password {
  287. KMDSignatureModel * model = [[KMDSignatureModel alloc] init];
  288. model.identityRef = identity;
  289. model.isFormKeyChain = filePath?NO:YES;
  290. model.filePath = filePath;
  291. model.password = password;
  292. SecCertificateRef certificateRef = nil;
  293. OSStatus securityError = SecIdentityCopyCertificate(identity, &certificateRef);
  294. if (errSecSuccess == securityError) {
  295. const CSSM_X509_NAME *issuer = NULL;
  296. securityError = SecCertificateGetIssuer(certificateRef, &issuer);
  297. if (errSecSuccess == securityError) {
  298. model.issusName = [KMDSignatureManager certificateSubjectCommonName:issuer];
  299. }
  300. const CSSM_X509_NAME *subject = NULL;
  301. securityError = SecCertificateGetSubject(certificateRef, &subject);
  302. if (errSecSuccess == securityError) {
  303. model.name = [KMDSignatureManager certificateSubjectCommonName:subject];
  304. }
  305. CFAbsoluteTime notAfter = getExpiredDate(certificateRef);
  306. CFDateRef cfDate = CFDateCreate(kCFAllocatorDefault, notAfter);
  307. NSDate * date = (__bridge NSDate *)cfDate;
  308. model.expiresDate = date;
  309. model.DN = [self subNameFromCerificateWithLongDescription:certificateRef];
  310. }
  311. if (self.signatures.count >0) {
  312. [self.signatures insertObject:model atIndex:0];
  313. } else {
  314. [self.signatures addObject:model];
  315. }
  316. }
  317. - (BOOL)removeSignatureCertPath:(NSString *)filePath
  318. {
  319. if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
  320. return NO;
  321. }
  322. for (KMDSignatureModel * model in self.signatures) {
  323. if ([model.filePath isEqual:filePath]) {
  324. [self.signatures removeObject:model];
  325. [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
  326. break;
  327. }
  328. }
  329. if ([[NSFileManager defaultManager] fileExistsAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC]) {
  330. [[NSFileManager defaultManager] removeItemAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC error:nil];
  331. for (KMDSignatureModel * model in self.signatures) {
  332. NSString *tag = [DSignatureApperanceManager manager].tagString;
  333. NSMutableDictionary *newDictionary = [NSMutableDictionary dictionary];
  334. NSMutableDictionary * dic = [NSMutableDictionary dictionary];
  335. [dic setValue:model.filePath forKey:Swift_oc_Tool.SAVEFILEPATH_KEY_OC];
  336. if (model.password) {
  337. [dic setValue:model.password forKey:Swift_oc_Tool.PASSWORD_KEY_OC];
  338. }
  339. [newDictionary setObject:dic forKey:tag];
  340. [newDictionary writeToFile:Swift_oc_Tool.kDigitalSignaturePlistPath_OC atomically:YES];
  341. }
  342. }
  343. return YES;
  344. }
  345. - (BOOL)importKeyChainWithP12FilePath:(NSString *)filePath passWord:(NSString *)passWord
  346. {
  347. SecIdentityRef identity = nil;
  348. NSData *PKCS12Data = [NSData dataWithContentsOfFile:filePath];
  349. CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
  350. CFStringRef password = (__bridge CFStringRef)passWord;
  351. const void *keys[] = { kSecImportExportPassphrase };
  352. const void *values[] = { password };
  353. CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
  354. CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
  355. OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
  356. CFRelease(options);
  357. CFRelease(password);
  358. if (securityError == errSecSuccess) {
  359. CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
  360. identity = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
  361. } else {
  362. NSLog(@"Error opening Certificate.");
  363. }
  364. if (identity) {
  365. [self addSecIdentityRef:identity certPath:nil passWord:nil];
  366. return YES;
  367. } else {
  368. return NO;
  369. }
  370. }
  371. + (BOOL)exportKeyChainWithP12FilePath:(NSString *)filePath signatureModel:(KMDSignatureModel *)model passWord:(NSString *)passWord
  372. {
  373. BOOL isSave = NO;
  374. SecIdentityRef identityRef = model.identityRef;
  375. CFDataRef dataRef = NULL;
  376. SecItemImportExportKeyParameters params;
  377. memset(&params, 0, sizeof(params));
  378. params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
  379. params.flags = 0;
  380. params.passphrase = (__bridge CFTypeRef _Nullable)(passWord);
  381. params.alertTitle = NULL;
  382. params.alertPrompt = NULL;
  383. params.accessRef = NULL;
  384. OSStatus status = SecItemExport(identityRef, kSecFormatPKCS12, 0, &params, &dataRef);
  385. if (status == errSecSuccess) {
  386. isSave = [(__bridge NSData*)dataRef writeToFile:filePath atomically:YES];
  387. }
  388. return isSave;
  389. }
  390. - (BOOL)moveP12DigitalFileWithFilePath:(NSString *)filePath password:(NSString *)password {
  391. if (![[NSFileManager defaultManager] fileExistsAtPath:Swift_oc_Tool.kDigitalSignatureFolderPath_OC]) {
  392. if (![[NSFileManager defaultManager] createDirectoryAtPath:Swift_oc_Tool.kDigitalSignatureFolderPath_OC withIntermediateDirectories:YES attributes:nil error:nil]) {
  393. return NO;
  394. }
  395. }
  396. if (![[NSFileManager defaultManager] fileExistsAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC]) {
  397. if (![[NSFileManager defaultManager] createFileAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC contents:nil attributes:nil]) {
  398. return NO;
  399. }
  400. }
  401. NSString* toPath = [Swift_oc_Tool.kDigitalSignatureFolderPath_OC stringByAppendingPathComponent:filePath.lastPathComponent];
  402. toPath = [self getUniqueFilePath:toPath];
  403. BOOL isSuccess = [[NSFileManager defaultManager] copyItemAtPath:filePath toPath:toPath error:nil];
  404. if (isSuccess) {
  405. NSString *tag = [DSignatureApperanceManager manager].tagString;
  406. NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:Swift_oc_Tool.kDigitalSignaturePlistPath_OC];
  407. NSMutableDictionary *newDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary];
  408. NSMutableDictionary * dic = [NSMutableDictionary dictionary];
  409. [dic setValue:toPath forKey:Swift_oc_Tool.SAVEFILEPATH_KEY_OC];
  410. if (password) {
  411. [dic setValue:password forKey:Swift_oc_Tool.PASSWORD_KEY_OC];
  412. }
  413. [newDictionary setObject:dic forKey:tag];
  414. if ([newDictionary writeToFile:Swift_oc_Tool.kDigitalSignaturePlistPath_OC atomically:YES]) {
  415. [self addSignatureCertPath:toPath passWord:password];
  416. return YES;
  417. }
  418. }
  419. return NO;
  420. }
  421. + (NSString *)stringFromDNwithSubjectName:(CFArrayRef)array {
  422. if (!array || CFArrayGetCount(array) == 0) {
  423. return @"";
  424. }
  425. NSMutableString *out = [NSMutableString string];
  426. const void *keys[] = { kSecOIDCommonName, kSecOIDOrganizationName, kSecOIDOrganizationalUnitName, kSecOIDEmailAddress, kSecOIDLocalityName, kSecOIDStateProvinceName, kSecOIDCountryName };
  427. const char *labels[] = { "cn", "o", "ou", "email", "l", "s", "c" };
  428. for (int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
  429. for (CFIndex n = 0; n < CFArrayGetCount(array); n++) {
  430. CFDictionaryRef dict = CFArrayGetValueAtIndex(array, n);
  431. if (!dict || CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
  432. continue;
  433. }
  434. CFTypeRef dictkey = CFDictionaryGetValue(dict, kSecPropertyKeyLabel);
  435. if (!dictkey || !CFEqual(dictkey, keys[i])) {
  436. continue;
  437. }
  438. CFStringRef str = CFDictionaryGetValue(dict, kSecPropertyKeyValue);
  439. if (str) {
  440. NSString *keyValue = [NSString stringWithFormat:@"%s=%@", labels[i], (__bridge NSString *)str];
  441. if (out.length < 1) {
  442. [out appendString:keyValue];
  443. } else {
  444. [out appendFormat:@",%@", keyValue];
  445. }
  446. }
  447. }
  448. }
  449. return out;
  450. }
  451. @end