ios-cert-enrollment 0.0.2

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.
@@ -0,0 +1,8 @@
1
+ require File.expand_path('../ios-cert-enrollment/configuration', __FILE__)
2
+ require File.expand_path('../ios-cert-enrollment/sign', __FILE__)
3
+ require File.expand_path('../ios-cert-enrollment/profile', __FILE__)
4
+ require File.expand_path('../ios-cert-enrollment/device', __FILE__)
5
+
6
+ module IOSCertEnrollment
7
+ extend Configuration
8
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path('../configuration', __FILE__)
2
+ require "openssl"
3
+
4
+ module IOSCertEnrollment
5
+ class Certificate
6
+
7
+ attr_accessor :certificate, :mime_type
8
+
9
+ def initialize(certificate,mime_type)
10
+ self.certificate = certificate
11
+ self.mime_type = mime_type
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path('../version', __FILE__)
2
+
3
+ module IOSCertEnrollment
4
+ # Defines constants and methods related to configuration
5
+ module Configuration
6
+ VALID_OPTIONS_KEYS = [
7
+ :ssl_certificate_path,
8
+ :ssl_key_path,
9
+ :base_url,
10
+ :identifier,
11
+ :display_name,
12
+ :organization
13
+ ].freeze
14
+
15
+ attr_accessor *VALID_OPTIONS_KEYS
16
+
17
+ # Convenience method to allow configuration options to be set in a block
18
+ def configure
19
+ yield self
20
+ end
21
+
22
+ # Create a hash of options and their values
23
+ def options
24
+ VALID_OPTIONS_KEYS.inject({}) do |option, key|
25
+ option.merge!(key => send(key))
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path('../configuration', __FILE__)
2
+ require File.expand_path('../certificate', __FILE__)
3
+ require File.expand_path('../ssl', __FILE__)
4
+
5
+ require "plist"
6
+
7
+ module IOSCertEnrollment
8
+ module Device
9
+ class << self
10
+ def parse(p7sign)
11
+ return Plist::parse_xml(p7sign.data)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,153 @@
1
+ require File.expand_path('../configuration', __FILE__)
2
+ require "rubygems"
3
+ require "uuidtools"
4
+ require "plist"
5
+ module IOSCertEnrollment
6
+ class Profile
7
+ attr_accessor :url, :identifier, :display_name, :description, :icon, :payload, :organization, :expiration
8
+ def initialize(url="")
9
+ self.url = IOSCertEnrollment.base_url + url
10
+ self.identifier = IOSCertEnrollment.identifier
11
+ self.display_name = IOSCertEnrollment.display_name
12
+ self.organization = IOSCertEnrollment.organization
13
+ self.description = ""
14
+ self.expiration = nil
15
+
16
+ end
17
+
18
+ def service
19
+ payload = general_payload()
20
+ payload['PayloadType'] = "Profile Service" # do not modify
21
+ payload['PayloadIdentifier'] = self.identifier+".mobileconfig.profile-service"
22
+
23
+ # strings that show up in UI, customizable
24
+ payload['PayloadDisplayName'] = self.display_name
25
+ payload['PayloadDescription'] = self.description
26
+
27
+ payload_content = Hash.new
28
+ payload_content['URL'] = self.url
29
+ payload_content['DeviceAttributes'] = [
30
+ "UDID",
31
+ "VERSION",
32
+ "PRODUCT", # ie. iPhone1,1 or iPod2,1
33
+ "DEVICE_NAME", # given device name "My iPhone"
34
+ "MAC_ADDRESS_EN0",
35
+ "IMEI",
36
+ "ICCID"
37
+ ];
38
+
39
+ payload['PayloadContent'] = payload_content
40
+ self.payload = Plist::Emit.dump(payload)
41
+ return self
42
+ end
43
+
44
+ def encrypted_service
45
+ ## ASA encryption_cert_payload
46
+ payload = general_payload()
47
+
48
+ payload['PayloadIdentifier'] = self.identifier+".encrypted-profile-service"
49
+ payload['PayloadType'] = "Configuration" # do not modify
50
+
51
+ # strings that show up in UI, customisable
52
+ payload['PayloadDisplayName'] = self.display_name
53
+ payload['PayloadDescription'] = self.description
54
+
55
+ payload['PayloadContent'] = [encryption_cert_request("Profile Service")];
56
+ self.payload = Plist::Emit.dump(payload)
57
+ return self
58
+ end
59
+
60
+
61
+ def webclip
62
+
63
+ webclip_payload = general_payload()
64
+
65
+ webclip_payload['PayloadIdentifier'] = self.identifier+".webclip.tester"
66
+ webclip_payload['PayloadType'] = "com.apple.webClip.managed" # do not modify
67
+
68
+ # strings that show up in UI, customisable
69
+ webclip_payload['PayloadDisplayName'] = self.display_name
70
+ webclip_payload['PayloadDescription'] = self.description
71
+
72
+ # allow user to remove webclip
73
+ webclip_payload['IsRemovable'] = true
74
+ webclip_payload['FullScreen'] = true
75
+ webclip_payload['Icon'] = self.icon
76
+ webclip_payload['Precomposed'] = true
77
+ # the link
78
+ webclip_payload['Label'] = self.display_name
79
+ webclip_payload['URL'] = self.url
80
+
81
+ #client_cert_payload = scep_cert_payload(request, "Client Authentication", "foo");
82
+
83
+ self.payload = Plist::Emit.dump(payload)
84
+ return self
85
+
86
+ end
87
+
88
+
89
+
90
+ def configuration(encrypted_content)
91
+ payload = general_payload()
92
+ payload['PayloadIdentifier'] = self.identifier+".intranet"
93
+ payload['PayloadType'] = "Configuration" # do not modify
94
+
95
+ # strings that show up in UI, customisable
96
+ payload['PayloadDisplayName'] = self.display_name
97
+ payload['PayloadDescription'] = self.description
98
+ payload['PayloadExpirationDate'] = self.expiration || Date.today + (360 * 10) # expire in 10 years
99
+
100
+ payload['EncryptedPayloadContent'] = StringIO.new(encrypted_content)
101
+ self.payload = Plist::Emit.dump(payload)
102
+ return self
103
+
104
+ end
105
+
106
+ def sign
107
+ signed_profile = OpenSSL::PKCS7.sign(SSL.certificate, SSL.key, self.payload, [], OpenSSL::PKCS7::BINARY)
108
+ return Certificate.new(signed_profile.to_der, "application/x-apple-aspen-config")
109
+
110
+ end
111
+
112
+ def encrypt(certificates)
113
+ encrypted_profile = OpenSSL::PKCS7.encrypt(certificates, self.payload, OpenSSL::Cipher::Cipher::new("des-ede3-cbc"), OpenSSL::PKCS7::BINARY)
114
+ return Certificate.new(encrypted_profile.to_der, "application/x-apple-aspen-config")
115
+
116
+ end
117
+
118
+
119
+ private
120
+ def encryption_cert_request(purpose)
121
+ ## AKA scep_cert_payload
122
+ payload = general_payload()
123
+
124
+
125
+ payload['PayloadIdentifier'] = self.identifier+".encryption-cert-request"
126
+ payload['PayloadType'] = "com.apple.security.scep" # do not modify
127
+
128
+ payload['PayloadDisplayName'] = purpose
129
+ payload['PayloadDescription'] = "Provides device encryption identity"
130
+
131
+ payload_content = Hash.new
132
+ payload_content['URL'] = self.url
133
+ payload_content['Subject'] = [ [ [ "O", self.organization ] ],
134
+ [ [ "CN", purpose + " (" + UUIDTools::UUID.random_create().to_s + ")" ] ] ];
135
+
136
+ payload_content['Keysize'] = 1024
137
+ payload_content['Key Type'] = "RSA"
138
+ payload_content['Key Usage'] = 5 # digital signature (1) | key encipherment (4)
139
+ payload_content['GetCACaps'] = ["POSTPKIOperation","Renewal","SHA-1"]
140
+
141
+ payload['PayloadContent'] = payload_content;
142
+ payload
143
+ end
144
+
145
+ def general_payload()
146
+ payload = Hash.new
147
+ payload['PayloadVersion'] = 1 # do not modify
148
+ payload['PayloadUUID'] = UUIDTools::UUID.random_create().to_s # should be unique
149
+ payload['PayloadOrganization'] = self.organization
150
+ payload
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,85 @@
1
+ require File.expand_path('../configuration', __FILE__)
2
+ require File.expand_path('../certificate', __FILE__)
3
+ require File.expand_path('../ssl', __FILE__)
4
+
5
+ require "openssl"
6
+
7
+ module IOSCertEnrollment
8
+ module Sign
9
+
10
+ class << self
11
+ def registration_authority
12
+ scep_certs = OpenSSL::PKCS7.new()
13
+ scep_certs.type="signed"
14
+ scep_certs.certificates=[SSL.certificate,SSL.certificate]
15
+ return Certificate.new(scep_certs.to_der, "application/x-x509-ca-ra-cert")
16
+ end
17
+
18
+ def certificate_authority_caps
19
+ return "POSTPKIOperation\nSHA-1\nDES3\n"
20
+ end
21
+
22
+ def sign_PKI(data)
23
+
24
+ p7sign = OpenSSL::PKCS7.new(data)
25
+ store = OpenSSL::X509::Store.new
26
+ p7sign.verify(nil, store, nil, OpenSSL::PKCS7::NOVERIFY)
27
+ signers = p7sign.signers
28
+ p7enc = OpenSSL::PKCS7.new(p7sign.data)
29
+
30
+ # Certificate Signing Request
31
+ csr = p7enc.decrypt(SSL.key, SSL.certificate)
32
+
33
+ # Signed Certificate
34
+ cert = self.sign_certificate(csr)
35
+
36
+ degenerate_pkcs7 = OpenSSL::PKCS7.new()
37
+ degenerate_pkcs7.type="signed"
38
+ degenerate_pkcs7.certificates=[cert]
39
+ enc_cert = OpenSSL::PKCS7.encrypt(p7sign.certificates, degenerate_pkcs7.to_der,
40
+ OpenSSL::Cipher::Cipher::new("des-ede3-cbc"), OpenSSL::PKCS7::BINARY)
41
+ reply = OpenSSL::PKCS7.sign(SSL.certificate, SSL.key, enc_cert.to_der, [], OpenSSL::PKCS7::BINARY)
42
+
43
+ return Certificate.new(reply.to_der, "application/x-pki-message")
44
+ end
45
+
46
+ def verify_response(raw_postback_data)
47
+ p7sign = OpenSSL::PKCS7.new(raw_postback_data)
48
+ store = OpenSSL::X509::Store.new
49
+ p7sign.verify(nil, store, nil, OpenSSL::PKCS7::NOVERIFY)
50
+ return p7sign
51
+ end
52
+ def verify_signer(p7sign)
53
+ signers = p7sign.signers
54
+
55
+ return (signers[0].issuer.to_s == SSL.certificate.subject.to_s)
56
+ end
57
+
58
+
59
+ end
60
+ private
61
+ def self.sign_certificate(signing_request)
62
+ request = OpenSSL::X509::Request.new(signing_request)
63
+
64
+ # New Certificate
65
+ cert = OpenSSL::X509::Certificate.new
66
+ cert.version = 2
67
+ cert.serial = 1
68
+ cert.subject = request.subject
69
+ cert.issuer = SSL.certificate.subject
70
+ cert.public_key = request.public_key
71
+ cert.not_before = Time.now
72
+ cert.not_after = Time.now+(86400*1)
73
+
74
+ # Prepare to sign
75
+ ef = OpenSSL::X509::ExtensionFactory.new
76
+ ef.subject_certificate = cert
77
+ ef.issuer_certificate = SSL.certificate
78
+ cert.add_extension(ef.create_extension("keyUsage", "digitalSignature,keyEncipherment", true))
79
+ cert.sign(SSL.key, OpenSSL::Digest::SHA1.new)
80
+
81
+ return cert
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,17 @@
1
+ module IOSCertEnrollment
2
+ module SSL
3
+ @@key, @@certificate = nil
4
+ class << self
5
+ def key
6
+ return @@key if @@key
7
+ return @@key = OpenSSL::PKey::RSA.new(File.read(IOSCertEnrollment.ssl_key_path))
8
+ end
9
+
10
+ def certificate
11
+ return @@certificate if @@certificate
12
+ return @@certificate = OpenSSL::X509::Certificate.new(File.read(IOSCertEnrollment.ssl_certificate_path))
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module IOSCertEnrollment
2
+ VERSION = '0.0.2'.freeze unless defined?(::IOSCertEnrollment::VERSION)
3
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ios-cert-enrollment
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Nolan Brown
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-10 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rake
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rdoc
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: uuidtools
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: plist
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ description: Easy tools to implement a SCEP server for iOS Configuration Profiles
77
+ email: nolanbrown@gmail.com
78
+ executables: []
79
+
80
+ extensions: []
81
+
82
+ extra_rdoc_files: []
83
+
84
+ files:
85
+ - lib/ios-cert-enrollment.rb
86
+ - lib/ios-cert-enrollment/certificate.rb
87
+ - lib/ios-cert-enrollment/configuration.rb
88
+ - lib/ios-cert-enrollment/device.rb
89
+ - lib/ios-cert-enrollment/profile.rb
90
+ - lib/ios-cert-enrollment/sign.rb
91
+ - lib/ios-cert-enrollment/ssl.rb
92
+ - lib/ios-cert-enrollment/version.rb
93
+ homepage: http://github.com/nolanbrown
94
+ licenses: []
95
+
96
+ post_install_message:
97
+ rdoc_options:
98
+ - --title
99
+ - iOS Portal
100
+ - --main
101
+ require_paths:
102
+ - lib
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ requirements: []
123
+
124
+ rubyforge_project:
125
+ rubygems_version: 1.8.24
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: SCEP server for iOS Configuration Profiles
129
+ test_files: []
130
+