Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sign in to Apple ID in Xcode via command line?

I inherited a Xcode setup that builds an iOS app that uses Automatic Signing for the development builds. I now have the task to build some CI setup for this project, but without changing the actual Xcode project. This means I can't switch to manual signing for now.

As the project is building fine locally, I didn't expect this to be a big problem, but it turns out Automatic Signing (obviously, in hindsight) needs your Xcode to be signed into the Apple ID (Xcode => Preferences => Accounts) that should be used for automatically creating certificates.

Is there a way to add an Apple ID to Xcode via the command line?


This is what I already did:

I looked around already, but could not find any obvious answers via Google. All the questions and answers here on StackOverflow always mention "Just quickly open Xcode and enter your credentials" which unfortunately does not work on our CI setup.

I found this Jenkins "Xcode Plugin" that lets you import a .developerprofile that you can export from Xcode. But my Java is really rusty and I couldn't fully understand if this "only" imports profiles and identities, or also the list of accounts.

Playing around with an .developerprofile myself, it seems to include the account information (and all the certificates etc.) in a .zip file, so you can extract the files. That also includes a accounts.keychain and accounts.plist, but those are both encrypted with the password - which I don't know how to use to get to the real data to investigate there further.

I also tried to find out where Xcode originally saves the information if you add a new Apple ID: It seems to put the account names and passwords, and some token, into your "login" (com.apple.gs.xcode.auth.com.apple.account.AppleIDAuthentication.token) and "iCloud" keychain (Xcode-AlternateDSID and Xcode-Token). I also couldn't recreate the existing entries in Keychain access manually, as "Access Control" -> "Access group for this item:" was always different when creating an application password manually. Copying the items into a new keychain to be exported also didn't work, as the iCloud keychain doesn't let me copy stuff over to a new one (even after disabling keychain sync in iCloud, so the keychain is named "local items").

like image 295
janpio Avatar asked Feb 20 '19 13:02

janpio


People also ask

How do I log into my Apple ID using Xcode?

Add your Apple ID account and assign the target to a team so Xcode can enable the Sign in with Apple capability with your provisioning profile. Choose a run destination from the scheme pop-up menu that you're signed into with an Apple ID and that uses Two-Factor Authentication.

What is Apple Xcode command line tools?

The Command Line Tools Package is a small self-contained package available for download separately from Xcode and that allows you to do command line development in OS X. It consists of two components: OS X SDK and command-line tools such as Clang, which are installed in /usr/bin.

How do you access signing and capabilities in Xcode?

In the Project navigator of the main window, select the project — the root group with the same name as your app — and in the project editor that appears, select the appropriate target and then click the Signing & Capabilities tab.

How do I run a command in Xcode?

go to the 'Info' tab and in a menu 'Executable' choose 'Other...' in file window go to search input field and type 'terminal' and click on its icon when you find it. Now you should see 'Terminal. app' in 'Executable' field.


Video Answer


1 Answers

First off, I'm not sure that what you are trying to do is a good idea.

Keep in mind that if you are going to set up Xcode to automatically request iOS developer certificates on every build, and that build executes on different machines (say, hosted CI such as Travis or Azure Pipelines), your iOS developer certificate will be revoked and regenerated every time.

A much better option (in my opinion) is to export your iOS development certificates and provisioning profiles via your developer profile, and import the development certificate and provisioning profile in your build environment. Then, if needed, update your Xcode project to use the certificate and profile you've just imported.

I think Fastlane can already do pretty much all that. If you're looking for inspiration, Azure Pipelines has similar functionality. The have a task which installs a provisioning profile, one which installs a certificate and one which builds an Xcode project and allows you to override the certificate and provisioning profiles used when signing your product.


Having said that, the accounts.plist and the accounts.keychain probably contain the information you are looking for. Both files are encrypted using AES encryption.

The key used to encrypt the file is derived from the password using PBKDF2 (Password-Based Key Derivation Function 2), using these parameters:

  • Hashing function: SHA256
  • Password: Your password
  • Salt: The byte representation of your password, using UTF8-encoding
  • Number of hashing iterations: 33333
  • Key length: 10

The 'magic numbers' are the default values used by Apple's SecKeyDeriveFromPassword function, as described here and implemented here

Once you have your encryption key, you can decrypt the file. You'll need an initialization vector (IV), which again is the default value used by Apple - a 16-byte array consisting entirely of zeros.

In C, you should be able to use use code like this to generate the encryption key:

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);

To decrypt the data, use:

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);

Hope it helps!

like image 158
Frederik Carlier Avatar answered Oct 13 '22 14:10

Frederik Carlier