action_mailer_x509 0.7.0 → 0.7.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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- action_mailer_x509 (0.6.2)
4
+ action_mailer_x509 (0.7.2)
5
5
  actionmailer
6
6
  mail
7
7
  rake
@@ -32,7 +32,7 @@ GEM
32
32
  mail (2.5.4)
33
33
  mime-types (~> 1.16)
34
34
  treetop (~> 1.4.8)
35
- mime-types (1.23)
35
+ mime-types (1.25)
36
36
  minitest (4.7.5)
37
37
  multi_json (1.7.9)
38
38
  polyglot (0.3.3)
@@ -59,7 +59,7 @@ GEM
59
59
  thor (0.18.1)
60
60
  thread_safe (0.1.2)
61
61
  atomic
62
- treetop (1.4.14)
62
+ treetop (1.4.15)
63
63
  polyglot
64
64
  polyglot (>= 0.3.1)
65
65
  tzinfo (0.3.37)
@@ -79,7 +79,7 @@ Put this line in your Gemfile
79
79
 
80
80
  gem 'actionmailer_x509'
81
81
 
82
- == USING
82
+ == SETTINGS
83
83
 
84
84
  First for all - run install generator, which adds configuration file
85
85
 
@@ -120,10 +120,42 @@ And you may also change the default configuration in any place of the program:
120
120
 
121
121
  2) Change only for decoding and verifyng if you use Mail object
122
122
 
123
- Mail.new(encoded_text).proceed(configuration)
123
+ Mail.new(encoded_text).proceed(result_type, configuration)
124
124
 
125
125
  If you set configuration as nil in "proceed" func, "ActionMailerX509.default_configuration" will be used.
126
126
 
127
+
128
+ == USING
129
+
130
+ Encoding/Sign
131
+
132
+ You may set simpy set configuration for all funcs of mailer
133
+
134
+ class SendCrypted < ActionMailer::Base
135
+ x509_configuration :test
136
+ end
137
+
138
+ or set it how param of 'mail' func for each mailer method
139
+
140
+ class SendCrypted < ActionMailer::Base
141
+ def prepare_mail(from, to, subject, body, x509_configuration = ActionMailerX509.default_configuration)
142
+ mail(
143
+ from: from,
144
+ to: to,
145
+ subject: subject,
146
+ x509_configuration: x509_configuration
147
+ )do |format|
148
+ #format.text { render text: body }
149
+ format.html { render text: body }
150
+ end
151
+ end
152
+ end
153
+
154
+ Decoding/Verify
155
+
156
+ Mail.new(encoded_mail).proceed(result_type, configuration)
157
+
158
+
127
159
  == HELPERS
128
160
 
129
161
  You may use some helpers for fast encoding in case when you proceed received email. Helper has the following signature:
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'action_mailer_x509'
3
- s.version = '0.7.0'
3
+ s.version = '0.7.2'
4
4
  s.authors = ['Jenua Boiko']
5
5
  s.email = 'jeyboy1985@gmail.com'
6
6
  s.files = `git ls-files`.split("\n")
@@ -14,6 +14,5 @@ Gem::Specification.new do |s|
14
14
  s.add_dependency 'rake'
15
15
  s.add_dependency 'actionmailer'
16
16
  s.add_dependency 'mail'
17
- #s.add_development_dependency 'rspec'
18
17
  end
19
18
 
@@ -1,12 +1,6 @@
1
- class Configuration
2
- ATTRS = { 'C' => :country,
3
- 'ST' => :state,
4
- 'L' => :location,
5
- 'O' => :organization,
6
- 'OU' => :organizational_unit,
7
- 'CN' => :common_name,
8
- 'emailAddress' => :email}
1
+ require 'action_mailer_x509/security_object'
9
2
 
3
+ class Configuration
10
4
  def initialize(params = {})
11
5
  params.symbolize_keys!
