rest_pki 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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/CHANGELOG.md +12 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +19 -0
  7. data/Rakefile +2 -0
  8. data/lib/rest_pki.rb +35 -0
  9. data/lib/rest_pki/authentication.rb +43 -0
  10. data/lib/rest_pki/cades_signature_finisher.rb +48 -0
  11. data/lib/rest_pki/cades_signature_starter.rb +148 -0
  12. data/lib/rest_pki/full_xml_signature_starter.rb +51 -0
  13. data/lib/rest_pki/namespace_manager.rb +16 -0
  14. data/lib/rest_pki/object.rb +126 -0
  15. data/lib/rest_pki/pades_signature_finisher.rb +50 -0
  16. data/lib/rest_pki/pades_signature_starter.rb +110 -0
  17. data/lib/rest_pki/pades_visual_positioning_presets.rb +32 -0
  18. data/lib/rest_pki/resources/authentication_model.rb +5 -0
  19. data/lib/rest_pki/resources/cades_model.rb +5 -0
  20. data/lib/rest_pki/resources/pades_model.rb +5 -0
  21. data/lib/rest_pki/resources/response_model.rb +5 -0
  22. data/lib/rest_pki/resources/xml_model.rb +5 -0
  23. data/lib/rest_pki/rest_base_error.rb +12 -0
  24. data/lib/rest_pki/rest_error.rb +16 -0
  25. data/lib/rest_pki/rest_unreachable_error.rb +13 -0
  26. data/lib/rest_pki/restpki_client.rb +95 -0
  27. data/lib/rest_pki/restpki_error.rb +16 -0
  28. data/lib/rest_pki/signature_finisher.rb +33 -0
  29. data/lib/rest_pki/signature_starter.rb +59 -0
  30. data/lib/rest_pki/standard_security_contexts.rb +10 -0
  31. data/lib/rest_pki/standard_signature_policies.rb +25 -0
  32. data/lib/rest_pki/validation_error.rb +11 -0
  33. data/lib/rest_pki/validation_item.rb +33 -0
  34. data/lib/rest_pki/validation_results.rb +107 -0
  35. data/lib/rest_pki/version.rb +3 -0
  36. data/lib/rest_pki/xml_element_signature_starter.rb +68 -0
  37. data/lib/rest_pki/xml_id_resolution_table.rb +40 -0
  38. data/lib/rest_pki/xml_insertion_options.rb +11 -0
  39. data/lib/rest_pki/xml_signature_finisher.rb +52 -0
  40. data/lib/rest_pki/xml_signature_starter.rb +84 -0
  41. data/rest_pki.gemspec +30 -0
  42. metadata +146 -0
