class MotionProvisioning::Certificate
Attributes
output_path[RW]
platform[RW]
type[RW]
Public Instance Methods
available_identities()
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 238 def available_identities ids = [] keychain = $keychain || "#{Dir.home}/Library/Keychains/login.keychain" available = `security find-identity -v -p codesigning #{keychain}` available.split("\n").each do |current| next if current.include? "REVOKED" begin id = current.match(/.*\) (.*) \"(.*)\"/) ids << { fingerprint: id[1], name: id[2] } rescue # the last line does not match end end ids end
certificate_name(type, platform)
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 13 def certificate_name(type, platform) self.type = type self.platform = platform self.output_path = MotionProvisioning.output_path certificate_path = File.join(output_path, "#{platform}_#{type}_certificate.cer") private_key_path = File.join(output_path, "#{platform}_#{type}_private_key.p12") # First check if there is a certificate and key file, and if it is installed identities = available_identities if File.exist?(certificate_path) && File.exist?(private_key_path) && ENV['recreate_certificate'].nil? fingerprint = sha1_fingerprint(certificate_path) installed_cert = identities.detect { |e| e[:fingerprint] == fingerprint } if installed_cert Utils.log("Info", "Using certificate '#{installed_cert[:name]}'.") return installed_cert[:fingerprint] else # The certificate is not installed, so we install the cert and the key import_file(private_key_path) import_file(certificate_path) Utils.log("Info", "Using certificate '#{common_name(certificate_path)}'.") return sha1_fingerprint(certificate_path) end end # Make sure a client is created and logged in client # Lets see if any of the user certificates is in the keychain installed_certificate = nil if !certificates.empty? installed_certs_sha1 = identities.map { |e| e[:fingerprint] } installed_certificate = certificates.detect do |certificate| sha1 = OpenSSL::Digest::SHA1.new(certificate.motionprovisioning_certContent || certificate.download_raw) installed_certs_sha1.include?(sha1.to_s.upcase) end end if certificates.empty? # There are no certificates in the server so we create a new one Utils.log("Warning", "Couldn't find any existing certificates. Creating a new certificate...") if certificate = create_certificate return sha1_fingerprint(certificate) else Utils.log("Error", "Something went wrong when trying to create a new certificate.") abort end elsif installed_certificate.nil? # There are certificates in the server, but none are installed locally. Revoke all and create a new one. Utils.log("Error", "None of the available certificates (#{certificates.count}) are installed locally. Revoking...") # For distribution, ask before revoking if self.type == :distribution answer = Utils.ask("Info", "There #{certificates.count == 1 ? 'is 1' : "are #{certificates.count}"} distribution certificate(s) in your account, but none installed locally.\n" \ "Before revoking and creating a new certificate, ask other team members who might have them installed to share them with you.\n" \ "Do you want to continue revoking the certificates? (Y/n):") abort if answer.no? end # Revoke all and create new one if MotionProvisioning.free certificates.each do |certificate| client.revoke_development_certificate(certificate.motionprovisioning_serialNumber) end else certificates.each(&:revoke!) end if certificate = create_certificate return sha1_fingerprint(certificate) else Utils.log("Error", "Something went wrong when trying to create a new certificate...") abort end elsif ENV['recreate_certificate'] != nil # Force recreate certificate, even though a valid certificate is already installed locally if self.type == :distribution answer = Utils.ask("Info", "There are #{certificates.count} distribution certificates in your account.\n" \ "Before revoking and creating a new one, ask other team members who might have them installed to share them with you.\n" \ "Do you want to revoke the existing certificates? (Y/n):") if answer.yes? certificates.each(&:revoke!) end else answer = Utils.ask("Info", "You have #{certificates.count} development certificates in your account.\n" \ "Do you want to revoke your existing certificates? (Y/n):") if answer.yes? if MotionProvisioning.free certificates.each do |certificate| client.revoke_development_certificate(certificate.motionprovisioning_serialNumber) end else certificates.each(&:revoke!) end end end if certificate = create_certificate return sha1_fingerprint(certificate) else Utils.log("Error", "Something went wrong when trying to create a new certificate.") abort end else # There are certificates on the server, and one of them is installed locally. Utils.log("Info", "Found certificate '#{installed_certificate.name}' which is installed in the local machine.") path = store_certificate_raw(installed_certificate.motionprovisioning_certContent || installed_certificate.download_raw) password = Utils.ask_password("Info", "Exporting private key from Keychain for certificate '#{installed_certificate.name}'. Choose a password (you will be asked for this password when importing this key into the Keychain in another machine):") private_key_contents = private_key(common_name(path), sha1_fingerprint(path), password) File.write(private_key_path, private_key_contents) # This certificate is installed on the local machine Utils.log("Info", "Using certificate '#{installed_certificate.name}'.") sha1_fingerprint(path) end end
certificate_type()
click to toggle source
The kind of certificate we're interested in
# File lib/motion-provisioning/certificate.rb, line 156 def certificate_type cert_type = nil case platform when :ios, :tvos cert_type = Spaceship.certificate.production cert_type = Spaceship.certificate.in_house if Spaceship.client.in_house? cert_type = Spaceship.certificate.development if self.type == :development when :mac cert_type = Spaceship.certificate.mac_development cert_type = Spaceship.certificate.mac_app_distribution if self.type == :distribution cert_type = Spaceship.certificate.developer_id_application if self.type == :developer_id end cert_type end
certificates()
click to toggle source
All certificates of this type
# File lib/motion-provisioning/certificate.rb, line 134 def certificates @certificates ||= begin if MotionProvisioning.free client.development_certificates(mac: mac?).map do |cert| certificate = Spaceship::Portal::Certificate.factory(cert) certificate.motionprovisioning_certContent = cert['certContent'].read certificate.motionprovisioning_serialNumber = cert['serialNumber'] certificate end else certificates = certificate_type.all # Filter out development certificates belonging to other team members if self.type == :development user_id = MotionProvisioning.team['currentTeamMember']['teamMemberId'] certificates.select! { |c| c.owner_id == user_id } end certificates end end end
client()
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 5 def client MotionProvisioning.client end
common_name(path)
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 268 def common_name(path) result = `openssl x509 -in "#{path}" -inform der -noout -sha1 -subject` begin return result.match(/\/CN=(.*?)\//)[1] rescue Utils.log("Error", "Error parsing certificate '#{path}'") end end
create_certificate()
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 175 def create_certificate # Create a new certificate signing request csr, pkey = create_certificate_signing_request # Store all that onto the filesystem request_path = File.expand_path(File.join(self.output_path, "#{platform}_#{type}.certSigningRequest")) File.write(request_path, csr.to_pem) private_key_path = File.expand_path(File.join(self.output_path, "#{platform}_#{type}_private_key.p12")) File.write(private_key_path, pkey.export) # Use the signing request to create a new distribution certificate begin certificate = nil certificate_attrs = nil if MotionProvisioning.free certificate_attrs = client.create_development_certificate(csr.to_pem) # Fetch the certificate again because the response does not contain # the certContent key certificate_attrs = client.development_certificates(mac: mac?).detect do |cert| cert['certificateId'] == certificate_attrs['certificateId'] end certificate_attrs['certificateTypeDisplayId'] = certificate_attrs['certificateType']['certificateTypeDisplayId'] certificate = Spaceship::Portal::Certificate.factory(certificate_attrs) certificate.motionprovisioning_certContent = certificate_attrs['certContent'].read certificate else certificate = certificate_type.create!(csr: csr) end rescue => ex if ex.to_s.include?("You already have a current") FileUtils.rm(private_key_path) FileUtils.rm(request_path) Utils.log("Error", "Could not create another certificate, reached the maximum number of available certificates. Manually revoke certificates in the Developer Portal.") abort end raise ex end Utils.log("Info", "Successfully created certificate.") cert_path = store_certificate_raw(certificate.motionprovisioning_certContent || certificate.download_raw) # Import all the things into the Keychain import_file(private_key_path) import_file(cert_path) Utils.log("Info", "Successfully installed certificate.") if self.type == :distribution Utils.log("Warning", "You have just created a distribution certificate. These certificates must be shared with other team members by sending them the private key (.p12) and certificate (.cer) files in your 'provisioning' folder and install them in the keychain.") else Utils.log("Warning", "You have just created a development certificate. If you want to use this certificate on another machine, transfer the private key (.p12) and certificate (.cer) files in your 'provisioning' folder and install them in the keychain.") end Utils.ask("Info", "Press any key to continue...") cert_path end
create_certificate_signing_request()
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 171 def create_certificate_signing_request $motion_provisioninig_csr || Spaceship.certificate.create_certificate_signing_request end
import_file(path)
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 277 def import_file(path) unless File.exist?(path) Utils.log("Error", "Could not find file '#{path}'") abort end keychain = $keychain || "#{Dir.home}/Library/Keychains/login.keychain" command = "security import #{path.shellescape} -k '#{keychain}' -P ''" command << " -T /usr/bin/codesign" command << " -T /usr/bin/security" `#{command} 2>&1` end
mac?()
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 9 def mac? self.platform == :mac end
private_key(name, fingerprint, password)
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 292 def private_key(name, fingerprint, password) export_private_key = File.join(File.expand_path(__dir__), '../../bin/export_private_key') `#{export_private_key} "#{name}" "#{fingerprint}" "#{password}"`.strip end
sha1_fingerprint(path)
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 257 def sha1_fingerprint(path) result = `openssl x509 -in "#{path}" -inform der -noout -sha1 -fingerprint` begin result = result.match(/SHA1 Fingerprint=(.*)/)[1] result.delete!(':') return result rescue Utils.log("Error", "Error parsing certificate '#{path}'") end end
store_certificate_raw(raw_data)
click to toggle source
# File lib/motion-provisioning/certificate.rb, line 232 def store_certificate_raw(raw_data) path = File.expand_path(File.join(self.output_path, "#{platform}_#{type}_certificate.cer")) File.write(path, raw_data) path end