12
6
  params.each_pair { |k, v| self.send("#{k}=".to_sym, v) }
@@ -88,7 +82,7 @@ class Configuration
88
82
 
89
83
  subject_attrs = worker.certificate.subject.to_a
90
84
  subject_attrs = subject_attrs.each_with_object({}) do |attr, obj|
91
- obj.update(ATTRS[attr.first] => attr[1])
85
+ obj.update(SecurityObject::ATTRS[attr.first] => attr[1])
92
86
  end
93
87
 
94
88
  {
@@ -0,0 +1,179 @@
1
+ class SecurityObject
2
+ ATTRS = { 'C' => :country,
3
+ 'ST' => :state,
4
+ 'L' => :location,
5
+ 'O' => :organization,
6
+ 'OU' => :organizational_unit,
7
+ 'CN' => :common_name,
8
+ 'emailAddress' => :email}
9
+
10
+ IATTRS = ATTRS.invert
11
+
12
+ #params: Hash
13
+ #key_length int - length of the rsa key
14
+ #password string - passphrase for key/certificate
15
+ #pack_key_with_pass bool - force repack rsa key with given password
16
+ #subject Hash - fields represented by ATTRS const
17
+ #time_from Time - certificate start time
18
+ #time_length int - certificate life time in seconds
19
+ #description string - p12 description
20
+ #file string - file path - where have be save new certificate
21
+ class << self
22
+ private
23
+ def to_file(data, path)
24
+ File.open(path, 'wb') do |file|
25
+ file.write(data)
26
+ end
27
+ end
28
+
29
+ def subject(params)
30
+ params ||= {}
31
+ params.symbolize_keys!
32
+ subject = params.each_with_object('') do |attr, obj|
33
+ obj << "/#{IATTRS[attr.first]}=#{attr[1]}"
34
+ end
35
+ OpenSSL::X509::Name.parse subject
36
+ end
37
+
38
+ def repack_key(rsa_key, password)
39
+ cipher = OpenSSL::Cipher::Cipher.new('des3')
40
+ private_key = rsa_key.to_pem(cipher, password)
41
+ public_key = rsa_key.public_key.to_pem
42
+ OpenSSL::PKey::RSA.new(private_key + public_key, password)
43
+ end
44
+
45
+ def rsa_key(params={})
46
+ key = OpenSSL::PKey::RSA.new(params[:key_length] || 4096) # the CA's public/private key
47
+ (repack_key(key, params[:password]) if params[:pack_key_with_pass]) || key
48
+ end
49
+
50
+ def certificate(params = {}, root_certificate = nil)
51
+ root_key = rsa_key(params)
52
+
53
+ root_ca = OpenSSL::X509::Certificate.new
54
+ root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate
55
+ root_ca.serial = 1
56
+ root_ca.subject = subject(params[:subject])
57
+ root_ca.issuer = (root_certificate || root_ca).subject # root CA's are "self-signed"
58
+ root_ca.public_key = root_key.public_key
59
+ root_ca.not_before = params[:time_from] || Time.now
60
+ root_ca.not_after = root_ca.not_before + (params[:time_length] || 1.year) #2 * 365 * 24 * 60 * 60 # 2 years validity
61
+
62
+ [ root_key, root_ca ]
63
+ end
64
+ public
65
+
66
+ def self_signed_certificate(params = {})
67
+ params.symbolize_keys!
68
+
69
+ root_key, root_ca = certificate(params)
70
+ root_ca.serial = 1
71
+
72
+ ef = OpenSSL::X509::ExtensionFactory.new
73
+ ef.subject_certificate = root_ca
74
+ ef.issuer_certificate = root_ca
75
+ root_ca.add_extension(ef.create_extension('basicConstraints', 'CA:TRUE', true))
76
+ root_ca.add_extension(ef.create_extension('keyUsage', 'keyCertSign, cRLSign', true))
77
+ root_ca.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
78
+ root_ca.add_extension(ef.create_extension('authorityKeyIdentifier', 'keyid:always', false))
79
+ root_ca.sign(root_key, OpenSSL::Digest::SHA256.new)
80
+
81
+
82
+ [ root_key, root_ca ]
83
+
84
+
85
+ # key = OpenSSL::PKey::RSA.new(1024)
86
+ # public_key = key.public_key
87
+ #
88
+ # subject = "/C=BE/O=Test/OU=Test/CN=Test"
89
+ #
90
+ # cert = OpenSSL::X509::Certificate.new
91
+ # cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
92
+ # cert.not_before = Time.now
93
+ # cert.not_after = Time.now + 365 * 24 * 60 * 60
94
+ # cert.public_key = public_key
95
+ # cert.serial = 0x0
96
+ # cert.version = 2
97
+ #
98
+ # ef = OpenSSL::X509::ExtensionFactory.new
99
+ # ef.subject_certificate = cert
100
+ # ef.issuer_certificate = cert
101
+ # cert.extensions = [
102
+ # ef.create_extension("basicConstraints","CA:TRUE", true),
103
+ # ef.create_extension("subjectKeyIdentifier", "hash"),
104
+ ## ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
105
+ # ]
106
+ # cert.add_extension ef.create_extension("authorityKeyIdentifier",
107
+ # "keyid:always,issuer:always")
108
+ #
109
+ # cert.sign key, OpenSSL::Digest::SHA1.new
110
+ end
111
+
112
+ def signed_certificate(params = {})
113
+ params.symbolize_keys!
114
+ root_key, root_ca = self_signed_certificate(params)
115
+ key, cert = certificate(params, root_ca)
116
+ cert.serial = 2
117
+
118
+ ef = OpenSSL::X509::ExtensionFactory.new
119
+ ef.subject_certificate = cert
120
+ ef.issuer_certificate = root_ca
121
+ cert.add_extension(ef.create_extension('keyUsage', 'digitalSignature', true))
122
+ cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
123
+ cert.sign(root_key, OpenSSL::Digest::SHA256.new)
124
+
125
+ [ key, cert ]
126
+ end
127
+
128
+
129
+ def p12_certificate(params = {})
130
+ params.symbolize_keys!
131
+
132
+ key, cert = signed_certificate(params)
133
+
134
+ #root_key = OpenSSL::PKey::RSA.new 4096 # the CA's public/private key
135
+ #root_ca = OpenSSL::X509::Certificate.new
136
+ #root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate
137
+ #root_ca.serial = 1
138
+ #root_ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby CA"
139
+ #root_ca.issuer = root_ca.subject # root CA's are "self-signed"
140
+ #root_ca.public_key = root_key.public_key
141
+ #root_ca.not_before = Time.now
142
+ #root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
143
+ #
144
+ #ef = OpenSSL::X509::ExtensionFactory.new
145
+ #ef.subject_certificate = root_ca
146
+ #ef.issuer_certificate = root_ca
147
+ #root_ca.add_extension(ef.create_extension('basicConstraints', 'CA:TRUE', true))
148
+ #root_ca.add_extension(ef.create_extension('keyUsage', 'keyCertSign, cRLSign', true))
149
+ #root_ca.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
150
+ #root_ca.add_extension(ef.create_extension('authorityKeyIdentifier', 'keyid:always', false))
151
+ #root_ca.sign(root_key, OpenSSL::Digest::SHA256.new)
152
+ #
153
+ ## The next step is to create the end-entity certificate using the root CA
154
+ ## certificate.
155
+ ##
156
+ #key = OpenSSL::PKey::RSA.new 4096
157
+ #cert = OpenSSL::X509::Certificate.new
158
+ #cert.version = 2
159
+ #cert.serial = 2
160
+ #cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby certificate"
161
+ #cert.issuer = root_ca.subject # root CA is the issuer
162
+ #cert.public_key = key.public_key
163
+ #cert.not_before = Time.now
164
+ #cert.not_after = cert.not_before + 1 * 365 * 24 * 60 * 60 # 1 years validity
165
+ #
166
+ #ef = OpenSSL::X509::ExtensionFactory.new
167
+ #ef.subject_certificate = cert
168
+ #ef.issuer_certificate = root_ca
169
+ #cert.add_extension(ef.create_extension('keyUsage', 'digitalSignature', true))
170
+ #cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
171
+ #cert.sign(root_key, OpenSSL::Digest::SHA256.new)
172
+
173
+ p12 = OpenSSL::PKCS12.create(params[:password], params[:description] || 'My Name', key, cert)
174
+ bytes = p12.to_der
175
+ to_file(bytes, path) if params[:file]
176
+ bytes
177
+ end
178
+ end
179
+ end
@@ -44,14 +44,13 @@ module ActionMailerX509
44
44
  end
45
45
 
46
46
  def sign(text)
47
- write OpenSSL::PKCS7.sign(certificate, rsa_key, text, [], OpenSSL::PKCS7::DETACHED)
48
- #OpenSSL::PKCS7.sign(certificate, rsa_key, text, [], OpenSSL::PKCS7::BINARY)
47
+ write OpenSSL::PKCS7.sign(certificate, rsa_key, text, [], OpenSSL::PKCS7::DETACHED|OpenSSL::PKCS7::BINARY)
49
48
  end
50
49
 
51
50
  def verify(text)
52
- result = read(text).verify(nil, @certificate_store, nil, nil)
53
- #read(text).verify(nil, @certificate_store, nil, OpenSSL::PKCS7::NOVERIFY)
54
- result ? read(text).data : raise(VerificationError.new('Wrong args'))
51
+ #set the signer's certificates are not chain verified.
52
+ result = read(text).verify(nil, @certificate_store, nil, OpenSSL::PKCS7::NOVERIFY)
53
+ result ? read(text).data : raise(VerificationError.new('Verification failed !!!'))
55
54
  rescue => e
56
55
  raise VerificationError.new(e.message)
57
56
  end
@@ -3,6 +3,14 @@ require 'action_mailer_x509'
3
3
  module Mail #:nodoc:
4
4
  class Message #:nodoc:
5
5
 
6
+ #fix mail gem bug on decoding
7
+ def subject( val = nil )
8
+ val ||= header[:subject].default
9
+ val.gsub!(' ', '_') if val && val.start_with?('=?')
10
+ default( :subject, val )
11
+ header[:subject].default
12
+ end
13
+
6
14
  def proceed(result_type = 'html', configuration = nil)
7
15
  proceed_part(_proceed(configuration), result_type)
8
16
  end
@@ -26,6 +34,13 @@ module Mail #:nodoc:
26
34
  parts.empty? ? to_part : body.encoded
27
35
  end
28
36
 
37
+ def get_states
38
+ {
39
+ crypted: (body.to_s =~ /application\/(x-)?pkcs[0-9]+-mime/).present?,
40
+ signed: (body.to_s =~ /application\/(x-)?pkcs[0-9]+-signature/).present?
41
+ }
42
+ end
43
+
29
44
  protected
30
45
  # Returns true if the message is a multipart/alternative
31
46
  def alternative?(part)
@@ -57,16 +72,18 @@ module Mail #:nodoc:
57
72
 
58
73
  _decode_body(result_type, @new_part || part)
59
74
  else
60
- part.decoded.to_s unless part.attachment?
75
+ part.decoded unless part.attachment?
61
76
  end
62
- end
77
+ end.to_s
63
78
 
64
79
  # we need manually split body on parts and decode each part separate
65
80
  def _decode_body(result_type, obj = self)
66
- obj.body.split(obj.boundary) if obj.parts.blank? && obj.boundary.present?
81
+ obj.body.split!(obj.boundary) if obj.parts.blank? && obj.boundary.present?
82
+
67
83
 
68
84
  if obj.parts.present?
69
- obj.parts.map {|part| proceed_part(part, result_type)}.join(break_line(result_type))
85
+ proceed_parts = obj.parts.map {|part| proceed_part(part, result_type).force_encoding('utf-8')}
86
+ proceed_parts.join(break_line(result_type))
70
87
  else
71
88
  obj.decoded
72
89
  end
@@ -101,10 +118,14 @@ module Mail #:nodoc:
101
118
 
102
119
  def is_crypted?
103
120
  (header['Content-Type'].encoded =~ /application\/(x-)?pkcs[0-9]+-mime/).present?
121
+ rescue
122
+ false
104
123
  end
105
124
 
106
125
  def is_signed?
107
126
  (header['Content-Type'].encoded =~ /application\/(x-)?pkcs[0-9]+-signature/).present?
127
+ rescue
128
+ false
108
129
  end
109
130
  end
110
131
  end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Test additional functions' do
4
+ describe 'Correct' do
5
+ describe 'Certificate generation' do
6
+ it 'Must generate p12' do
7
+ p12_bytes = SecurityObject.p12_certificate({:password => 'demo'})
8
+ lambda { OpenSSL::PKCS12.new p12_bytes, 'demo' }.should_not raise_error
9
+ end
10
+ end
11
+ end
12
+
13
+ describe 'Incorrect' do
14
+ it 'Must dont open p12 with wrong pass' do
15
+ p12_bytes = SecurityObject.p12_certificate({:password => 'demo'})
16
+ lambda { OpenSSL::PKCS12.new p12_bytes, 'wrong' }.should raise_error
17
+ end
18
+ end
19
+ end
@@ -16,7 +16,7 @@ describe 'Test basic functions' do
16
16
  end
17
17
 
18
18
  it 'Verification check' do
19
- verified = @mail.proceed(Notifier.x509_configuration)
19
+ verified = @mail.proceed('plain', Notifier.x509_configuration)
20
20
  verified.should eql @raw_mail.body.to_s
21
21
  end
22
22
  end
@@ -35,7 +35,7 @@ describe 'Test basic functions' do
35
35
  end
36
36
 
37
37
  it 'Must generate crypted text' do
38
- decrypted = @mail.proceed(Notifier.x509_configuration)
38
+ decrypted = @mail.proceed('plain', Notifier.x509_configuration)
39
39
  decrypted.to_s.should eql @raw_mail.body.decoded
40
40
  end
41
41
  end
@@ -47,7 +47,7 @@ describe 'Test basic functions' do
47
47
  add_config
48
48
  mail = Notifier.fufu('<destination@foobar.com>', '<demo@foobar.com>')
49
49
 
50
- decrypted = mail.proceed(Notifier.x509_configuration)
50
+ decrypted = mail.proceed('plain', Notifier.x509_configuration)
51
51
  decrypted.to_s.should eql raw_mail.body.decoded
52
52
  end
53
53
 
@@ -58,7 +58,7 @@ describe 'Test basic functions' do
58
58
  add_config(true, true, true)
59
59
  mail = Notifier.fufu('<destination@foobar.com>', '<demo@foobar.com>')
60
60
 
61
- decrypted = mail.proceed(Notifier.x509_configuration)
61
+ decrypted = mail.proceed('plain', Notifier.x509_configuration)
62
62
  decrypted.to_s.should eql raw_mail.body.decoded
63
63
  end
64
64
  end
@@ -71,7 +71,7 @@ describe 'Test basic functions' do
71
71
  mail.body.to_s.should_not be_empty
72
72
 
73
73
  set_config_param(sign_passphrase: 'wrong')
74
- -> { mail.proceed(Notifier.x509_configuration) }.should raise_error OpenSSL::PKey::RSAError
74
+ -> { mail.proceed('plain', Notifier.x509_configuration) }.should raise_error OpenSSL::PKey::RSAError
75
75
  end
76
76
 
77
77
  describe 'incorrect text' do
@@ -79,14 +79,14 @@ describe 'Test basic functions' do
79
79
  add_config(true, false)
80
80
  mail = Notifier.fufu('<destination@foobar.com>', '<demo@foobar.com>')
81
81
  mail.body = mail.body.to_s.gsub(/[0-9]/, 'g')
82
- -> { mail.proceed(Notifier.x509_configuration) }.should raise_error VerificationError
82
+ -> { mail.proceed('plain', Notifier.x509_configuration) }.should raise_error VerificationError
83
83
  end
84
84
 
85
85
  it 'crypt' do
86
86
  add_config(false, true)
87
87
  mail = Notifier.fufu('<destination@foobar.com>', '<demo@foobar.com>')
88
88
  mail.body = mail.body.to_s.gsub(/[0-9]/, 'g')
89
- -> { mail.proceed(Notifier.x509_configuration) }.should raise_error DecodeError
89
+ -> { mail.proceed('plain', Notifier.x509_configuration) }.should raise_error DecodeError
90
90
  end
91
91
  end
92
92
 
@@ -97,7 +97,7 @@ describe 'Test basic functions' do
97
97
  crypt_key: 'cert.key')
