sign_pass 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/rsign_pass +42 -0
- data/lib/sign_pass.rb +229 -0
- 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: []
|