Apple, біль і сертифікати

Знайомтеся, Боб — досвідчений ios розробник, Аліса — не менш запекла тестировщица. Справа була ввечері справа була в п'ятницю. Боб дофиксил багу, начебто протестил на своїх девайсах. Потім Боб запускає вже відточені до автоматизму команди:

git checkout develop
git merge bug_fix_#999
git checkout master && git merge develop --no-ff ....
git push ....


На пуш на сервері спрацьовує jenkins/teamcity/travis, який запускає білд. У цей же самий час наш Боб пише Алісі, що скоро піде додому, і хоче, щоб аппа пішла сьогодні в стор на апрув, щоб выйграть зайві пару днів, так як на носі вихідні, якщо звичайно додаток пройде ручне тестування Аліси.

Додаток Боба задоволене звичайне: пару сотень компилируемых клас файлів, ще з десяток cocoapods залежностей ну і купка сторибордов — Боб цінує свій і колег час і не пише UI в коді, будь як Боб. Боб знає, що його додаток з чистого старту на сервері збирається за 4 хвилини для develop версії, яка йде на тест Алісі, і стільки ж або трохи більше для production версії. Боб знає, що йому потрібно близько 10 хвилин, щоб дочекатися закінчення повного складання і потім повідомити Алісі, що вона може приступати до тестування. Боб людина відповідальна, тому після закінчення 10 хвилин після пуша перевіряє статус білду, так як знає, що сервер це окремий паралельний світ зі своїми правилами, законами і дивацтвами.

П'ятниця, вечір, Боба відокремлює від довгоочікуваних вихідних лише 10 хвилин, після яких передасть естафету Алісі. Боб вбиває з сафарі bobcompany.ci/dashboard, де бачить червону лампочку навпроти свого додатку, очі Боба потьмяніли, розчаруванню не було меж. Боб тисне на show more, де його зустрічає помилка:

Code Sign error: No codesigning identities found: No codesigning identities (i.e. certificate and private key pairs) that match the provisioning profile specified in build your settings (“com.company.bob") were found.


Тут нерви Боба зовсім здають:





*Коротко про помилку, вона проявляється коли ми намагаємося підписати додаток неіснуючим сертифікатом, під неіснуючим розуміється або він не встановлений на машині, або він застарів і .mobileprovision заведений на більш свіжу версію сертифікату того ж облікового запису того ж пакету.

І найприкріше, що ця помилка тільки для production версії, яка запускається другий, причому, спочатку xcode компілює залежності (cocoapods) і вже тільки після перевіряє валідність підпису, коли збирає основне додаток. Тому помилка проявляється приблизно у другій половині процесу складання, з-за чого перші 6-7 хвилин витрачені в порожню, від чого Боб засмутився ще більше.

Наш розробник Боб не перший раз стикається з цією проблемою, адже він працює у великій компанії, де кілька команд ios девелоперів, де нормальна практика для розробки використовувати один Apple аккаунт на кілька людей. Так, Боб в курсі тулзы https://github.com/neonichu/FixCode, але ж не змушувати ж її насильно всім ставити, розробники ніжні створіння, не всі люблять коли їх змушують щось робити проти їхньої волі, Боб сам такий.

Бобу все це настільки набридло, що він уже забув, що збирався додому 10 хвилин тому, замість цього Боб замовляє піцу, расчехляет макбук, який вже встиг запакувати, наливає каву, просить бэкендщиков/адмінів дати доступ до сервера, коннектітся туди по ssh і починає з'ясовувати в чому ж проблема і як її можна полагодити.

Ну окей. Першим ділом Боб перевіряє які сертифікати взагалі є на машині:

security find-identity -v login.keychain


що видає

1) 40948A3CA3527F580B9ECB2131DE6B1938FB3D7C "iPhone Developer: Mike ... (KSDA3C3QF2)"
2) 0279CB81AEAD8CE015282DD1FA76CE520A815C4D "iPhone Developer: Bob .. (4WT74HLM2M)"
3) 79A2544B1A63C3F9D3DA3FFAB199FEAADB7EC306 "iPhone Developer: Alica ... (VJ53F2J4EK)"
....
24 valid identities found


Так, як мінімум, в keychain, який використовується за дэфолту на сервері, 24 сертифіката. Боб знає, що кожен .mobileprovision файл створюється на якийсь один конкретний сертифікат. Потрібно з'ясувати на якій сертифікат створений .mobileprovision файл для якого впав білд і зрозуміти, що ж сталося з сертифікатом. Потрібно взагалі зрозуміти як .mobileprovision файл пов'язаний з сертифікатом. Боб знає, що девелоп версія програми зібралася, тому на сервері точно є сертифікат і зараз потрібно зрозуміти як він співвідноситися з .mobileprovision файлом. Для цього Бобу потрібно знайти .mobileprovision файл і дані про сертифікаті, щоб почати шукати якісь відповідності між ними.

Шукаємо .mobileprovision файл по його бандлу ід (якщо ваш бандл wildcard, то можна шукати за будь-якими іншими ознаками):

cd ~/Library/MobileDevice/Profiles Provisioning
find . -name ".*..mobileprovision" -type f -exec grep -H -n -a {} -e "com\.company\.bob" \;


Відмінно, ми знайшли наш файл:

./f98a06f3-21c2-4de0-975f-5df74197c731..mobileprovision:30: <string>4HUHB9J47M.com.company.bob</string>


У файлі є префікс 4HUHB9J47M у бандла. З раніше отриманого списку сертифікатів нічого з цим значенням не збігається. Тому Бобу доводиться йти в developer.apple.com і шукати на який же обліковий запис створений цей .mobileprovision файл. Методом тику він з'ясовує, що це сертифікат Майка:

