📜 ⬆️ ⬇️

Apple pain and certificates

Meet Bob, a seasoned ios developer, Alice is a no less tester. It was in the evening it was on Friday. Bob prefixed the bug, it seems to be tested on his devices. Then Bob starts the commands already honed to automatism:

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


On the push on the server, jenkins / teamcity / travis is triggered, which starts the build. At the same time, our Bob writes to Alice that he will soon go home, and wants the app to leave today in the app store for April in order to win an extra couple of days, since it’s the weekend, if the application passes Alisa’s manual testing.
')
Bob’s application is pretty ordinary: a couple of hundred compiled class files, another ten or so cocoapods of dependencies and a handful of storyboards — Bob appreciates his time and the time of his colleagues, so he doesn’t write UI in code. Bob knows that his application from a clean start on the server is collected in 4 minutes to develop a version that goes to test Alice, and as much or slightly more for the production version. Bob also knows that he needs about 10 minutes to wait until the end of the complete assembly and then inform Alice that she can start testing. Bob is a responsible person, so after 10 minutes after pushing, he checks the build status, because he knows that the server is a separate parallel world with its own rules, laws, and oddities.

Friday evening, Bob is only 10 minutes away from the long-awaited weekend, after which he passes the baton to Alice. Bob drives a bobcompany.ci/dashboard into a safari, where he sees a red light bulb in front of his application, Bob’s eyes faded, there was no limit to disappointment. Bob presses on show more , where he gets an error:

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


Here Bob's nerves completely pass:





* Briefly about the error, it manifests itself when we try to sign an application with a non-existent certificate, a non-existent means either it is not installed on the machine, or it is outdated and the mobileprovision is installed on a more recent version of the certificate of the same account for the same bundle.

And the most annoying is that this error is only for the production version of the build, which runs the second after the develop version, and, first, xcode compiles dependencies (cocoapods) and only after that checks the signature validity, that is, only when the main application builds. Therefore, the error appears in about the second half of the build process, which is why the first 6-7 minutes are spent empty, which makes Bob even more upset.

Our developer Bob is not the first time faced with this problem, because he works in a large company, where several teams of ios developers, where the normal practice for the development is to use one Apple account for several people. Yes, Bob knows about the Xcode plugin https://github.com/neonichu/FixCode , but you don’t have to force him to install it forcibly, the developers are gentle creatures, not everyone likes being forced to do something against their will, Bob himself such.

Bob got so sick of it all that he had already forgotten that he was going home 10 minutes ago, instead Bob orders pizza, uncovers a macbook that he has already packed, pours coffee, asks admins to give access to the server, connects there via ssh and starts to figure out what exactly is the problem and how can it be solved.

OK. First of all, Bob checks which certificates are in general on the machine:

 security find-identity -v login.keychain 


What gives

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


So, at least in keychain, which is used according to the default on the server, there are 24 certificates. Bob knows that each mobileprovision file is created for any one particular certificate. You need to find out on which certificate mobileprovision created the file for which the build fell and understand what happened to the certificate. It is generally necessary to understand how the mobileprovision file is associated with the certificate. Bob knows that the develop version of the application is assembled, so the server definitely has this certificate and now you need to understand how it relates to the mobileprovision file. To do this, Bob needs to find the mobileprovision file and certificate information in order to start looking for some matches between them.

We are looking for a mobileprovision file by its bundle (if your bundle is a wildcard, then you can search for any other signs):

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


Great, we found our file:

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


The file has the prefix 4HUHB9J47M for the bundle. From the previously obtained list of certificates, nothing matches this value. Therefore, Bob has to go to developer.apple.com and look for what account created this mobileprovision file. At random he finds out that this is Mike's certificate:

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


Great, now we have something to work with. Bob is not a cryptanalyst or a security surveyor, but he is a good googler, so he easily found a way to get the necessary information from the certificate.

Pull the data into the .pem file:

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


In the file we get the following:

 -----BEGIN CERTIFICATE----- MIIFnjCCBIagAwIBAgIIN8GwnYhLQ/kwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw ... .... ..... lD+ocFo6+mab/Ph6mTJOZkZu+hnqhzbTD9Q9dXKWkeXAwTqaESNfnhnuOdfCX3vu YAz0Hb46G9fkLa5lHjVydbtms685C+uz9Ss4GNRfji1cz5KyblAQAAsqQBUiCwnb z34= -----END CERTIFICATE----- 


As it turned out, from this file you can read the certificate information, try to get the most interesting for us:

 openssl x509 -noout -fingerprint -in cert.pem 


Gives us:

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


If we remove the colon, we get 40948A3CA3527F580B9ECB2131DE6B1938FB3D7C, this is just Mike's sha1 certificate, which we can see in the UI in Keychain:



We can also get the certificate validity period if we need to write a validator of expired certificates:

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


We see the same in keychain:



In general, the certificate is clear. “How can it be tied to our mobileprovision file?” The curious Bob thinks. Let's first take a closer look at the mobileprovision file:



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


The output gives us interesting information for the data field, namely:

 <data> MIIFnjCCBIagAwIBAgIIN8GwnYhLQ/kwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1 ... ... YAz0Hb46G9fkLa5lHjVydbtms685C+uz9Ss4GNRfji1cz5KyblAQAAsqQBUiCwnb z34= </data> 


Somewhere this Bob has already seen, similar to the content of the previously received .pem file:

 -----BEGIN CERTIFICATE----- MIIFnjCCBIagAwIBAgIIN8GwnYhLQ/kwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw ... .... ..... lD+ocFo6+mab/Ph6mTJOZkZu+hnqhzbTD9Q9dXKWkeXAwTqaESNfnhnuOdfCX3vu YAz0Hb46G9fkLa5lHjVydbtms685C+uz9Ss4GNRfji1cz5KyblAQAAsqQBUiCwnb z34= -----END CERTIFICATE----- 




Then Bob understood, here it is the mobileprovision file hook on the certificate.

So, we need to take our mobileprovision file, pull out the value for the data field, then run through all valid certificates and compare with the data from their .pem submission. Bob immediately added a small script that he passed to his colleagues from the backend so that they would run it before each build of ios projects. The script runs quite simply:

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


ruby script
 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 



Now developers can quickly get feedback from the server if there is something wrong with the certificates. And in general, do not run the assembly until the problem is solved.

Source: https://habr.com/ru/post/302100/


All Articles