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.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +19 -0
- data/Rakefile +2 -0
- data/lib/rest_pki.rb +35 -0
- data/lib/rest_pki/authentication.rb +43 -0
- data/lib/rest_pki/cades_signature_finisher.rb +48 -0
- data/lib/rest_pki/cades_signature_starter.rb +148 -0
- data/lib/rest_pki/full_xml_signature_starter.rb +51 -0
- data/lib/rest_pki/namespace_manager.rb +16 -0
- data/lib/rest_pki/object.rb +126 -0
- data/lib/rest_pki/pades_signature_finisher.rb +50 -0
- data/lib/rest_pki/pades_signature_starter.rb +110 -0
- data/lib/rest_pki/pades_visual_positioning_presets.rb +32 -0
- data/lib/rest_pki/resources/authentication_model.rb +5 -0
- data/lib/rest_pki/resources/cades_model.rb +5 -0
- data/lib/rest_pki/resources/pades_model.rb +5 -0
- data/lib/rest_pki/resources/response_model.rb +5 -0
- data/lib/rest_pki/resources/xml_model.rb +5 -0
- data/lib/rest_pki/rest_base_error.rb +12 -0
- data/lib/rest_pki/rest_error.rb +16 -0
- data/lib/rest_pki/rest_unreachable_error.rb +13 -0
- data/lib/rest_pki/restpki_client.rb +95 -0
- data/lib/rest_pki/restpki_error.rb +16 -0
- data/lib/rest_pki/signature_finisher.rb +33 -0
- data/lib/rest_pki/signature_starter.rb +59 -0
- data/lib/rest_pki/standard_security_contexts.rb +10 -0
- data/lib/rest_pki/standard_signature_policies.rb +25 -0
- data/lib/rest_pki/validation_error.rb +11 -0
- data/lib/rest_pki/validation_item.rb +33 -0
- data/lib/rest_pki/validation_results.rb +107 -0
- data/lib/rest_pki/version.rb +3 -0
- data/lib/rest_pki/xml_element_signature_starter.rb +68 -0
- data/lib/rest_pki/xml_id_resolution_table.rb +40 -0
- data/lib/rest_pki/xml_insertion_options.rb +11 -0
- data/lib/rest_pki/xml_signature_finisher.rb +52 -0
- data/lib/rest_pki/xml_signature_starter.rb +84 -0
- data/rest_pki.gemspec +30 -0
- 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,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,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,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,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
|