=================签名准备文件==================
首先是需要:xxxxx.p12
首先是需要:xxxx..mobileprovision
=============================================
①步骤
把 xxxxxx.p12 转换成 xxxx.cer 和 xxxx.pem
----------------------------------------------
①转换代码
openssl pkcs12 -in my.p12 -out xxx.pem -nodes
<得到:xxx.pem>两个任选一
openssl pkcs12 -clcerts -nokeys -out xxx.pem -in my.p12
<得到:xxx.pem>两个任选一
openssl pkcs12 -in my.p12 -out xxxx.crt -nokeys -clcerts
openssl x509 -inform pem -in xxxx.crt -outform der -out xxxx.cer
<得到:xxxx.cer>
========得到文件=保存起来=等会代码签名用===========
通过 xxxxx.p12转换
得到 xxxx.pem
得到 xxxx.cer
==================目前4个文件 列表===============
//自备文件
xxxxx.p12
xxxxx.mobileprovision
//通过xxx.p12转换得到文件
xxxx.pem
xxxx.cer
===============================================
//签名调用(xddSigning signIpaAtPath)
NSString *cachepath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
//签名保存文件路径
NSString *outputPath=[cachepath stringByAppendingPathComponent:@"Alook_qqqq_sign.ipa"];
//待签名文件路径<我是放到程序目录>线上可以读取Document
NSString *ipaPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Alook.ipa"];
[xddSigning signIpaAtPath:ipaPath outputPath:outputPath withCompletionHandler:^(NSError * error) {
if(error){
[xddCode xddLog:[NSString stringWithFormat:@"签名:%@",@"失败"]];
}else{
[xddCode xddLog:[NSString stringWithFormat:@"签名成功:路径=%@",outputPath]];
}
}];
// signIpaAtPath 签名具体实现
+ (void)signIpaAtPath:(NSString*)ipaPath outputPath:(NSString*)outputPath withCompletionHandler:(void (^)(NSError *))completionHandler {
// 1. Unpack IPA to a temporary directory.
NSError *error;
NSString *unpackedDirectory;
if (![xddCode unpackIpaAtPath:ipaPath outDirectory:&unpackedDirectory error:&error]) {
completionHandler(error);
return;
}
// 2. Sign its main bundle via above method.
// The bundle will be located at <temporarydirectory>/<zipfilename>/Payload/*.app internally
NSString *zipFilename = [ipaPath lastPathComponent];
zipFilename = [zipFilename stringByReplacingOccurrencesOfString:@".ipa" withString:@""];
NSString *payloadDirectory = [NSString stringWithFormat:@"%@/Payload", unpackedDirectory];
NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:payloadDirectory error:&error];
if (error) {
completionHandler(error);
return;
} else if (files.count == 0) {
NSError *err = [xddCode _errorFromString:@"Payload directory of IPA has no contents"];
completionHandler(err);
return;
}
NSString *dotAppDirectory = @"";//获取程序包
for (NSString *directory in files) {
if ([directory containsString:@".app"]) {
dotAppDirectory = directory;
break;
}
}
//...../Payload/xxxx.app
NSString *bundleDirectory = [NSString stringWithFormat:@"%@/%@", payloadDirectory, dotAppDirectory];
NSLog(@"Signing bundle at path '%@'", bundleDirectory);
[xddCode xddLog:[NSString stringWithFormat:@"正在签名:%@",bundleDirectory]];
//开始签名
[self signBundleAtPath:bundleDirectory withCompletionHandler:^(NSError *err) {
if (err) {
completionHandler(err);
return;
}
// 3. Repack IPA to output path
NSError *error2;
if (![xddCode repackIpaAtPath:[NSString stringWithFormat:@"%@/%@", [xddCode applicationTemporaryDirectory], zipFilename] toPath:outputPath error:&error2]) {
completionHandler(error2);
} else {
// Success!
completionHandler(nil);
[xddCode xddLog:[NSString stringWithFormat:@"签名完毕:paAtPath=%@",[NSString stringWithFormat:@"%@/%@", [xddCode applicationTemporaryDirectory], zipFilename]]];
[xddCode xddLog:[NSString stringWithFormat:@"签名完毕:toPath=%@",outputPath]];
}
}];
}
+ (void)signBundleAtPath:(NSString*)path withCompletionHandler:(void (^)(NSError *error))completionHandler {
// We need to handle application extensions, e.g. watchOS applications and VPN plugins etc.
// These are stored in the bundle's root directory at the following locations:
// - /Plugins
// - /Watch
// Therefore, recurse through those directories as required before continuing for the root directory.
dispatch_group_t dispatch_group = dispatch_group_create();
NSMutableArray * __block subBundleErrors = [NSMutableArray array];
if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"%@/PlugIns", path]]) {
// Recurse through the plugins.
for (NSString *subBundle in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[NSString stringWithFormat:@"%@/PlugIns", path] error:nil]) {
NSString *__block subBundlePath = [NSString stringWithFormat:@"%@/PlugIns/%@", path, subBundle];
// Enter the dispatch group
dispatch_group_enter(dispatch_group);
NSLog(@"Handling sub-bundle: %@", subBundlePath);
// Sign the bundle
[self signBundleAtPath:subBundlePath withCompletionHandler:^(NSError *error) {
if (error)
[subBundleErrors addObject:error];
NSLog(@"Finished sub-bundle: %@", subBundlePath);
dispatch_group_leave(dispatch_group);
}];
}
}
if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:@"%@/Watch", path]]) {
// Recurse through the watchOS stuff.
for (NSString *subBundle in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[NSString stringWithFormat:@"%@/Watch", path] error:nil]) {
NSString * __block subBundlePath = [NSString stringWithFormat:@"%@/Watch/%@", path, subBundle];
// Enter the dispatch group
dispatch_group_enter(dispatch_group);
NSLog(@"Handling sub-bundle: %@", subBundlePath);
// Sign the bundle
[self signBundleAtPath:subBundlePath withCompletionHandler:^(NSError *error) {
if (error)
[subBundleErrors addObject:error];
NSLog(@"Handled sub-bundle: %@", subBundlePath);
dispatch_group_leave(dispatch_group);
}];
}
}
// Wait on sub-bundles to finish, if needed.
dispatch_group_wait(dispatch_group, DISPATCH_TIME_FOREVER);
if (subBundleErrors.count > 0) {
// Errors when handling sub-bundles!
for (NSError *err in subBundleErrors) {
NSLog(@"Error: %@", err.localizedDescription);
}
completionHandler([subBundleErrors lastObject]);
return;
}
// 1. Read Info.plist to gain the applicationId and binaryLocation.
// 2. Get provisioning profile and certificate info
// 3. Sign bundle
NSDictionary *infoplist = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", path]];
if (!infoplist || [infoplist allKeys].count == 0) {
NSError *error = [xddCode _errorFromString:@"Failed to open Info.plist!"];
completionHandler(error);
return;
}
// Find the systemType for this bundle.
NSString *platformName = [infoplist objectForKey:@"DTPlatformName"];
EESystemType systemType = -1;
if ([platformName isEqualToString:@"iphoneos"]) {
systemType = EESystemTypeiOS;
} else if ([platformName isEqualToString:@"watchos"]) {
systemType = EESystemTypewatchOS;
} else if ([platformName isEqualToString:@"tvos"]) {
systemType = EESystemTypetvOS;
} else {
// Base case, assume iOS.
systemType = EESystemTypeiOS;
}
NSLog(@"Platform: %@ for bundle: %@", platformName, [path lastPathComponent]);
NSString *applicationId = [infoplist objectForKey:@"CFBundleIdentifier"];
NSString *binaryLocation = [path stringByAppendingFormat:@"/%@", [infoplist objectForKey:@"CFBundleExecutable"]];
NSLog(@"applicationId: %@ for bundle: %@", applicationId, [path lastPathComponent]);
NSLog(@"binaryLocation: %@ for bundle: %@", binaryLocation, [path lastPathComponent]);
// We get entitlements from the binary using ldid::Analyze() during provisioning, updating them as needed
// for the current Team ID.
{
// We now have a valid provisioning profile for this application!
// And, we also have a valid development codesigning certificate, with its private key!
// Add embedded.mobileprovision to the bundle, overwriting if needed.
NSError *fileIOError;
NSString *embeddedPath = [NSString stringWithFormat:@"%@/embedded.mobileprovision", path];
NSLog(@"embeddedPath=%@", embeddedPath);
//删除旧签名文件
if ([[NSFileManager defaultManager] fileExistsAtPath:embeddedPath]) {
[[NSFileManager defaultManager] removeItemAtPath:embeddedPath error:&fileIOError];
if (fileIOError) {
NSLog(@"removeItemAtPath%@", fileIOError);
return;
}
}
//写入新的
NSString *mobileprovisionFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Web/qqqq.mobileprovision"];
//最后:从文件中读出 xxxx.mobileprovision
NSData *embeddedMobileProvision = [NSData dataWithContentsOfFile:mobileprovisionFileName options:0 error:NULL];
if (![(NSData*)embeddedMobileProvision writeToFile:embeddedPath options:NSDataWritingAtomic error:&fileIOError]) {
if (fileIOError) {
NSLog(@"writeToFile err:%@", fileIOError);
} else {
NSLog(@"writeToFile Failed to write '%@'.", embeddedPath);
}
return;
}
// Next step: signing. To do this, we use EESigner with these four results.
NSString *p12FileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Web/qqqq.cer"];
//从文件中读出 配置文件 xxxx.cer
NSData *p12Provision = [NSData dataWithContentsOfFile:p12FileName options:0 error:NULL];//从FileName中读取出数据
//从文件中读出 秘钥 xxxx.pem
NSString *ProvisionFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Web/qqqq.pem"];
NSData *ProvisionNsData = [[NSData alloc] initWithContentsOfFile:ProvisionFileName];
NSString *privateKey = [[NSString alloc] initWithData:ProvisionNsData encoding:NSUTF8StringEncoding];
//获取 Entitlements
NSDictionary* entitlements = [xddCode getEntitlements:mobileprovisionFileName];
//创建签名类signer
EESigning *signer = [EESigning signerWithCertificate:p12Provision privateKey:privateKey];
//EESigning *signer = [NSClassFromString(@"EESigning") signerWithCertificate:p12Provision privateKey:privateKey];
//调用签名开始
[signer signBundleAtPath:path entitlements:entitlements identifier:applicationId withCallback:^(BOOL success, NSString *result) {
NSLog(@"signBundleAtPath.result=%@", result);
NSString*title = @"成功";
NSError *error = nil;
if (!success) {
title = @"失败";
}
[xddCode xddLog:[NSString stringWithFormat:@"signBundleAtPath title=%@,result=%@",title,result]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// We will now pause so that ldid can cleanup after itself.
[NSThread sleepForTimeInterval:1];
NSError *error = nil;
if (!success) {
error = [xddCode _errorFromString:result];
}
// We're done.
completionHandler(error);
});
}];
}
}