pkcs7-cryptographer 0.2.2 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf8d46e30794182b24393965b11655facc02bd6597a0856e59516b5b185f2ea1
4
- data.tar.gz: 0564d998860e51680e422ae8c87768145458a44ae891e6683754436db7fcb38f
3
+ metadata.gz: d8e03b7542f2787153a2243935c5a0c748abae6fec0e066ce08ee158f21cf75f
4
+ data.tar.gz: 25baf89c080fb55f909e3d92d22e1ec05810e2ed91d00d47e83e0f6460c75f32
5
5
  SHA512:
6
- metadata.gz: 1cce85246daad30252c7db7fa69a48c430069ebb06f7a4c27a410363f64d0182abcd46fd256960e6b13e01642ea581a5d1688d2e4ccd52635810c12e7628520d
7
- data.tar.gz: 3a6c09eeb65e4a7fa3d406cc98d9673ccbc253dace154a2ef90ae612bc6c3129f29fe12f03cac7a643887efcd475557229c56bdf8cf93261de6edfd1e78fa4f7
6
+ metadata.gz: 527dc589baa95742d7b81d825373efac10f136791f5d97e4970565a3e01e5e5f2c004731d13b59150174858865c295b93ffb8e8210c9ebb714eb0d9840254935
7
+ data.tar.gz: 4e5595dca5adeb1daa3b58a00aeaeb463011c6e86e2a2036a1ba4afeba1268d141d8cf04212be08abdd287636aa6838a1bde49ad0ddd17ea6e647a1718b30895
data/.rubocop.yml CHANGED
@@ -22,4 +22,7 @@ RSpec/MultipleMemoizedHelpers:
22
22
  Enabled: false
23
23
 
24
24
  RSpec/NestedGroups:
25
- Max: 4
25
+ Max: 6
26
+
27
+ RSpec/ExampleLength:
28
+ Max: 10
data/Gemfile.lock CHANGED
@@ -1,15 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pkcs7-cryptographer (0.2.2)
4
+ pkcs7-cryptographer (1.1.1)
5
+ activesupport (>= 6.1.4.1)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ activesupport (6.1.4.1)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 1.6, < 2)
13
+ minitest (>= 5.1)
14
+ tzinfo (~> 2.0)
15
+ zeitwerk (~> 2.3)
9
16
  ast (2.4.2)
10
17
  coderay (1.1.3)
18
+ concurrent-ruby (1.1.9)
11
19
  diff-lcs (1.4.4)
20
+ i18n (1.8.10)
21
+ concurrent-ruby (~> 1.0)
12
22
  method_source (1.0.0)
23
+ minitest (5.14.4)
13
24
  parallel (1.20.1)
14
25
  parser (3.0.0.0)
15
26
  ast (~> 2.4.1)
@@ -50,7 +61,11 @@ GEM
50
61
  rubocop (~> 1.0)
51
62
  rubocop-ast (>= 1.1.0)
52
63
  ruby-progressbar (1.11.0)
64
+ timecop (0.9.4)
65
+ tzinfo (2.0.4)
66
+ concurrent-ruby (~> 1.0)
53
67
  unicode-display_width (2.0.0)
68
+ zeitwerk (2.4.2)
54
69
 
55
70
  PLATFORMS
56
71
  x86_64-darwin-19
@@ -64,6 +79,7 @@ DEPENDENCIES
64
79
  rubocop (= 1.12.0)
65
80
  rubocop-rake (= 0.5.1)
66
81
  rubocop-rspec (= 2.2.0)
82
+ timecop (= 0.9.4)
67
83
 
68
84
  BUNDLED WITH
69
85
  2.2.3
