ios - 如何通过命令行在 Xcode 中登录 Apple ID?
问题描述
我继承了一个 Xcode 设置,它构建了一个使用自动签名进行开发构建的 iOS 应用程序。我现在的任务是为这个项目构建一些 CI 设置,但不改变实际的 Xcode 项目。这意味着我现在无法切换到手动签名。
由于该项目在本地构建良好,我没想到这会成为一个大问题,但事实证明自动签名(显然,事后看来)需要您的 Xcode 登录到 Apple ID(Xcode => Preferences => Accounts ) 应该用于自动创建证书。
有没有办法通过命令行将 Apple ID 添加到 Xcode?
这是我已经做过的:
我已经环顾四周,但无法通过谷歌找到任何明显的答案。StackOverflow 上的所有问题和答案总是提到“只需快速打开 Xcode 并输入您的凭据”,遗憾的是这不适用于我们的 CI 设置。
我发现了这个 Jenkins “Xcode Plugin”,它可以让你导入一个.developerprofile
你可以从 Xcode 导出的插件。但是我的 Java 真的很生疏,我无法完全理解这是否“仅”导入配置文件和身份,或者帐户列表。
玩弄.developerprofile
我自己,它似乎将帐户信息(以及所有证书等)包含在一个.zip
文件中,因此您可以提取文件。这也包括accounts.keychain
and accounts.plist
,但它们都用密码加密 - 我不知道如何使用它来获取真实数据以进一步调查。
如果您添加新的 Apple ID,我还试图找出 Xcode 最初保存信息的位置:它似乎将帐户名和密码以及一些令牌放入您的“登录”(com.apple.gs.xcode.auth.com.apple.account.AppleIDAuthentication.token
)和“iCloud”钥匙串(Xcode-AlternateDSID
和Xcode-Token
)中。我也无法手动重新创建钥匙串访问中的现有条目,因为“访问控制”->“此项目的访问组:”在手动创建应用程序密码时总是不同的。将项目复制到要导出的新钥匙串中也不起作用,因为iCloud
钥匙串不允许我将东西复制到新钥匙串(即使在 iCloud 中禁用钥匙串同步后,钥匙串也被命名为“本地项目”) .
解决方案
首先,我不确定您正在尝试做的事情是一个好主意。
请记住,如果您要将 Xcode 设置为在每次构建时自动请求 iOS 开发人员证书,并且该构建在不同的机器上执行(例如,托管 CI,例如 Travis 或 Azure Pipelines),您的 iOS 开发人员证书将被吊销并每次都重生。
一个更好的选择(在我看来)是通过您的开发人员配置文件导出您的 iOS 开发证书和配置文件,并在您的构建环境中导入开发证书和配置文件。然后,如果需要,更新您的 Xcode 项目以使用您刚刚导入的证书和配置文件。
我认为 Fastlane 已经可以做到几乎所有这些。如果你正在寻找灵感,Azure Pipelines 具有类似的功能。有一个任务是安装配置文件,一个是安装证书,另一个是构建 Xcode 项目并允许您覆盖在签署产品时使用的证书和配置文件。
话虽如此,accounts.plist
和accounts.keychain
可能包含您正在寻找的信息。两个文件均使用 AES 加密进行加密。
用于加密文件的密钥是使用 PBKDF2(基于密码的密钥派生函数 2)从密码中派生的,使用以下参数:
- 哈希函数:SHA256
- 密码:您的密码
- Salt:密码的字节表示,使用 UTF8 编码
- 哈希迭代次数:33333
- 密钥长度:10
“幻数”是 AppleSecKeyDeriveFromPassword
函数使用的默认值,如此处所述并在此处实现
获得加密密钥后,您可以解密文件。您需要一个初始化向量 (IV),它也是 Apple 使用的默认值 - 一个 16 字节数组,完全由零组成。
在 C 中,您应该能够使用这样的代码来生成加密密钥:
CFStringRef password = CFSTR("verysecretstuff");
const char* saltBytes = CFStringGetCStringPtr(password, kCFStringEncodingUTF8);
CFDataRef salt = CFDataCreate(NULL, saltBytes, CFStringGetLength(password));
int keySizeInBits = kSecAES128;
CFNumberRef keySize = CFNumberCreate(NULL, kCFNumberIntType, &keySizeInBits);
int rounds = 33333;
CFNumberRef numberOfRounds = CFNumberCreate(NULL, kCFNumberIntType, &rounds);
CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(NULL, 3, NULL, NULL);
CFDictionaryAddValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeAES);
CFDictionaryAddValue(parameters, kSecAttrKeySizeInBits, keySize);
CFDictionaryAddValue(parameters, kSecAttrPRF, kSecAttrPRFHmacAlgSHA256);
CFDictionaryAddValue(parameters, kSecAttrRounds, numberOfRounds);
CFDictionaryAddValue(parameters, kSecAttrSalt, salt);
CFErrorRef error = NULL;
SecKeyRef key = SecKeyDeriveFromPassword(password, parameters, &error);
要解密数据,请使用:
const UInt *bytes = NULL; // Encrypted data
CFDataRef data = CFDataCreate(NULL, bytes, length);
CFErrorRef error = NULL;
SecTransformRef transform = SecDecryptTransformCreate(key, &error);
if ( transform == NULL )
{
CFShow(error);
CFRelease(error);
}
SecTransformSetAttribute(transform, kSecEncryptionMode, kSecModeCBCKey, &error);
SecTransformSetAttribute(transform, kSecPaddingKey, kSecPaddingPKCS7Key, &error);
SecTransformSetAttribute(transform, kSecTransformInputAttributeName, data, &error);
CFDataRef result = SecTransformExecute(transform, &error);
CFShow(result);
CFRelease(result);
CFRelease(data);
CFRelease(transform);
希望能帮助到你!
推荐阅读
- python - 如何为每个上传的文件创建 Tkinter 条目?
- javascript - 谷歌地图不适用于 Chrome 中的引导卡布局
- r - 谁使用 Shiny 输入来编写表格
- python - 如何显示来自纺织品的数据 - tkinter
- c# - C# Azure Function .NET Core 3 字符串编码问题
- javascript - 打印时如何在所有浏览器中将第一页底部的元素定位在固定位置?
- python - 我已经为要写入的字符串定义了一个函数,然后将每个单独的字母打印出来。但它不适用于 input()
- wordpress - client_max_body_size 指令在 Dokku Wordpress 应用程序上不起作用(?)
- c# - Dynamics CRM OrganizationServiceContext - 突然返回限制记录?
- python - 我们如何扩展数据框中的一列并将扩展结果的索引与原始数据框的索引匹配?