pki_express 1.0.0

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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +3 -0
  3. data/.gitignore +28 -0
  4. data/CHANGELOG.md +2 -0
  5. data/Gemfile +4 -0
  6. data/README.md +24 -0
  7. data/Rakefile +1 -0
  8. data/lib/pki_express.rb +48 -0
  9. data/lib/pki_express/auth_complete_result.rb +22 -0
  10. data/lib/pki_express/auth_start_result.rb +77 -0
  11. data/lib/pki_express/authentication.rb +285 -0
  12. data/lib/pki_express/base_signer.rb +55 -0
  13. data/lib/pki_express/cades_signature_starter.rb +242 -0
  14. data/lib/pki_express/command_error.rb +14 -0
  15. data/lib/pki_express/commands.rb +21 -0
  16. data/lib/pki_express/enum.rb +9 -0
  17. data/lib/pki_express/error_codes.rb +46 -0
  18. data/lib/pki_express/installation_not_found_error.rb +8 -0
  19. data/lib/pki_express/name.rb +48 -0
  20. data/lib/pki_express/pades_horizontal_align.rb +9 -0
  21. data/lib/pki_express/pades_measurement_units.rb +8 -0
  22. data/lib/pki_express/pades_page_optimization.rb +51 -0
  23. data/lib/pki_express/pades_page_orientation.rb +9 -0
  24. data/lib/pki_express/pades_paper_size.rb +21 -0
  25. data/lib/pki_express/pades_signature_starter.rb +232 -0
  26. data/lib/pki_express/pades_size.rb +17 -0
  27. data/lib/pki_express/pades_text_horizontal_align.rb +8 -0
  28. data/lib/pki_express/pades_vertical_align.rb +9 -0
  29. data/lib/pki_express/pades_visual_auto_positioning.rb +22 -0
  30. data/lib/pki_express/pades_visual_image.rb +52 -0
  31. data/lib/pki_express/pades_visual_manual_positioning.rb +17 -0
  32. data/lib/pki_express/pades_visual_positioning.rb +28 -0
  33. data/lib/pki_express/pades_visual_rectangle.rb +74 -0
  34. data/lib/pki_express/pades_visual_representation.rb +22 -0
  35. data/lib/pki_express/pades_visual_text.rb +35 -0
  36. data/lib/pki_express/pk_certificate.rb +62 -0
  37. data/lib/pki_express/pki_brazil_certificate_fields.rb +58 -0
  38. data/lib/pki_express/pki_brazil_certificate_types.rb +19 -0
  39. data/lib/pki_express/pki_express_config.rb +26 -0
  40. data/lib/pki_express/pki_express_operator.rb +216 -0
  41. data/lib/pki_express/pki_italy_certificate_fields.rb +16 -0
  42. data/lib/pki_express/pki_italy_certificate_types.rb +11 -0
  43. data/lib/pki_express/signature_finisher.rb +298 -0
  44. data/lib/pki_express/signature_start_result.rb +13 -0
  45. data/lib/pki_express/signature_starter.rb +115 -0
  46. data/lib/pki_express/signer.rb +106 -0
  47. data/lib/pki_express/standard_signature_policies.rb +36 -0
  48. data/lib/pki_express/timestamp_authority.rb +51 -0
  49. data/lib/pki_express/validation_error.rb +8 -0
  50. data/lib/pki_express/validation_item.rb +43 -0
  51. data/lib/pki_express/validation_results.rb +121 -0
  52. data/lib/pki_express/version.rb +3 -0
  53. data/lib/pki_express/version_manager.rb +21 -0
  54. data/pki_express.gemspec +27 -0
  55. metadata +129 -0