98
98
  mail = Notifier.fufu('<destination@foobar.com>', '<demo@foobar.com>')
99
99
  mail.body = mail.body.to_s.gsub(/[0-9]/, 'g')
100
- -> { mail.proceed(Notifier.x509_configuration) }.should raise_error VerificationError
100
+ -> { mail.proceed('plain', Notifier.x509_configuration) }.should raise_error VerificationError
101
101
  end
102
102
 
103
103
  it 'crypt' do
@@ -4,6 +4,8 @@ require 'active_support/all'
4
4
  require 'action_mailer'
5
5
  require 'action_mailer_x509'
6
6
  require 'action_mailer_x509/x509'
7
+ require 'action_mailer_x509/security_object'
8
+ require 'action_mailer_x509/configuration'
7
9
  require 'models/notifier'
8
10
 
9
11
  RSpec.configure do |config|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_mailer_x509
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-28 00:00:00.000000000 Z
12
+ date: 2013-09-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -82,6 +82,7 @@ files:
82
82
  - lib/action_mailer_x509.rb
83
83
  - lib/action_mailer_x509/configuration.rb
84
84
  - lib/action_mailer_x509/railtie.rb
85
+ - lib/action_mailer_x509/security_object.rb
85
86
  - lib/action_mailer_x509/x509.rb
86
87
  - lib/generators/action_mailer_x509/install_generator.rb
87
88
  - lib/generators/action_mailer_x509/templates/x509_settings.rb
@@ -90,6 +91,7 @@ files:
90
91
  - lib/overrides/mail/message.rb
91
92
  - lib/tasks/action_mailer_x509.rake
92
93
  - lib/views/notifier/fufu.erb
94
+ - spec/adds_spec.rb
93
95
  - spec/ops_spec.rb
94
96
  - spec/spec_helper.rb
95
97
  homepage: http://github.com/jeyboy/action_mailer_x509
@@ -105,12 +107,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
107
  - - ! '>='
106
108
  - !ruby/object:Gem::Version
107
109
  version: '0'
110
+ segments:
111
+ - 0
112
+ hash: -205818147
108
113
  required_rubygems_version: !ruby/object:Gem::Requirement
109
114
  none: false
110
115
  requirements:
111
116
  - - ! '>='
112
117
  - !ruby/object:Gem::Version
113
118
  version: '0'
119
+ segments:
120
+ - 0
121
+ hash: -205818147
114
122
  requirements: []
115
123
  rubyforge_project:
116
124
  rubygems_version: 1.8.24