pki_express 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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