Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validate certificate and provisioning profile

On our iOS projects, we commit to the version control repository both the signing certificate and the provisioning profiles used to generate AdHoc and AppStore builds. This way, whenever a new developer downloads a new fresh copy of the app, he has everything he needs to create an AdHoc build for testers.

We are using Jenkins for Continous Integration, and I would like to have a script that does some sanity checks on the commited files. In particular, I'd like to check that the commited provisioning profiles were indeed generated with the signing certificate commited in the repository.

Does anyone know how to do this from the command line? I can't figure out the .mobileprovision file format, although it seems to be a signed binary plist file.

like image 664
pgb Avatar asked Jul 15 '11 20:07

pgb


1 Answers

Answering my own question, I hope this helps someone else.

Turns out, the mobileprovision file is a PKCS7 digitally signed message. It is not signed with the developer's certificate, but with Apple's one.

However, the data that's signed is an XML plist that contains the public key of the certificate you use to sign your binaries.

So basically, the steps are as follows:

  1. Extract the data from the PKCS7 file.
  2. Extract the public-key from the p12 file.
  3. Compare the two, and check if they are the same.

I managed to do this easily with Ruby, since it provides nice wrappers to OpenSSL. I left a script in Github, if anyone wants to use.

The relevant parts of the code are as follows:

profile = File.read(@profile_file)
certificate = File.read(@certificate_file)

p7 = OpenSSL::PKCS7.new(profile)
cert = OpenSSL::PKCS12.new(certificate, @certificate_password)

store = OpenSSL::X509::Store.new
p7.verify([], store)

plist = REXML::Document.new(p7.data)

plist.elements.each('/plist/dict/key') do |ele|
  if ele.text == "DeveloperCertificates"
    keys = ele.next_element
    key = keys.get_elements('//array/data')[0].text

    profile_cert = "-----BEGIN CERTIFICATE-----" + key.gsub(/\t/, "") + "-----END CERTIFICATE-----\n"

    @provisioning_cert = OpenSSL::X509::Certificate.new(profile_cert)
  end
end

# Compare @provisioning_cert.to_s and cert.certificate.to_s
like image 117
pgb Avatar answered Oct 14 '22 12:10

pgb