@@ -0,0 +1,16 @@
1
+
2
+ module RestPki
3
+ class RestPkiError < RestBaseError
4
+ attr_reader :error_code, :detail
5
+
6
+ def initialize(verb, url, error_code, detail)
7
+ message = "REST PKI action #{verb} #{url} error: #{error_code}"
8
+ unless detail.to_s.blank?
9
+ message += " (#{detail})"
10
+ end
11
+ super(message, verb, url)
12
+ @error_code = error_code
13
+ @detail = detail
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+
2
+ module RestPki
3
+ class SignatureFinisher
4
+ attr_accessor :token, :signature
5
+
6
+ def initialize(restpki_client)
7
+ @restpki_client = restpki_client
8
+ @token = nil
9
+ @signature = nil
10
+ @callback_argument = nil
11
+ @certificate_info = nil
12
+ @done = false
13
+ end
14
+
15
+ def finish
16
+ raise "#{self.class.name}#finish is an abstract method."
17
+ end
18
+
19
+ def get_callback_argument
20
+ unless @done
21
+ raise 'The field "callback_argument" can only be accessed after calling the finish method'
22
+ end
23
+ @callback_argument
24
+ end
25
+
26
+ def certificate_info
27
+ unless @done
28
+ raise 'The field "certificate_info" can only be accessed after calling the finish method'
29
+ end
30
+ @certificate_info
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ require 'base64'
2
+
3
+ module RestPki
4
+ class SignatureStarter
5
+ attr_accessor :signature_policy_id, :security_context_id, :callback_argument, :ignore_revocation_status_unknown
6
+
7
+ def initialize(restpki_client)
8
+ @restpki_client = restpki_client
9
+ @signer_certificate_base64 = nil
10
+ @signature_policy_id = nil
11
+ @security_context_id = nil
12
+ @callback_argument = nil
13
+ @done = false
14
+ @certificate = nil
15
+ @ignore_revocation_status_unknown = false
16
+ end
17
+
18
+ def certificate
19
+ unless @done
20
+ raise 'The field "certificate_info" can only be accessed after calling one of the start methods'
21
+ end
22
+ @certificate
23
+ end
24
+
25
+ def signer_certificate_base64=(content_base64)
26
+ @signer_certificate_base64 = content_base64
27
+ end
28
+
29
+ def signer_certificate=(content_raw)
30
+ @signer_certificate_base64 = Base64.encode64(content_raw)
31
+ end
32
+
33
+ def start_with_webpki
34
+ raise "#{self.class.name}#start_with_webpki is an abstract method."
35
+ end
36
+
37
+ def start
38
+ raise "#{self.class.name}#start is an abstract method."
39
+ end
40
+
41
+ protected
42
+ def get_signature_algorithm(oid)
43
+ case oid
44
+ when '1.2.840.113549.2.5'
45
+ return OpenSSL::Digest::MD5.new
46
+ when '1.3.14.3.2.26'
47
+ return OpenSSL::Digest::SHA1.new
48
+ when '2.16.840.1.101.3.4.2.1'
49
+ return OpenSSL::Digest::SHA256.new
50
+ when '2.16.840.1.101.3.4.2.2'
51
+ return OpenSSL::Digest::SHA384.new
52
+ when '2.16.840.1.101.3.4.2.3'
53
+ return OpenSSL::Digest::SHA512.new
54
+ else
55
+ return nil
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,10 @@
1
+
2
+ module RestPki
3
+ class StandardSecurityContexts
4
+
5
+ PKI_BRAZIL = '201856ce-273c-4058-a872-8937bd547d36'
6
+ PKI_ITALY = 'c438b17e-4862-446b-86ad-6f85734f0bfe'
7
+ WINDOWS_SERVER = '3881384c-a54d-45c5-bbe9-976b674f5ec7'
8
+
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+
2
+ module RestPki
3
+ class StandardSignaturePolicies
4
+
5
+ PADES_BASIC = '78d20b33-014d-440e-ad07-929f05d00cdf'
6
+ PADES_BASIC_WITH_ICPBR_CERTS = '3fec800c-366c-49bf-82c5-2e72154e70f6'
7
+ PADES_T_WITH_ICPBR_CERTS = '6a39aeea-a2d0-4754-bf8c-19da15296ddb'
8
+ PADES_ICPBR_ADR_BASICA = '531d5012-4c0d-4b6f-89e8-ebdcc605d7c2'
9
+ PADES_ICPBR_ADR_TEMPO = '10f0d9a5-a0a9-42e9-9523-e181ce05a25b'
10
+
11
+ CADES_BES = 'a4522485-c9e5-46c3-950b-0d6e951e17d1'
12
+ CADES_ICPBR_ADR_BASICA = '3ddd8001-1672-4eb5-a4a2-6e32b17ddc46'
13
+ CADES_ICPBR_ADR_TEMPO = 'a5332ad1-d105-447c-a4bb-b5d02177e439'
14
+ # CADES_ICPBR_ADR_VALIDACAO = '92378630-dddf-45eb-8296-8fee0b73d5bb'
15
+ CADES_ICPBR_ADR_COMPLETA = '30d881e7-924a-4a14-b5cc-d5a1717d92f6'
16
+
17
+ XML_XADES_BES = '1beba282-d1b6-4458-8e46-bd8ad6800b54'
18
+ XML_DSIG_BASIC = '2bb5d8c9-49ba-4c62-8104-8141f6459d08'
19
+ XML_ICPBR_NFE_PADRAO_NACIONAL = 'a3c24251-d43a-4ba4-b25d-ee8e2ab24f06'
20
+ XML_ICPBR_ADR_BASICA = '1cf5db62-58b6-40ba-88a3-d41bada9b621'
21
+ XML_ICPBR_ADR_TEMPO = '5aa2e0af-5269-43b0-8d45-f4ef52921f04'
22
+
23
+
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module RestPki
3
+ class ValidationError < RestBaseError
4
+ attr_reader :validation_results
5
+
6
+ def initialize(verb, url, validation_results)
7
+ super(validation_results.__to_string, verb, url)
8
+ @validation_results = validation_results
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+
2
+ module RestPki
3
+ class ValidationItem
4
+ attr_reader :type, :message, :detail
5
+
6
+ def initialize(model)
7
+ @type = model['type']
8
+ @message = model['message']
9
+ @detail = model['detail']
10
+ @inner_validation_results = nil
11
+ unless model['innerValidationResults'].nil?
12
+ @inner_validation_results = RestPki::ValidationResults.new(model['innerValidationResults'])
13
+ end
14
+ end
15
+
16
+ def __to_string
17
+ to_string(0)
18
+ end
19
+
20
+ def to_string(indentation_level)
21
+ text = ''
22
+ text += @message
23
+ unless @detail.to_s.blank?
24
+ text += " (#{@detail})"
25
+ end
26
+ unless @inner_validation_results.nil?
27
+ text += '\n'
28
+ text += @inner_validation_results.to_string(indentation_level + 1)
29
+ end
30
+ text
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,107 @@
1
+
2
+ module RestPki
3
+ class ValidationResults
4
+
5
+ def initialize(model)
6
+ @errors = convert_items(model['errors'])
7
+ @warnings = convert_items(model['warnings'])
8
+ @passed_checks = convert_items(model['passedChecks'])
9
+ end
10
+
11
+ def is_valid
12
+ @errors.to_a.empty?
13
+ end
14
+
15
+ def get_checks_performed
16
+ @errors.to_a.length + @warnings.to_a.length + @passed_checks.to_a.length
17
+ end
18
+
19
+ def has_errors
20
+ !is_valid
21
+ end
22
+
23
+ def has_warnings
24
+ @warnings.to_a.any?
25
+ end
26
+
27
+ def has_passed_checks
28
+ @passed_checks.to_a.any?
29
+ end
30
+
31
+ def __to_string
32
+ to_string(0)
33
+ end
34
+
35
+ def to_string(indentation_level)
36
+ tab = ''
37
+ (1..indentation_level).each do; tab += '\t'; end
38
+ text = ''
39
+ text += get_summary(indentation_level)
40
+ if has_errors
41
+ text += "\n#{tab}Errors:\n"
42
+ text += join_items(@errors, indentation_level)
43
+ end
44
+ if has_warnings
45
+ text += "\n#{tab}Warnings:\n"
46
+ text += join_items(@warnings, indentation_level)
47
+ end
48
+ if has_passed_checks
49
+ text += "\n#{tab}Passed checks:\n"
50
+ text += join_items(@passed_checks, indentation_level)
51
+ end
52
+ text
53
+ end
54
+
55
+ def get_summary(indentation_level=0)
56
+ tab = ''
57
+ (1..indentation_level).each do; tab += '\t'; end
58
+ text = "#{tab}Validation results: "
59
+ if get_checks_performed == 0
60
+ text += 'no checks performed'
61
+ else
62
+ text += "#{get_checks_performed} checks performed"
63
+ if has_errors
64
+ text += ", #{@errors.to_a.length} errors"
65
+ end
66
+ if has_warnings
67
+ text += ", #{@warnings.to_a.length} warnings"
68
+ end
69
+ if has_passed_checks
70
+ if !has_errors && !has_warnings
71
+ text += ', all passed'
72
+ else
73
+ text += ", #{@passed_checks.to_a.length} passed"
74
+ end
75
+ end
76
+ end
77
+ text
78
+ end
79
+
80
+ private
81
+ def convert_items(items)
82
+ converted = []
83
+ items.each do |item|
84
+ converted.push(RestPki::ValidationItem.new(item))
85
+ end
86
+ converted
87
+ end
88
+
89
+ def join_items(items, indentation_level)
90
+ text = ''
91
+ is_first = true
92
+ tab = ''
93
+ (1..indentation_level).each do; tab += '\t'; end
94
+
95
+ items.each do |item|
96
+ if is_first
97
+ is_first = false
98
+ else
99
+ text += '\n'
100
+ end
101
+ text += "#{tab}- "
102
+ text += item.to_string(indentation_level)
103
+ end
104
+ text
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ module RestPki
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,68 @@
1
+ module RestPki
2
+ class XmlElementSignatureStarter < XmlSignatureStarter
3
+ attr_accessor :element_tosign_id, :id_resolution_table
4
+
5
+ def initialize(restpki_client)
6
+ super(restpki_client)
7
+ @element_tosign_id = nil
8
+ @id_resolution_table = nil
9
+ end
10
+
11
+ def start_with_webpki
12
+ verify_common_parameters(true)
13
+ if @xml_content_base64.to_s.blank?
14
+ raise 'The XML was not set'
15
+ end
16
+ if @element_tosign_id.to_s.blank?
17
+ raise 'The XML element id to sign was not set'
18
+ end
19
+
20
+ request = get_request
21
+ request['elementToSignId'] = @element_tosign_id
22
+ unless @id_resolution_table.nil?
23
+ request['idResolutionTable'] = @id_resolution_table.to_model
24
+ end
25
+
26
+ response = @restpki_client.post('Api/XmlSignatures/XmlElementSignature', request, 'xml_model')
27
+
28
+ unless response['certificate'].nil?
29
+ @certificate = response['certificate']
30
+ end
31
+ @done = true
32
+
33
+ response['token']
34
+ end
35
+
36
+ def start
37
+ verify_common_parameters(true)
38
+ if @xml_content_base64.to_s.blank?
39
+ raise 'The XML was not set'
40
+ end
41
+ if @element_tosign_id.to_s.blank?
42
+ raise 'The XML element id to sign was not set'
43
+ end
44
+
45
+ request = get_request
46
+ request['elementToSignId'] = @element_tosign_id
47
+ unless @id_resolution_table.nil?
48
+ request['idResolutionTable'] = @id_resolution_table.to_model
49
+ end
50
+
51
+ response = @restpki_client.post('Api/XmlSignatures/XmlElementSignature', request, 'xml_model')
52
+
53
+ unless response['certificate'].nil?
54
+ @certificate = response['certificate']
55
+ end
56
+ @done = true
57
+
58
+ {
59
+ :token => response['token'],
60
+ :to_sign_data => response['toSignData'],
61
+ :to_sign_hash => response['toSignHash'],
62
+ :digest_algorithm_oid => response['digestAlgorithmOid'],
63
+ :signature_algorithm => get_signature_algorithm(response['digestAlgorithmOid'])
64
+ }
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,40 @@
1
+ module RestPki
2
+ class XmlIdResolutionTable
3
+ attr_accessor :include_xml_id_attribute, :element_id_attributes, :global_id_attributes
4
+
5
+ def initialize(inc_xml_id_attribute=nil)
6
+ @include_xml_id_attribute = inc_xml_id_attribute
7
+ @element_id_attributes = Hash.new
8
+ @global_id_attributes = Hash.new
9
+ end
10
+
11
+ def add_global_id_attribute(local_name, namespace=nil)
12
+ @global_id_attributes.push({
13
+ localName: local_name,
14
+ namespace: namespace
15
+ })
16
+ end
17
+
18
+ def set_element_id_attribute(element_local_name, element_namespace, attribute_local_name, attribute_namespace=nil)
19
+ @element_id_attributes.push({
20
+ element: {
21
+ localName: element_local_name,
22
+ namespace: element_namespace
23
+ },
24
+ attribute: {
25
+ localName: attribute_local_name,
26
+ namespace: attribute_namespace
27
+ }
28
+ })
29
+ end
30
+
31
+ def to_model
32
+ {
33
+ includeXmlIdAttribute: @include_xml_id_attribute,
34
+ elementIdAttributes: @element_id_attributes,
35
+ globalIdAttributes: @global_id_attributes
36
+ }
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module RestPki
3
+ class XmlInsertionOptions
4
+
5
+ APPEND_CHILD = 'AppendChild'
6
+ PREPEND_CHILD = 'PrependChild'
7
+ APPEND_SIBLING = 'AppendSibling'
8
+ PREPEND_SIBLING = 'PrependSibling'
9
+
10
+ end
11
+ end
@@ -0,0 +1,52 @@
1
+ require 'base64'
2
+
3
+ module RestPki
4
+ class XmlSignatureFinisher < SignatureFinisher
5
+
6
+ def initialize(restpki_client)
7
+ super(restpki_client)
8
+ @signed_xml_content = nil
9
+ end
10
+
11
+ def finish
12
+ if @token.to_s.blank?
13
+ raise 'The token was not set'
14
+ end
15
+
16
+ if @signature.to_s.blank?
17
+ response = @restpki_client.post("Api/XmlSignatures/#{@token}/Finalize", nil, 'xml_model')
18
+ else
19
+ request = { signature: @signature }
20
+ response = @restpki_client.post("Api/XmlSignatures/#{@token}/SignedBytes", request, 'xml_model')
21
+ end
22
+
23
+ @signed_xml_content = Base64.decode64(response['signedXml'])
24
+ @callback_argument = response['callbackArgument']
25
+ @certificate_info = response['certificate']
26
+ @done = true
27
+
28
+ @signed_xml_content
29
+ end
30
+
31
+ def signed_xml_content
32
+ unless @done
33
+ raise 'The field "signed_xml_content" can only be accessed after calling the finish method'
34
+ end
35
+
36
+ @signed_xml_content
37
+ end
38
+
39
+ def write_signed_xml(xml_path)
40
+ unless @done
41
+ raise 'The method write_signed_xml can only be called after calling the finish method'
42
+ end
43
+
44
+ file = File.open(xml_path, 'wb')
45
+ file.write(@signed_xml_content)
46
+ file.close
47
+
48
+ nil # No value is returned.
49
+ end
50
+
51
+ end
52
+ end