sign_pass 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/bin/rsign_pass +42 -0
  2. data/lib/sign_pass.rb +229 -0
  3. metadata +58 -0
data/bin/rsign_pass ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sign_pass'
4
+ require 'optparse'
5
+
6
+ options = {}
7
+ optparse = OptionParser.new do |opts|
8
+ # Set a banner, displayed at the top of the screen
9
+ opts.banner = "Usage: rsign_pass -p /path/to/pass/directory -c /path/to/ssl/certificate -w certififcate-password -o /path/for/output/file"
10
+
11
+ options[:pass_path] = ""
12
+ opts.on('-p', '--pass FILE', String, 'Path to the pass directory') do |file|
13
+ puts file
14
+ options[:pass_path] = file
15
+ end
16
+
17
+ opts.on('-c', '--certificate FILE', String, 'Path to the certificate') do |file|
18
+ options[:certificate] = file
19
+ end
20
+
21
+ opts.on('-w', '--password PASSWORD', String, 'Certificate password') do |password|
22
+ options[:certificate_password] = password
23
+ end
24
+
25
+
26
+ opts.on('-o', '--output FILE', String, 'File location for the output') do |file|
27
+ options[:output] = file
28
+ end
29
+
30
+ opts.on('-f', '--force', 'Force pass signing by removing manifest and signiture if needed') do |b|
31
+ options[:force_pass_signing] = b
32
+ end
33
+
34
+ opts.on('-h', '--help', 'Display this screen') do
35
+ puts opts
36
+ exit
37
+ end
38
+ end
39
+ optparse.parse!
40
+ pass = SignPass.new(options[:pass_path], options[:certificate], options[:certificate_password], options[:output])
41
+ puts options[:force_pass_signing]
42
+ pass.sign_pass!(options[:force_pass_signing])
data/lib/sign_pass.rb ADDED
@@ -0,0 +1,229 @@
1
+ #
2
+ # File: sign_pass.rb
3
+ #
4
+ # Abstract: Pass Server reference implementation
5
+ #
6
+ # Version: 1.0
7
+ #
8
+ # Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple")
9
+ # in consideration of your agreement to the following terms, and your use,
10
+ # installation, modification or redistribution of this Apple software
11
+ # constitutes acceptance of these terms. If you do not agree with these
12
+ # terms, please do not use, install, modify or redistribute this Apple
13
+ # software.
14
+ #
15
+ # In consideration of your agreement to abide by the following terms, and
16
+ # subject to these terms, Apple grants you a personal, non - exclusive
17
+ # license, under Apple's copyrights in this original Apple software ( the
18
+ # "Apple Software" ), to use, reproduce, modify and redistribute the Apple
19
+ # Software, with or without modifications, in source and / or binary forms;
20
+ # provided that if you redistribute the Apple Software in its entirety and
21
+ # without modifications, you must retain this notice and the following text
22
+ # and disclaimers in all such redistributions of the Apple Software. Neither
23
+ # the name, trademarks, service marks or logos of Apple Inc. may be used to
24
+ # endorse or promote products derived from the Apple Software without specific
25
+ # prior written permission from Apple. Except as expressly stated in this
26
+ # notice, no other rights or licenses, express or implied, are granted by
27
+ # Apple herein, including but not limited to any patent rights that may be
28
+ # infringed by your derivative works or by other works in which the Apple
29
+ # Software may be incorporated.
30
+ #
31
+ # The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
32
+ # WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
33
+ # WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
34
+ # PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION
35
+ # ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
36
+ #
37
+ # IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
38
+ # CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40
+ # INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
41
+ # AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER
42
+ # UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR
43
+ # OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
+ #
45
+ # Copyright ( C ) 2012 Apple Inc. All Rights Reserved.
46
+ #
47
+
48
+ require 'rubygems'
49
+ require 'fileutils'
50
+ require 'tmpdir'
51
+ require 'digest/sha1'
52
+ require 'json'
53
+ require 'openssl'
54
+ require 'zip/zip'
55
+ require 'zip/zipfilesystem'
56
+
57
+
58
+ class SignPass
59
+ attr_accessor :pass_url, :certificate_url, :certificate_password, :output_url, :compress_into_zip_file, :temporary_directory, :temporary_path, :manifest_url, :signature_url, :wwdr_intermediate_certificate_path
60
+
61
+
62
+ def initialize(pass_url, certificate_url, certificate_password, wwdr_intermediate_certificate_path, output_url, compress_into_zip_file=true)
63
+ self.pass_url = pass_url
64
+ self.certificate_url = certificate_url
65
+ self.certificate_password = certificate_password
66
+ self.wwdr_intermediate_certificate_path = wwdr_intermediate_certificate_path
67
+ self.output_url = output_url
68
+ self.compress_into_zip_file = compress_into_zip_file
69
+ end
70
+
71
+
72
+ def sign_pass!(force_clean_raw_pass=false)
73
+ # Validate that requested contents are not a signed and expanded pass archive.
74
+ self.validate_directory_as_unsigned_raw_pass(force_clean_raw_pass)
75
+
76
+ # Get a temporary place to stash the pass contents
77
+ self.create_temporary_directory
78
+
79
+ # Make a copy of the pass contents to the temporary folder
80
+ self.copy_pass_to_temporary_location
81
+
82
+ # Clean out the unneeded .DS_Store files
83
+ self.clean_ds_store_files
84
+
85
+ # Build the json manifest
86
+ self.generate_json_manifest
87
+
88
+ # Sign the manifest
89
+ self.sign_manifest
90
+
91
+ # Package pass
92
+ self.compress_pass_file
93
+
94
+ # Clean up the temp directory
95
+ #self.delete_temp_dir
96
+ end
97
+
98
+
99
+ # private
100
+
101
+ # Ensures that the raw pass directory does not contain signatures
102
+ def validate_directory_as_unsigned_raw_pass(force_clean=false)
103
+ if force_clean
104
+ force_clean_raw_pass
105
+ end
106
+
107
+ has_manifiest = File.exists?(File.join(self.pass_url, "/manifest.json"))
108
+ puts "Raw pass has manifest? #{has_manifiest}"
109
+
110
+ has_signiture = File.exists?(File.join(self.pass_url, "/signature"))
111
+ puts "Raw pass has signature? #{has_signiture}"
112
+
113
+ if has_signiture || has_manifiest
114
+ raise RuntimeError, "#{self.pass_url} contains pass signing artificats that need to be removed before signing."
115
+
116
+ end
117
+ end
118
+
119
+ def force_clean_raw_pass
120
+ puts "Force cleaning the raw pass directory."
121
+ if File.exists?(File.join(self.pass_url, "/manifest.json"))
122
+ File.delete(File.join(self.pass_url, "/manifest.json"))
123
+ end
124
+
125
+ if File.exists?(File.join(self.pass_url, "/signature"))
126
+ File.delete(File.join(self.pass_url, "/signature"))
127
+ end
128
+
129
+ end
130
+
131
+
132
+ # Creates a temporary place to work with the pass files without polluting the original
133
+ def create_temporary_directory
134
+ self.temporary_directory = Dir.mktmpdir
135
+ puts "Creating temp dir at #{self.temporary_directory}"
136
+ self.temporary_path = self.temporary_directory + "/" + self.pass_url.split("/").last
137
+
138
+ # Check if the directory exists
139
+ if File.directory?(self.temporary_path)
140
+ # Need to clean up the directory
141
+ FileUtils.rm_rf(self.temporary_path)
142
+ end
143
+
144
+ end
145
+
146
+ # Copies the pass contents to the temporary location
147
+ def copy_pass_to_temporary_location
148
+ puts "Copying pass to temp directory."
149
+ FileUtils.cp_r(self.pass_url, self.temporary_directory)
150
+ end
151
+
152
+
153
+ # Removes .DS_Store files if they exist
154
+ def clean_ds_store_files
155
+ puts "Cleaning .DS_Store files"
156
+ Dir.glob(self.temporary_path + "**/.DS_Store").each do |file|
157
+ File.delete(file)
158
+ end
159
+
160
+ end
161
+
162
+
163
+ # Creates a json manifest where each files contents has a SHA1 hash
164
+ def generate_json_manifest
165
+ puts "Generating JSON manifest"
166
+ manifest = {}
167
+ # Gather all the files and generate a sha1 hash
168
+ Dir.glob(self.temporary_path + "/**").each do |file|
169
+ manifest[File.basename(file)] = Digest::SHA1.hexdigest(File.read(file))
170
+ end
171
+
172
+ # Write the hash dictionary out to a manifest file
173
+ self.manifest_url = self.temporary_path + "/manifest.json"
174
+ File.open(self.manifest_url, "w") do |f|
175
+ f.write(manifest.to_json)
176
+ end
177
+
178
+ end
179
+
180
+ def sign_manifest
181
+ puts "Signing the manifest"
182
+ # Import the certificate
183
+ p12_certificate = OpenSSL::PKCS12::new(File.read(self.certificate_url), self.certificate_password)
184
+ wwdr_certificate = OpenSSL::X509::Certificate.new(File.read(self.wwdr_intermediate_certificate_path))
185
+
186
+ # Sign the data
187
+ flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED
188
+ signed = OpenSSL::PKCS7::sign(p12_certificate.certificate, p12_certificate.key, File.read(self.manifest_url), [wwdr_certificate], flag)
189
+
190
+ # Create an output path for the signed data
191
+ self.signature_url = self.temporary_path + "/signature"
192
+
193
+ # Write out the data
194
+ File.open(self.signature_url, "w") do |f|
195
+ f.syswrite signed.to_der
196
+ end
197
+ end
198
+
199
+ def compress_pass_file
200
+ puts "Compressing the pass"
201
+ zipped_file = File.open(self.output_url, "w")
202
+
203
+ Zip::ZipOutputStream.open(zipped_file.path) do |z|
204
+ Dir.glob(self.temporary_path + "/**").each do |file|
205
+ z.put_next_entry(File.basename(file))
206
+ z.print IO.read(file)
207
+ end
208
+ end
209
+ zipped_file
210
+ end
211
+
212
+ def delete_temp_dir
213
+ FileUtils.rm_rf(self.temporary_path)
214
+ end
215
+ end
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sign_pass
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Apple
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubyzip
16
+ requirement: &70275300012040 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70275300012040
25
+ description: A ruby implementation of the pass signing and packaging utility.
26
+ email: developer@apple.com
27
+ executables:
28
+ - rsign_pass
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - lib/sign_pass.rb
33
+ - bin/rsign_pass
34
+ homepage: http://www.apple.com
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.10
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Packages and signs passes.
58
+ test_files: []