// // KMDSignatureManager.m // SignFlow // // Created by 丁林圭 on 2021/8/2. // #import "KMDSignatureManager.h" #import "CPDFListView+Private.h" //#import "KMDSignatureAppearanceManager.h" //#import //#import "NSString_SKExtensions.h" #import @implementation KMDSignatureModel - (void)dealloc { } @end #pragma mark - KMDSignatureManager @interface KMDSignatureManager() @property (nonatomic,retain) NSMutableArray *signatures; @end @implementation KMDSignatureManager - (void)dealloc { } static CFAbsoluteTime getExpiredDate(SecCertificateRef cert) { CFErrorRef error; CFTypeRef key = kSecOIDX509V1ValidityNotAfter; CFArrayRef keySelection = CFArrayCreate(NULL, &key, 1, &kCFTypeArrayCallBacks); CFDictionaryRef values = SecCertificateCopyValues(cert, keySelection, &error); CFDictionaryRef dict = CFDictionaryGetValue(values, key); CFNumberRef notAfterRef = (CFNumberRef) CFDictionaryGetValue(dict, kSecPropertyKeyValue); CFAbsoluteTime notAfter; CFNumberGetValue(notAfterRef, kCFNumberDoubleType, ¬After); return notAfter; } - (NSString *)subNameFromCerificateWithLongDescription:(SecCertificateRef)certificateRef { if (certificateRef == NULL) return nil; #if TARGET_OS_MAC const void *keys[] = { kSecOIDX509V1SubjectName}; CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks); CFErrorRef error; CFDictionaryRef vals = SecCertificateCopyValues(certificateRef, keySelection, &error); NSString *string = nil; for(int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) { CFDictionaryRef dict_values = CFDictionaryGetValue(vals, keys[i]); CFArrayRef values = CFDictionaryGetValue(dict_values, kSecPropertyKeyValue); if (values == NULL) continue; string = [KMDSignatureManager stringFromDNwithSubjectName:values]; } CFRelease(vals); #endif return string; } + (KMDSignatureManager *)defaultManager { static KMDSignatureManager *singleton = nil; static dispatch_once_t pred; dispatch_once(&pred, ^{ singleton = [[KMDSignatureManager alloc] init]; }); return singleton; } - (id)init { self = [super init]; if (self) { self.signatures = [NSMutableArray array]; } return self; } - (void)loadAllKeyChainCertificates { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:DSignatureDidStartLoadNotification object:nil]; self.signatures = [NSMutableArray array]; NSMutableDictionary *keychainQuery = [NSMutableDictionary dictionary]; [keychainQuery setObject:(__bridge id)kSecClassIdentity forKey:(__bridge id)kSecClass]; [keychainQuery setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit]; [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes]; [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnRef]; OSStatus keychainErr = noErr; CFArrayRef outDictionary; keychainErr = SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&outDictionary); NSArray* array = (__bridge NSArray *)outDictionary; for (NSInteger i=0; i 0)) { CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0); SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity); key = identity; } return key; } + (NSString *)certificateSubjectCommonName:(CSSM_X509_NAME *)name { NSString *result = nil; for (uint32 rdn = 0; rdn < name->numberOfRDNs; ++rdn) { CSSM_X509_RDN rdnRef = name->RelativeDistinguishedName[rdn]; for (uint32 pair = 0; pair < rdnRef.numberOfPairs; ++pair) { CSSM_DATA type = rdnRef.AttributeTypeAndValue[pair].type; if (CSSMOID_CommonName.Length == type.Length && memcmp(CSSMOID_CommonName.Data, type.Data, CSSMOID_CommonName.Length) == 0) { CSSM_DATA value = rdnRef.AttributeTypeAndValue[pair].value; NSData *certCnData = [NSData dataWithBytes:value.Data length:value.Length]; NSString *certCnString = [[NSString alloc] initWithData:certCnData encoding:NSUTF8StringEncoding]; if (!result) { result = certCnString; } else { result = [result stringByAppendingFormat:@", %@", certCnString]; } } } } return result; } - (NSArray *)sortContens:(NSArray *)contens { NSMutableArray *tContens = [NSMutableArray array]; CPDFSignatureConfigItem *nameItem = nil; CPDFSignatureConfigItem *dnItem = nil; CPDFSignatureConfigItem *reaItem = nil; CPDFSignatureConfigItem *locaItem = nil; CPDFSignatureConfigItem *dateItem = nil; CPDFSignatureConfigItem *verItem = nil; for (CPDFSignatureConfigItem *item in contens) { if ([item.key isEqual:Swift_oc_Tool.NAME_KEY_OC]) { nameItem = item; } else if ([item.key isEqual:Swift_oc_Tool.DN_KEY_OC]) { dnItem = item; } else if ([item.key isEqual:Swift_oc_Tool.REASON_KEY_OC]) { reaItem = item; } else if ([item.key isEqual:Swift_oc_Tool.LOCATION_KEY_OC]) { locaItem = item; } else if ([item.key isEqual:Swift_oc_Tool.DATE_KEY_OC]) { dateItem = item; } else if ([item.key isEqual:Swift_oc_Tool.VERSION_KEY_OC]) { verItem = item; } } if (nameItem) { [tContens addObject:nameItem]; } if (dateItem) { [tContens addObject:dateItem]; } if (reaItem) { [tContens addObject:reaItem]; } if (dnItem) { [tContens addObject:dnItem]; } if (verItem) { [tContens addObject:verItem]; } if (locaItem) { [tContens addObject:locaItem]; } return tContens; } #pragma mark - Add - (void)addSignatureCertPath:(NSString *)filePath passWord:(NSString *)password { if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { return;; } SecIdentityRef identity = [KMDSignatureManager privateKeyUsingSecItemImportFromP12File:filePath password:password]; [self addSecIdentityRef:identity certPath:filePath passWord:password]; } - (void)addSecIdentityRef:(SecIdentityRef)identity certPath:(NSString *)filePath passWord:(NSString *)password { KMDSignatureModel * model = [[KMDSignatureModel alloc] init]; model.identityRef = identity; model.isFormKeyChain = filePath?NO:YES; model.filePath = filePath; model.password = password; SecCertificateRef certificateRef = nil; OSStatus securityError = SecIdentityCopyCertificate(identity, &certificateRef); if (errSecSuccess == securityError) { const CSSM_X509_NAME *issuer = NULL; securityError = SecCertificateGetIssuer(certificateRef, &issuer); if (errSecSuccess == securityError) { model.issusName = [KMDSignatureManager certificateSubjectCommonName:issuer]; } const CSSM_X509_NAME *subject = NULL; securityError = SecCertificateGetSubject(certificateRef, &subject); if (errSecSuccess == securityError) { model.name = [KMDSignatureManager certificateSubjectCommonName:subject]; } CFAbsoluteTime notAfter = getExpiredDate(certificateRef); CFDateRef cfDate = CFDateCreate(kCFAllocatorDefault, notAfter); NSDate * date = (__bridge NSDate *)cfDate; model.expiresDate = date; model.DN = [self subNameFromCerificateWithLongDescription:certificateRef]; } if (self.signatures.count >0) { [self.signatures insertObject:model atIndex:0]; } else { [self.signatures addObject:model]; } } - (BOOL)removeSignatureCertPath:(NSString *)filePath { if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { return NO; } for (KMDSignatureModel * model in self.signatures) { if ([model.filePath isEqual:filePath]) { [self.signatures removeObject:model]; [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; break; } } if ([[NSFileManager defaultManager] fileExistsAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC]) { [[NSFileManager defaultManager] removeItemAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC error:nil]; for (KMDSignatureModel * model in self.signatures) { NSString *tag = [DSignatureApperanceManager manager].tagString; NSMutableDictionary *newDictionary = [NSMutableDictionary dictionary]; NSMutableDictionary * dic = [NSMutableDictionary dictionary]; [dic setValue:model.filePath forKey:Swift_oc_Tool.SAVEFILEPATH_KEY_OC]; if (model.password) { [dic setValue:model.password forKey:Swift_oc_Tool.PASSWORD_KEY_OC]; } [newDictionary setObject:dic forKey:tag]; [newDictionary writeToFile:Swift_oc_Tool.kDigitalSignaturePlistPath_OC atomically:YES]; } } return YES; } - (BOOL)importKeyChainWithP12FilePath:(NSString *)filePath passWord:(NSString *)passWord { SecIdentityRef identity = nil; NSData *PKCS12Data = [NSData dataWithContentsOfFile:filePath]; CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data; CFStringRef password = (__bridge CFStringRef)passWord; const void *keys[] = { kSecImportExportPassphrase }; const void *values[] = { password }; CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL); CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items); CFRelease(options); CFRelease(password); if (securityError == errSecSuccess) { CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0); identity = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity); } else { NSLog(@"Error opening Certificate."); } if (identity) { [self addSecIdentityRef:identity certPath:nil passWord:nil]; return YES; } else { return NO; } } + (BOOL)exportKeyChainWithP12FilePath:(NSString *)filePath signatureModel:(KMDSignatureModel *)model passWord:(NSString *)passWord { BOOL isSave = NO; SecIdentityRef identityRef = model.identityRef; CFDataRef dataRef = NULL; SecItemImportExportKeyParameters params; memset(¶ms, 0, sizeof(params)); params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; params.flags = 0; params.passphrase = (__bridge CFTypeRef _Nullable)(passWord); params.alertTitle = NULL; params.alertPrompt = NULL; params.accessRef = NULL; OSStatus status = SecItemExport(identityRef, kSecFormatPKCS12, 0, ¶ms, &dataRef); if (status == errSecSuccess) { isSave = [(__bridge NSData*)dataRef writeToFile:filePath atomically:YES]; } return isSave; } - (BOOL)moveP12DigitalFileWithFilePath:(NSString *)filePath password:(NSString *)password { if (![[NSFileManager defaultManager] fileExistsAtPath:Swift_oc_Tool.kDigitalSignatureFolderPath_OC]) { if (![[NSFileManager defaultManager] createDirectoryAtPath:Swift_oc_Tool.kDigitalSignatureFolderPath_OC withIntermediateDirectories:YES attributes:nil error:nil]) { return NO; } } if (![[NSFileManager defaultManager] fileExistsAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC]) { if (![[NSFileManager defaultManager] createFileAtPath:Swift_oc_Tool.kDigitalSignaturePlistPath_OC contents:nil attributes:nil]) { return NO; } } NSString* toPath = [Swift_oc_Tool.kDigitalSignatureFolderPath_OC stringByAppendingPathComponent:filePath.lastPathComponent]; toPath = [self getUniqueFilePath:toPath]; BOOL isSuccess = [[NSFileManager defaultManager] copyItemAtPath:filePath toPath:toPath error:nil]; if (isSuccess) { NSString *tag = [DSignatureApperanceManager manager].tagString; NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:Swift_oc_Tool.kDigitalSignaturePlistPath_OC]; NSMutableDictionary *newDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary]; NSMutableDictionary * dic = [NSMutableDictionary dictionary]; [dic setValue:toPath forKey:Swift_oc_Tool.SAVEFILEPATH_KEY_OC]; if (password) { [dic setValue:password forKey:Swift_oc_Tool.PASSWORD_KEY_OC]; } [newDictionary setObject:dic forKey:tag]; if ([newDictionary writeToFile:Swift_oc_Tool.kDigitalSignaturePlistPath_OC atomically:YES]) { [self addSignatureCertPath:toPath passWord:password]; return YES; } } return NO; } + (NSString *)stringFromDNwithSubjectName:(CFArrayRef)array { if (!array || CFArrayGetCount(array) == 0) { return @""; } NSMutableString *out = [NSMutableString string]; const void *keys[] = { kSecOIDCommonName, kSecOIDOrganizationName, kSecOIDOrganizationalUnitName, kSecOIDEmailAddress, kSecOIDLocalityName, kSecOIDStateProvinceName, kSecOIDCountryName }; const char *labels[] = { "cn", "o", "ou", "email", "l", "s", "c" }; for (int i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) { for (CFIndex n = 0; n < CFArrayGetCount(array); n++) { CFDictionaryRef dict = CFArrayGetValueAtIndex(array, n); if (!dict || CFGetTypeID(dict) != CFDictionaryGetTypeID()) { continue; } CFTypeRef dictkey = CFDictionaryGetValue(dict, kSecPropertyKeyLabel); if (!dictkey || !CFEqual(dictkey, keys[i])) { continue; } CFStringRef str = CFDictionaryGetValue(dict, kSecPropertyKeyValue); if (str) { NSString *keyValue = [NSString stringWithFormat:@"%s=%@", labels[i], (__bridge NSString *)str]; if (out.length < 1) { [out appendString:keyValue]; } else { [out appendFormat:@",%@", keyValue]; } } } } return out; } @end