pkcs7-cryptographer 0.2.2 → 1.1.1

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.
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
  - - ">="