data/README.md CHANGED
@@ -4,13 +4,12 @@
4
4
  ![main workflow](https://github.com/dmuneras/pkcs7-cryptographer/actions/workflows/main.yml/badge.svg)
5
5
 
6
6
 
7
-
8
- Cryptographer is an small utility to encrypt and decrypt messages
7
+ Cryptographer is an small utility to encrypt, sign and decrypt messages
9
8
  using PKCS7.
10
9
 
11
10
  PKCS7 is used to store signed and encrypted data.This specific implementation
12
- uses aes-256-cbc as chipher in the encryption process. If you want to read more
13
- information about the involved data structures and theory around this,
11
+ uses `aes-256-cbc` as chipher in the encryption process. If you want to read
12
+ more information about the involved data structures and theory around this,
14
13
  please visit:
15
14
 
16
15
  - https://ruby-doc.org/stdlib-3.0.0/libdoc/openssl/rdoc/OpenSSL.html
@@ -37,6 +36,8 @@ Or install it yourself as:
37
36
  ```
38
37
  ## Usage
39
38
 
39
+ ### Using bare PKCS7::Cryptographer
40
+
40
41
  After installing the gem you will have the `PKCS7::Cryptographer` available.
41
42
 
42
43
  `PKCS7::Cryptographer` is a class that provides two public methods:
@@ -44,12 +45,13 @@ After installing the gem you will have the `PKCS7::Cryptographer` available.
44
45
  - `sign_and_encrypt`
45
46
  - `decrypt_and_verify`
46
47
 
47
- Read the following examples to get a better undertanding:
48
+ If you want to use the barebones cryptographer, you can. Please look at the
49
+ following example:
48
50
 
49
51
 
50
- ### Using bare PKCS7::Cryptographer
51
52
 
52
53
  ```ruby
54
+ require 'pkcs7/cryptographer'
53
55
 
54
56
  # This script assumes you have a read_file method to read the certificates and
55
57
  # keys.
@@ -71,7 +73,7 @@ Read the following examples to get a better undertanding:
71
73
  # Only the client can read the message since the required public
72
74
  # certificate to read it is the client certificate.
73
75
 
74
- # It could be read if the CA_STORE of the reader has certificate of the
76
+ # It could be read if the CA_STORE of the reader has the certificate of the
75
77
  # CA that signed the client certificate as trusted.
76
78
 
77
79
  cryptographer = PKCS7::Cryptographer.new
@@ -84,6 +86,8 @@ Read the following examples to get a better undertanding:
84
86
  public_certificate: CLIENT_CERTIFICATE
85
87
  )
86
88
 
89
+ # encrypted_data is a PEM formatted string
90
+
87
91
  # READ MESSAGE IN CLIENT
88
92
  # ----------------------------------------------------------------------------
89
93
  # Store of trusted certificates
@@ -103,8 +107,16 @@ Read the following examples to get a better undertanding:
103
107
 
104
108
  ### Using PKCS7::Cryptographer::Entity
105
109
 
110
+ There is a possibility to use entities to communicate using encrypted data. In
111
+ order to use it you have to import the entities implementation.
112
+
113
+ Please look at the following example:
114
+
106
115
  ```ruby
107
116
 
117
+ require 'pkcs7/cryptographer'
118
+ require 'pkcs7/cryptographer/entity'
119
+
108
120
  # This script assumes you have a read_file method to read the certificates and
109
121
  # keys. If you have any question about how to generate the keys/certificates
110
122
  # check this post: https://mariadb.com/kb/en/certificate-creation-with-openssl/
@@ -129,24 +141,62 @@ Read the following examples to get a better undertanding:
129
141
  )
130
142
 
131
143
  client_entity = PKCS7::Cryptographer::Entity.new(
132
- certificate: CLIENT_CERTIFICATE,
144
+ certificate: CLIENT_CERTIFICATE
133
145
  )
134
146
 
135
147
  # SEND MESSAGE TO THE CLIENT
136
148
  # ----------------------------------------------------------------------------
137
149
  data = "Victor Ibarbo"
138
- encrypted_data = ca_entity.encrypt_data(data: data, to: client_entity)
150
+ encrypted_data = ca_entity.encrypt_data(data: data, receiver: client_entity)
139
151
 
140
152
  # READ MESSAGE IN CLIENT
141
153
  # ----------------------------------------------------------------------------
142
154
  decrypted_data = client_entity.decrypt_data(
143
155
  data: encrypted_data,
144
- from: ca_entity
156
+ sender: ca_entity
145
157
  )
146
158
 
147
159
  # decrypted_data returns: "Victor Ibarbo"
148
160
  ```
149
161
 
162
+ When using entities, all the complexity of knowing which PKI credentials to
163
+ send to the cryptographer dissapears. You only need to initialize the
164
+ entities and use the methods to indicate to whom the message will be sent.
165
+
166
+ If you want to verify if certain entity you defined "trust" another one, use the
167
+ `trustable_entity?(<the other entity>)`.
168
+
169
+ ```ruby
170
+ ca_entity = PKCS7::Cryptographer::Entity.new(
171
+ key: CA_KEY,
172
+ certificate: CA_CERTIFICATE,
173
+ ca_store: CA_STORE
174
+ )
175
+
176
+ client_entity = PKCS7::Cryptographer::Entity.new(
177
+ certificate: CLIENT_CERTIFICATE
178
+ )
179
+
180
+ ca_entity.trustable_entity?(client_entity)
181
+
182
+ # Returns true because the client certificate was signed by the root
183
+ # certificate of the ca_authority.
184
+ ```
185
+
186
+ When sending data to an entity, you will most of the time initialize the entity
187
+ only with the `certificate` keyword arguments. So, initializing a receiver will
188
+ most of the time looks like this:
189
+
190
+ ```ruby
191
+ client_entity = PKCS7::Cryptographer::Entity.new(
192
+ certificate: CLIENT_CERTIFICATE
193
+ )
194
+ ```
195
+
196
+ The entity above can't encrypt messages or decrypt them, if you want to decrypt
197
+ and encrypt the entity should have its the key (private key), certificate and
198
+ the list of trusted certificates of the entity (ca_store).
199
+
150
200
  ## Development