@@ -0,0 +1,17 @@
1
+ module PkiExpress
2
+ class PadesVisualManualPositioning < PadesVisualPositioning
3
+ attr_accessor :signature_rectangle
4
+
5
+ def initialize(page_number=nil, measurement_units=nil, signature_rectangle=nil)
6
+ super(page_number, measurement_units)
7
+ @signature_rectangle = signature_rectangle
8
+ end
9
+
10
+ def to_model
11
+ model = super
12
+ model['manual'] = @signature_rectangle&.to_model
13
+ model
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,28 @@
1
+ module PkiExpress
2
+ class PadesVisualPositioning
3
+ attr_reader :measurement_units
4
+ attr_accessor :page_number, :page_optimization
5
+
6
+ def initialize(page_number=nil, measurement_units=nil)
7
+ @page_number = page_number
8
+ @measurement_units = measurement_units
9
+ @page_optimization = nil
10
+ end
11
+
12
+ def measurement_units=(value)
13
+ unless PadesMeasurementUnits.contains?(value)
14
+ raise 'The provided "measurement_units" is not valid. Try using PadesMeasurementUnits constants'
15
+ end
16
+
17
+ @measurement_units = value
18
+ end
19
+
20
+ def to_model
21
+ {
22
+ 'pageNumber': @page_number,
23
+ 'measurementUnits': @measurement_units,
24
+ 'pageOptimization': @page_optimization&.to_model
25
+ }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,74 @@
1
+ module PkiExpress
2
+ class PadesVisualRectangle
3
+ attr_accessor :left, :top, :right, :bottom, :width, :height
4
+
5
+ def initialize
6
+ @left = nil
7
+ @top = nil
8
+ @right = nil
9
+ @bottom = nil
10
+ @width = nil
11
+ @height = nil
12
+ end
13
+
14
+ def set_width_centered(width)
15
+ @width = width
16
+ @left = nil
17
+ @right = nil
18
+ end
19
+
20
+ def set_width_left_anchored(width, left)
21
+ @width = width
22
+ @left = left
23
+ @right = nil
24
+ end
25
+
26
+ def set_width_right_anchored(width, right)
27
+ @width = width
28
+ @left = nil
29
+ @right = right
30
+ end
31
+
32
+ def set_horizontal_stretch(left, right)
33
+ @width = nil
34
+ @left = left
35
+ @right = right
36
+ end
37
+
38
+ def set_height_centered(height)
39
+ @height = height
40
+ @top = nil
41
+ @bottom = nil
42
+ end
43
+
44
+ def set_height_top_anchored(height, top)
45
+ @height = height
46
+ @top = top
47
+ @bottom = nil
48
+ end
49
+
50
+ def set_height_bottom_anchored(height, bottom)
51
+ @height = height
52
+ @top = nil
53
+ @bottom = bottom
54
+ end
55
+
56
+ def set_vertical_stretch(top, bottom)
57
+ @height = nil
58
+ @top = top
59
+ @bottom = bottom
60
+ end
61
+
62
+ def to_model
63
+ {
64
+ left: @left,
65
+ top: @top,
66
+ right: @right,
67
+ bottom: @bottom,
68
+ width: @width,
69
+ height: @height,
70
+ }
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,22 @@
1
+ module PkiExpress
2
+ class PadesVisualRepresentation
3
+ attr_accessor :text, :image, :position
4
+
5
+ def initialize(text=nil, image=nil, position=nil)
6
+ @text = text
7
+ @image = image
8
+ @position = position
9
+ end
10
+
11
+ def to_model
12
+ unless @position
13
+ raise new 'The visual representation position was not set'
14
+ end
15
+ {
16
+ position: @position&.to_model,
17
+ text: @text&.to_model,
18
+ image: @image&.to_model,
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ module PkiExpress
2
+ class PadesVisualText
3
+ attr_reader :horizontal_align
4
+ attr_accessor :text, :include_signing_time, :font_size,
5
+ :container, :signing_time_format
6
+
7
+ def initialize(text=nil, include_signing_time=nil, font_size=nil)
8
+ @horizontal_align = :left
9
+ @text = text
10
+ @include_signing_time = include_signing_time
11
+ @font_size = font_size
12
+ @container = nil
13
+ @signing_time_format = nil
14
+ end
15
+
16
+ def horizontal_align=(value)
17
+ unless PadesTextHorizontalAlign.contains?(value)
18
+ raise 'The provided "horizontal_align" is not valid. Try using PadesTextHorizontalAlign constants'
19
+ end
20
+
21
+ @horizontal_align = value
22
+ end
23
+
24
+ def to_model
25
+ {
26
+ 'fontSize': @font_size,
27
+ 'text': @text,
28
+ 'includeSigningTime': @include_signing_time,
29
+ 'signingTimeFormat': @signing_time_format,
30
+ 'container': @container&.to_model,
31
+ 'horizontalAlign': @horizontal_align
32
+ }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,62 @@
1
+ module PkiExpress
2
+
3
+ class PKCertificate
4
+
5
+ attr_accessor :subject_name, :email_address, :issuer_name, :serial_number,
6
+ :validity_start, :validity_end, :pki_brazil, :pki_italy,
7
+ :issuer, :binary_thumbprint_sha256, :thumbprint
8
+
9
+ def initialize(model)
10
+ @subject_name = nil
11
+ @email_address = nil
12
+ @issuer_name = nil
13
+ @serial_number = nil
14
+ @validity_start = nil
15
+ @validity_end = nil
16
+ @pki_brazil = nil
17
+ @pki_italy = nil
18
+ @issuer = nil
19
+ @binary_thumbprint_sha256 = nil
20
+ @thumbprint = nil
21
+
22
+ unless model.nil?
23
+ @email_address = model.fetch(:emailAddress)
24
+ @serial_number = model.fetch(:serialNumber)
25
+ @validity_start = model.fetch(:validityStart)
26
+ @validity_end = model.fetch(:validityEnd)
27
+ @thumbprint = model.fetch(:thumbprint)
28
+
29
+ subject_name = model.fetch(:subjectName)
30
+ if subject_name
31
+ @subject_name = Name.new(subject_name)
32
+ end
33
+
34
+ issuer_name = model.fetch(:issuerName)
35
+ if issuer_name
36
+ @issuer_name = Name.new(issuer_name)
37
+ end
38
+
39
+ pki_brazil = model.fetch(:pkiBrazil)
40
+ if pki_brazil
41
+ @pki_brazil = PkiBrazilCertificateFields.new(pki_brazil)
42
+ end
43
+
44
+ pki_italy = model.fetch(:pkiItaly)
45
+ if pki_italy
46
+ @pki_italy = PkiItalyCertificateFields.new(pki_italy)
47
+ end
48
+
49
+ issuer = model.fetch(:issuer)
50
+ if issuer
51
+ @issuer = PKCertificate.new(issuer)
52
+ end
53
+
54
+ binary_thumbprint_sha256 = model.fetch(:binaryThumbprintSHA256)
55
+ if binary_thumbprint_sha256
56
+ @binary_thumbprint_sha256 = Base64.decode64(binary_thumbprint_sha256)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,58 @@
1
+ module PkiExpress
2
+
3
+ class PkiBrazilCertificateFields
4
+
5
+ attr_accessor :rg_emissor_uf, :cnpj, :rg_numero, :oab_numero, :company_name,
6
+ :rg_emissor, :certificate_type, :cpf, :responsavel,
7
+ :date_of_birth, :oab_uf
8
+
9
+ def initialize(model)
10
+ @certificate_type = nil
11
+ @cpf = nil
12
+ @cnpj = nil
13
+ @responsavel = nil
14
+ @date_of_birth = nil
15
+ @company_name = nil
16
+ @rg_numero = nil
17
+ @rg_emissor = nil
18
+ @rg_emissor_uf = nil
19
+ @oab_numero = nil
20
+ @oab_uf = nil
21
+
22
+ unless model.nil?
23
+ @certificate_type = model.fetch(:certificateType)
24
+ @cpf = model.fetch(:cpf)
25
+ @cnpj = model.fetch(:cnpj)
26
+ @responsavel = model.fetch(:responsavel)
27
+ @date_of_birth = model.fetch(:dateOfBirth)
28
+ @company_name = model.fetch(:companyName)
29
+ @rg_numero = model.fetch(:rgNumero)
30
+ @rg_emissor = model.fetch(:rgEmissor)
31
+ @rg_emissor_uf = model.fetch(:rgEmissorUF)
32
+ @oab_numero = model.fetch(:oabNumero)
33
+ @oab_uf = model.fetch(:oabUF)
34
+ end
35
+ end
36
+
37
+ def cpf_formatted
38
+ unless @cpf
39
+ return nil
40
+ end
41
+ unless /^\d{11}$/.match(@cpf)
42
+ return @cpf
43
+ end
44
+ "#{@cpf[0..2]}.#{@cpf[3..5]}.#{@cpf[6..8]}-#{@cpf[9..-1]}"
45
+ end
46
+
47
+ def cnpj_formatted
48
+ unless @cnpj
49
+ return nil
50
+ end
51
+ unless /^\d{14}$/.match(@cnpj)
52
+ return @cnpj
53
+ end
54
+ "#{@cnpj[0..1]}.#{@cnpj[2..4]}.#{@cnpj[5..7]}/#{@cnpj[8..11]}-#{@cnpj[12..-1]}"
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,19 @@
1
+ module PkiExpress
2
+ class PkiBrazilCertificateTypes < Enum
3
+ UNKNOWN = 'Unknown'
4
+ A1 = 'A1'
5
+ A2 = 'A2'
6
+ A3 = 'A3'
7
+ A4 = 'A4'
8
+ S1 = 'S1'
9
+ S2 = 'S2'
10
+ S3 = 'S3'
11
+ S4 = 'S4'
12
+ T3 = 'T3'
13
+ T4 = 'T4'
14
+
15
+ VALUES = [
16
+ UNKNOWN, A1, A2, A3, A4, S1, S2, S3, S4, T3, T4
17
+ ]
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ module PkiExpress
2
+
3
+ class PkiExpressConfig
4
+ attr_accessor :pki_express_home, :temp_folder, :transfer_data_folder
5
+ @@single_temp_folder = nil
6
+
7
+ def initialize(pki_express_home = nil, temp_folder = nil, transfer_data_folder = nil)
8
+ if not temp_folder.nil? and Pathname.exist?(temp_folder)
9
+ @temp_folder = temp_folder
10
+ else
11
+ if @@single_temp_folder.nil?
12
+ @@single_temp_folder = Dir.mktmpdir('pkie')
13
+ end
14
+ @temp_folder = @@single_temp_folder
15
+ end
16
+
17
+ if not transfer_data_folder.nil? and Pathname.exist?(transfer_data_folder)
18
+ @transfer_data_folder = transfer_data_folder
19
+ else
20
+ @transfer_data_folder = @temp_folder
21
+ end
22
+
23
+ @pki_express_home = pki_express_home
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,216 @@
1
+ require 'shellwords'
2
+ require 'open3'
3
+
4
+ module PkiExpress
5
+
6
+ class PkiExpressOperator
7
+ attr_accessor :offline, :trust_lacuna_test_root, :signature_policy,
8
+ :timestamp_authority
9
+
10
+ def initialize(config = PkiExpressConfig.new)
11
+ @temp_files = []
12
+ @file_references = {}
13
+
14
+ @config = config
15
+ @version_manager = VersionManager.new
16
+ @trusted_roots = []
17
+ @offline = false
18
+ @trust_lacuna_test_root = false
19
+ @signature_policy = nil
20
+ @timestamp_authority = nil
21
+
22
+ ObjectSpace.define_finalizer(self, self.class.method(:finalize))
23
+ end
24
+
25
+ def self.finalize
26
+ @temp_files.each do |file|
27
+ File.delete(file) if File.exist?(file)
28
+ end
29
+ end
30
+
31
+ def add_file_reference(key, reference_path)
32
+
33
+ if reference_path.nil?
34
+ raise ArgumentError.new('The provided reference path is not valid')
35
+ end
36
+
37
+ unless File.exists?(reference_path)
38
+ raise ArgumentError.new('The provided reference file was not found')
39
+ end
40
+
41
+ @file_references[key] = reference_path
42
+ end
43
+
44
+ def add_trusted_root(root_path)
45
+
46
+ if root_path.nil?
47
+ raise ArgumentError.new('The provided trusted root path is not valid')
48
+ end
49
+
50
+ unless File.exists?(root_path)
51
+ raise ArgumentError.new("The provided trusted root path doesn't exist: #{root_path}")
52
+ end
53
+
54
+ @trusted_roots.append(root_path)
55
+ end
56
+
57
+ protected
58
+ def invoke_plain(command, args = [])
59
+ invoke(command, args, true)
60
+ end
61
+
62
+ def invoke(command, args = [], plain_output = false)
63
+
64
+ # Add PKI Express invocation arguments.
65
+ cmd_args = []
66
+ get_pki_express_invocation.each do |arg|
67
+ cmd_args.append(arg)
68
+ end
69
+
70
+ # Add PKI Express command.
71
+ cmd_args.append(command)
72
+
73
+ # Add PKI Express arguments.
74
+ cmd_args.concat args
75
+
76
+ # Add file references if added.
77
+ unless @file_references.nil?
78
+ @file_references.each do |key, value|
79
+ cmd_args.append('--file_reference')
80
+ cmd_args.append("#{key}=#{value}")
81
+ end
82
+ end
83
+
84
+ # Add trusted roots if added.
85
+ unless @trusted_roots.nil?
86
+ @trusted_roots.each do |trusted_root|
87
+ cmd_args.append('--trust-root')
88
+ cmd_args.append(trusted_root)
89
+ end
90
+ end
91
+
92
+ # Add trust Lacuna test root if set.
93
+ if @trust_lacuna_test_root
94
+ cmd_args.append('--trust-test')
95
+ end
96
+
97
+ # Add offline option if provided.
98
+ if @offline
99
+ cmd_args.append('--offline')
100
+ # This option can only be used on versions greater than 1.2 of the
101
+ # PKI Express.
102
+ @version_manager.require_version('1.2')
103
+ end
104
+
105
+ # Add base64 output option
106
+ unless plain_output
107
+ cmd_args.append('--base64')
108
+ end
109
+
110
+ # Verify the necessity of using the --min-version flag.
111
+ if @version_manager.require_min_version_flag?
112
+ cmd_args.append('--min-version')
113
+ cmd_args.append(@version_manager.min_version.to_s)
114
+ end
115
+
116
+ # Escape command args
117
+ escaped = []
118
+ cmd_args.each do |arg|
119
+ escaped.append("\"#{arg}\"")
120
+ # escaped.append(Shellwords.escape(arg))
121
+ end
122
+
123
+ # Perform the "dotnet" command.
124
+ stdout, _stderr, status = Open3.capture3(escaped.join(' '))
125
+
126
+ if status != 0
127
+ if status === ErrorCodes::BAD_SYNTAX and @version_manager.min_version > '1.0'
128
+ raise CommandError.new(status, "#{stdout} >>>>> TIP: This operation requires PKI Express #{@version_manager.min_version}, please check your PKI Express version.")
129
+ end
130
+ if status === ErrorCodes::VALIDATION_FAILED
131
+ raise ValidationError.new(stdout)
132
+ end
133
+ if status === ErrorCodes::COMMAND_NOT_FOUND
134
+ raise InstallationNotFoundError.new("Could not find PKI Express's installation.")
135
+ end
136
+
137
+ raise CommandError.new(status, stdout)
138
+ end
139
+
140
+ # Return stdout if the command executed with success.
141
+ stdout
142
+ end
143
+
144
+ def get_pki_express_invocation
145
+
146
+ # Identify OS.
147
+ if (/linux/ =~ RUBY_PLATFORM) != nil
148
+ system = :linux
149
+ elsif (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
150
+ system = :win
151
+ else
152
+ raise Error.new("Unsupported OS: #{RUBY_PLATFORM}")
153
+ end
154
+
155
+ # Verify if hte PKI Express home is set on configuration
156
+ home = @config.pki_express_home
157
+ if not home.nil?
158
+
159
+ if system == :linux
160
+ unless File.exists?(File.expand_path('pkie.dll', home))
161
+ raise InstallationNotFoundError.new("The file pkie.dll could not be found on directory #{home}")
162
+ end
163
+ elsif not File.exists?(File.expand_path('pkie.exe', home))
164
+ raise InstallationNotFoundError.new("The file pkie.exe could not be found on directory #{home}")
165
+ end
166
+
167
+ elsif system == :win
168
+
169
+ if File.exists?(File.join(ENV['ProgramW6432'], 'Lacuna Software', 'PKI Express', 'pkie.exe'))
170
+ home = File.join(ENV['ProgramW6432'], 'Lacuna Software', 'PKI Express')
171
+ elsif File.exists?(File.join(ENV['ProgramFiles(x86)'], 'Lacuna Software', 'PKI Express', 'pkie.exe'))
172
+ home = File.join(ENV['ProgramFiles(x86)'], 'Lacuna Software', 'PKI Express')
173
+ elsif File.exists?(File.join(ENV['LOCALAPPDATA'], 'Lacuna Software', 'PKI Express', 'pkie.exe'))
174
+ home = File.join(ENV['LOCALAPPDATA'], 'Lacuna Software', 'PKI Express')
175
+ elsif File.exists?(File.join(ENV['LOCALAPPDATA'], 'Lacuna Software (x86)', 'PKI Express', 'pkie.exe'))
176
+ home = File.join(ENV['LOCALAPPDATA'], 'Lacuna Software (x86)', 'PKI Express')
177
+ end
178
+
179
+ if home.nil?
180
+ raise InstallationNotFoundError.new('Could not determine the
181
+ installation folder of PKI Express. If you installed PKI Express on
182
+ a custom folder, make sure your chosen folder are specified on the
183
+ PkiExpressConfig object.')
184
+ end
185
+ end
186
+
187
+ if system == :linux
188
+ unless home.nil?
189
+ return ['dotnet', File.expand_path('pkie.dll', home)]
190
+ end
191
+
192
+ return ['pkie']
193
+ end
194
+ [File.expand_path('pkie.exe', home)]
195
+ end
196
+
197
+ def parse_output(data_base64)
198
+ json_buff = Base64.decode64(data_base64)
199
+ JSON.parse(json_buff, symbolize_names: true)
200
+ end
201
+
202
+ def create_temp_file
203
+ file = Tempfile.new('pkie', @config.temp_folder)
204
+ temp_path = file.path
205
+ file.close
206
+ @temp_files.append(temp_path)
207
+ temp_path
208
+ end
209
+
210
+ def get_transfer_filename
211
+ # Generate 16 random bytes. Return a string containing the hex decimals of
212
+ # this array.
213
+ SecureRandom.hex(16)
214
+ end
215
+ end
216
+ end