2) 40948A3CA3527F580B9ECB2131DE6B1938FB3D7C "iPhone Developer: Mike .. (KSDA3C3QF2)"


Відмінно, будемо працювати з цим сертифікатом. Боб у нас не криптоаналітик або секурити гай, але він хороший гуглер, тому він з легкістю знайшов розколупати сертифікат.

Витягуємо дані .pem файл:

security find-certificate -p -c "iPhone Developer: Mike .. (KSDA3C3QF2)" > cert.pem


у файлі отримаємо наступне:

----- BEGIN CERTIFICATE-----
MIIFnjCCBIagAwIBAgIIN8GwnYhLQ/kwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgnv
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmmumswwkgydvqqldcnbchbszsbxb3js
ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczfemeiga1ueaww7qxbwbgugv29ybgr3
aWRlIERldmVsb3BlciBSZWxhdGlvbnMgq2vydglmawnhdglvbibbdxrob3jpdhkw
...
....
.....
lD+ocFo6+mab/Ph6mTJOZkZu+hnqhzbTD9Q9dXKWkeXAwTqaESNfnhnuOdfcx3vu
YAz0Hb46G9fkLa5lHjVydbtms685C+uz9Ss4GNRfji1cz5KyblAQAAsqQBUiCwnb
z34=
-----END CERTIFICATE-----


Як виявилося з цього файлу можна вважати інформацію про сертифікат, спробуємо отримати найбільш цікаву для нас:

openssl x509 -noout -fingerprint -in cert.pem


видає нам:

SHA1 Fingerprint=40:94:8A:3C:A3:52:7F:58:0B:9E:CB:21:31:DE:6B:19:38:FB:3D:7C


якщо прибрати двокрапки, то отримаємо 40948A3CA3527F580B9ECB2131DE6B1938FB3D7C, це як раз sha1 сертифіката Майка, який ми можемо побачити в UI в Keychain:



ми також можемо отримати термін валідності сертифіката, якщо потрібно написати валідатор минулих сертифікатів:

openssl x509 -noout -startdate -in cert.pem // Feb 27 07:13:41 GMT 2016
openssl x509 -noout -a list -in cert.pem // Feb 26 07:13:41 2017 GMT


це ж ми бачимо і в keychain:



Загалом з сертифікатом все ясно. «Як же його прив'язати до нашого .mobileprovision файлу?» — думає цікавий Боб. Давайте спочатку подивимося на .mobileprovision файл ближче:



security cms -D -i f98a06f3-21c2-4de0-975f-5df74197c731..mobileprovision 


висновок нам дає цікаву інформацію для поля data, а саме:

<data>
MIIFnjCCBIagAwIBAgIIN8GwnYhLQ/kwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgnv
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmmumswwkgydvqqldcnbchbszsbxb3js
ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczfemeiga1
...
...
YAz0Hb46G9fkLa5lHjVydbtms685C+uz9Ss4GNRfji1cz5KyblAQAAsqQBUiCwnb
z34=
</data>


Десь це Боб вже бачив, схоже на вмістом раніше отриманого .pem файлу:

----- BEGIN CERTIFICATE-----
MIIFnjCCBIagAwIBAgIIN8GwnYhLQ/kwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgnv
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmmumswwkgydvqqldcnbchbszsbxb3js
ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczfemeiga1ueaww7qxbwbgugv29ybgr3
aWRlIERldmVsb3BlciBSZWxhdGlvbnMgq2vydglmawnhdglvbibbdxrob3jpdhkw
...
....
.....
lD+ocFo6+mab/Ph6mTJOZkZu+hnqhzbTD9Q9dXKWkeXAwTqaESNfnhnuOdfcx3vu
YAz0Hb46G9fkLa5lHjVydbtms685C+uz9Ss4GNRfji1cz5KyblAQAAsqQBUiCwnb
z34=
-----END CERTIFICATE-----




Тут Боб зрозумів, ось вона зачіпка .mobileprovision файлу на сертифікат.

Разом нам потрібно взяти наш .mobileprovision файл, витягнути з нього поле data, потім пробігти по всіх валідним сертифікатами і порівняти з даними з їх .pem подання. Боб тут же накидав невеликий скрипт, який передав бэкендщикам, щоб вони його запускали перед кожною складанням ios проектів. Який працює досить просто:

ruby cert_checker.rb f98a06f3-21c2-4de0-975f-5df74197c731..mobileprovision


ruby скрипт
require 'active_support/core_ext/hash'

return if ARGV.empty?

xmlString = `security cms -D -i #{ARGV.first}`
data = Hash.from_xml(xmlString)

# отримали значення data з .mobileprovision
provision_cert_data = data['plist']['dict']['array'].map { |e| e['data'] }.compact.first

# пробігаємо по всім сертифікатам і збираємо їх дані .pem форматі
certs = `security find-identity -v login.keychain | grep -o "\\".*\\""`.split("\n").map { |e| e[1..-2] }
pems = certs.map { |e| `security find-certificate -p -c "#{e}"`.split("\n")[1..-2].join(") }
dict = Hash[pems.zip(certs)] 

# порівнюємо .mobileprovision data с .pem даними сертифікатів, якщо співпаде, значить знайшли сертифікат для .mobileprovision, 
# якщо не знайшли, значить на машині не встановлений сертифікат

if dict.keys.keep_if { |e| e = provision_cert_data }.empty? 
puts "Have no certificate for file #{ARGV.first}"
else
puts "Your .mobileprovision issued for #{dict[provision_cert_data]}"
end



Тепер розробники можуть швидко отримати фитбэк від сервера, якщо з ними щось не так і взагалі в пусту не запускати збірку до вирішення проблеми.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.