151
201
 
152
202
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
@@ -32,24 +32,24 @@ module PKCS7
32
32
  @ca_store.verify(entity.certificate)
33
33
  end
34
34
 
35
- def encrypt_data(data:, to:)
36
- perform_safely(to) do
35
+ def encrypt_data(data:, receiver:)
36
+ perform_safely(receiver) do
37
37
  @cryptographer.sign_and_encrypt(
38
38
  data: data,
39
39
  key: @key,
40
40
  certificate: @certificate,
41
- public_certificate: to.certificate
41
+ public_certificate: receiver.certificate
42
42
  )
43
43
  end
44
44
  end
45
45
 
46
- def decrypt_data(data:, from:)
47
- perform_safely(from) do
46
+ def decrypt_data(data:, sender:)
47
+ perform_safely(sender) do
48
48
  @cryptographer.decrypt_and_verify(
49
49
  data: data,
50
50
  key: @key,
51
51
  certificate: @certificate,
52
- public_certificate: from.certificate,
52
+ public_certificate: sender.certificate,
53
53
  ca_store: @ca_store
54
54
  )
55
55
  end
@@ -61,7 +61,8 @@ module PKCS7
61
61
 
62
62
  def perform_safely(entity)
63
63
  return false unless trustable_entity?(entity)
64
- return false unless @key.present?
64
+ return false unless @key
65
+
65
66
  yield
66
67
  end
67
68
  end
@@ -8,6 +8,11 @@ module PKCS7
8
8
  # certificate, key or encrypted message string.
9
9
  ###
10
10
  module Initializers
11
+ # PRIVATE METHODS
12
+ # ------------------------------------------------------------------------
13
+
14
+ private
15
+
11
16
  def x509_certificate(certificate)
12
17
  wrap_in_class_or_return(certificate, OpenSSL::X509::Certificate)
13
18
  end
@@ -16,6 +21,10 @@ module PKCS7
16
21
  wrap_in_class_or_return(key, OpenSSL::PKey::RSA)
17
22
  end
18
23
 
24
+ def certificate_signing_request(request)
25
+ wrap_in_class_or_return(request, OpenSSL::X509::Request)
26
+ end
27
+
19
28
  def pkcs7(pkcs7)
20
29
  wrap_in_class_or_return(pkcs7, OpenSSL::PKCS7)
21
30
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module PKCS7
4
4
  class Cryptographer
5
- VERSION = "0.2.2"
5
+ VERSION = "1.1.1"
6
6
  end
7
7
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openssl"
4
+ require "active_support/all"
4
5
  require_relative "cryptographer/version"
5
6
  require_relative "cryptographer/initializers"
6
7
 
@@ -17,6 +18,10 @@ module PKCS7
17
18
  class Cryptographer
18
19
  include PKCS7::Cryptographer::Initializers
19
20
 
21
+ # CONSTANS
22
+ # --------------------------------------------------------------------------
23
+ CYPHER_ALGORITHM = "aes-256-cbc"
24
+
20
25
  # PUBLIC METHODS
21
26
  # --------------------------------------------------------------------------
22
27
 
@@ -39,12 +44,7 @@ module PKCS7
39
44
  certificate = x509_certificate(certificate)
40
45
  public_certificate = x509_certificate(public_certificate)
41
46
  signed_data = OpenSSL::PKCS7.sign(certificate, key, data)
42
-
43
- encrypted_data = OpenSSL::PKCS7.encrypt(
44
- [public_certificate],
45
- signed_data.to_pem,
46
- OpenSSL::Cipher.new("aes-256-cbc")
47
- )
47
+ encrypted_data = encrypt(public_certificate, signed_data)
48
48
 
49
49
  encrypted_data.to_pem
50
50
  end
@@ -72,11 +72,85 @@ module PKCS7
72
72
  public_certificate = x509_certificate(public_certificate)
73
73
  encrypted_data = pkcs7(data)
74
74
  decrypted_data = encrypted_data.decrypt(key, certificate)
75
+
75
76
  signed_data = OpenSSL::PKCS7.new(decrypted_data)
77
+ verified = verified_signature?(signed_data, public_certificate, ca_store)
76
78
 
77
- return false unless signed_data.verify([public_certificate], ca_store)
79
+ return false unless verified
78
80
 
79
81
  signed_data.data
80
82
  end
83
+
84
+ def sign_certificate(
85
+ csr:,
86
+ key:,
87
+ certificate:,
88
+ valid_until: Time.current + 10.years
89
+ )
90
+ valid_until.to_time.utc
91
+ check_csr(csr)
92
+
93
+ sign_csr(csr, key, certificate, valid_until)
94
+ end
95
+
96
+ private
97
+
98
+ def encrypt(
99
+ public_certificate,
100
+ signed_data,
101
+ cypher_algorithm = CYPHER_ALGORITHM
102
+ )
103
+ OpenSSL::PKCS7.encrypt(
104
+ [public_certificate],
105
+ signed_data.to_der,
106
+ OpenSSL::Cipher.new(cypher_algorithm),
107
+ OpenSSL::PKCS7::BINARY
108
+ )
109
+ end
110
+
111
+ def verified_signature?(signed_data, public_certificate, ca_store)
112
+ signed_data.verify(
113
+ [public_certificate],
114
+ ca_store,
115
+ nil,
116
+ OpenSSL::PKCS7::NOINTERN | OpenSSL::PKCS7::NOCHAIN
117
+ )
118
+ end
119
+
120
+ def check_csr(signing_request)
121
+ csr = OpenSSL::X509::Request.new signing_request
122
+ raise "CSR can not be verified" unless csr.verify(csr.public_key)
123
+ end
124
+
125
+ def sign_csr(request, key, issuer_certificate, valid_until)
126
+ request = certificate_signing_request(request)
127
+ key = rsa_key(key)
128
+ issuer_certificate = x509_certificate(issuer_certificate)
129
+
130
+ csr_cert = build_certificate_from_csr(
131
+ request,
132
+ issuer_certificate,
133
+ valid_until
134
+ )
135
+ csr_cert.sign(key, OpenSSL::Digest.new("SHA1")) # TODO: review this one
136
+ x509_certificate(csr_cert.to_pem)
137
+ end
138
+
139
+ def build_certificate_from_csr(
140
+ signing_request,
141
+ issuer_certificate,
142
+ valid_until
143
+ )
144
+ certificate = OpenSSL::X509::Certificate.new
145
+ certificate.serial = Time.now.to_i
146
+ certificate.version = 2 # TODO: Check what to put here
147
+ certificate.not_before = Time.current
148
+ certificate.not_after = valid_until
149
+ certificate.subject = signing_request.subject
150
+ certificate.public_key = signing_request.public_key
151
+ certificate.issuer = issuer_certificate.subject
152
+
153
+ certificate
154
+ end
81
155
  end
82
156
  end
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  "Utility to encrypt and decrypt messages using OpenSSL::PKCS7"
14
14
  spec.homepage = "https://github.com/dmuneras/pkcs7-cryptographer"
15
15
  spec.license = "MIT"
16
- spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
17
17
 
18
18
  spec.metadata["homepage_uri"] = spec.homepage
19
19
  spec.metadata["source_code_uri"] = "https://github.com/dmuneras/pkcs7-cryptographer"
@@ -27,6 +27,8 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
29
 
30
+ spec.add_dependency "activesupport", ">= 6.1.4.1"
31
+
30
32
  spec.add_development_dependency "bundler", ">= 2"
31
33
  spec.add_development_dependency "pry"
32
34
  spec.add_development_dependency "rake", "~> 13.0"
@@ -34,4 +36,5 @@ Gem::Specification.new do |spec|
34
36
  spec.add_development_dependency "rubocop", "1.12.0"
35
37
  spec.add_development_dependency "rubocop-rake", "0.5.1"
36
38
  spec.add_development_dependency "rubocop-rspec", "2.2.0"
39
+ spec.add_development_dependency "timecop", "0.9.4"
37
40
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pkcs7-cryptographer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Munera Sanchez
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-25 00:00:00.000000000 Z
11
+ date: 2021-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 6.1.4.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 6.1.4.1
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +122,20 @@ dependencies:
108
122
  - - '='
109
123
  - !ruby/object:Gem::Version
110
124
  version: 2.2.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: timecop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '='
130
+ - !ruby/object:Gem::Version
131
+ version: 0.9.4
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '='
137
+ - !ruby/object:Gem::Version
138
+ version: 0.9.4
111
139
  description: Utility to encrypt and decrypt messages using OpenSSL::PKCS7
112
140
  email:
113
141
  - dmunera119@gmail.com
@@ -146,7 +174,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
146
174
  requirements:
147
175
  - - ">="
148
176
  - !ruby/object:Gem::Version
149
- version: 2.4.0
177
+ version: 2.5.0
150
178
  required_rubygems_version: !ruby/object:Gem::Requirement
151
179
  requirements:
152
180
  - - ">="