Last active
January 3, 2016 08:19
-
-
Save odrobnik/8435035 to your computer and use it in GitHub Desktop.
Working on this to bundle up the files of a pkpass
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'json' | |
require "openssl" | |
# use current time as serial number | |
serialNumber = Time.now.to_i.to_s | |
eventDate = Time.new(2014, 07, 24, 20, 15, 0, "+02:00") | |
seat = "1A" | |
# date should be string | |
eventDateString = eventDate.utc.strftime("%Y-%m-%dT%H:%M:%SZ") | |
# assemble a "signed" barcode message | |
barcodeMessage = "TICKET:#{eventDateString},#{seat},#{serialNumber}" | |
salt = "EXTRA SECRET SAUCE" | |
barcodeMessageSignature = Digest::MD5.hexdigest barcodeMessage + salt | |
barcodeMessage = barcodeMessage + "|#{barcodeMessageSignature}" | |
# check/load WWDR root certificate | |
begin | |
root_cert_file = File.read('WWDR.pem') | |
root_cert = OpenSSL::X509::Certificate.new root_cert_file | |
rescue => err | |
puts "Cannot load root certificate: #{err}" | |
exit 1 | |
end | |
# check/load signing certificate | |
begin | |
certificate = OpenSSL::X509::Certificate.new File.read('passcertificate.pem') | |
rescue => err | |
puts "Cannot load signing certificate: #{err}" | |
exit 1 | |
end | |
# check/load private signing key | |
begin | |
key_file = File.read('passkey.pem') | |
key = OpenSSL::PKey::RSA.new key_file, '12345' | |
rescue => err | |
puts "Cannot load private signing key: #{err}" | |
exit 1 | |
end | |
# assemble the pass in a hash | |
pass = { | |
"formatVersion" => 1 | |
} | |
# add pass meta data | |
pass["passTypeIdentifier"] = "pass.com.drobnik.movienight" | |
pass["serialNumber"] = serialNumber | |
pass["teamIdentifier"] = "Z7L2YCUH45" | |
pass["organizationName"] = "Cocoanetics" | |
pass["logoText"] = "Cocoanetics" | |
pass["description"] = "VIP Movie Night Sofa Seat" | |
pass["relevantDate"] = eventDateString #{}"2014-01-16T11:00:00Z" | |
location = { | |
"longitude" => 48.0528, | |
"latitude" => 14.5877 | |
} | |
pass["locations"] = [location] | |
pass["maxDistance"] = 10000 | |
#Time.new.utc.strftime("%Y-%m-%dT%H:%M:%S%Z") | |
# create the barcode | |
barcode = { | |
"format" => "PKBarcodeFormatPDF417", | |
"messageEncoding" => "iso-8859-1" | |
} | |
barcode["message"] = barcodeMessage | |
pass["barcode"] = barcode | |
# header fields | |
headerFields = [{ | |
"key" => "seat", | |
"label" => "Seat", | |
"value" => seat | |
}] | |
# primary fields | |
primaryFields = [{ | |
"key" => "name", | |
"value" => "VIP Movie Night" | |
}] | |
# secondary fields | |
secondaryFields = [{ | |
"key" => "location", | |
"label" => "Location", | |
"value" => "Oliver's Home Movie Theater" | |
}] | |
# auxiliary fields | |
auxiliaryFields = [{ | |
"key" => "date", | |
"label" => "Event Date", | |
"dateStyle" => "PKDateStyleMedium", | |
"timeStyle" => "PKDateStyleShort", | |
"value" => eventDateString | |
}] | |
# fields on back of pass | |
backFields = [ | |
{ | |
"key" => "phone", | |
"label" => "For more info", | |
"value" => "800-1234567890" | |
}, | |
{ | |
"key" => "terms", | |
"label" => "TERMS AND CONDITIONS", | |
"value" => "Free popcorn and drink at entrance. Please arrive sufficiently early to pick your seat and allow show to start on time." | |
} | |
] | |
# put ticket fields together | |
eventTicketFields = { | |
"headerFields" => headerFields, | |
"primaryFields" => primaryFields, | |
"secondaryFields" => secondaryFields, | |
"auxiliaryFields" => auxiliaryFields, | |
"backFields" => backFields | |
} | |
pass["eventTicket"] = eventTicketFields | |
# create pass JSON string | |
passJSON = JSON.pretty_generate(pass) | |
# get SHA1 of pass JSON | |
passSHA1 = Digest::SHA1.hexdigest passJSON | |
# files that are possible in pkpass | |
possibleResources = ['icon.png', '[email protected]', | |
'thumbnail.png', '[email protected]', | |
'strip.png', '[email protected]', | |
'logo.png', '[email protected]', | |
'background.png', '[email protected]'] | |
# filter possible resources with actual files in folder | |
resources = possibleResources & Dir['*'] | |
# first file is the JSON | |
manifest = {"pass.json" => passSHA1} | |
# keep track of files to put in ZIP | |
zipCommand = ["zip", "-q", serialNumber + ".pkpass", "pass.json", "signature", "manifest.json"] | |
# iterate over resources | |
resources.each do |resource_file| | |
# load file contents into variable | |
file = File.open(resource_file, "rb") | |
contents = file.read | |
# get resource SHA1 | |
contents_SHA1 = Digest::SHA1.hexdigest contents | |
# add resource file and SHA1 to manifest hash | |
manifest[resource_file] = contents_SHA1 | |
zipCommand << resource_file | |
end | |
# create manifest JSON string | |
manifestJSON = JSON.pretty_generate(manifest) | |
# write manifest to disk | |
manifest_file = open("manifest.json", "w") | |
manifest_file.write(manifestJSON) | |
manifest_file.close | |
# write pass file to disk | |
pass_file = open("pass.json", "w") | |
pass_file.write(passJSON) | |
pass_file.close | |
# create signature | |
signature = OpenSSL::PKCS7.sign(certificate, key, manifestJSON, [root_cert], OpenSSL::PKCS7::BINARY | OpenSSL::PKCS7::DETACHED).to_der | |
# write signature to disk | |
signature_file = open("signature", "wb") | |
signature_file.write signature | |
signature_file.close | |
# execute zip command | |
system(*zipCommand) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment