signature_dfe 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rubocop.yml +12 -0
- data/.github/workflows/test.yml +17 -0
- data/.rubocop.yml +27 -0
- data/.travis.yml +2 -2
- data/CHANGELOG +2 -0
- data/Gemfile +2 -2
- data/README.md +40 -3
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/lib/signature_dfe.rb +19 -14
- data/lib/signature_dfe/disabling.rb +22 -0
- data/lib/signature_dfe/evento_nfe.rb +22 -67
- data/lib/signature_dfe/nfe.rb +19 -62
- data/lib/signature_dfe/ssl.rb +115 -77
- data/lib/signature_dfe/templates/signature.xml +20 -0
- data/lib/signature_dfe/templates/signed_info.xml +12 -0
- data/lib/signature_dfe/version.rb +1 -1
- data/lib/signature_dfe_check.rb +32 -0
- data/lib/signature_dfe_xml.rb +115 -0
- data/signature_dfe.gemspec +26 -34
- metadata +25 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 187cda15b027142a73148dea40fbc77c0c50e29bf2ee52277ed8bef11bceda4e
|
4
|
+
data.tar.gz: 583dcd4b1b348b90df234d155898f5a66d3ae9fe60945c5fa3dd07ff3dd4683e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1fce5b56885cb6b1b3bca3206307770a20cfa9152428128ccf446ddee6cacad35829e061bafbc701053bb44f067ccbfca45bdd795d5528e2b76e1ffeae059b8
|
7
|
+
data.tar.gz: 82b77da8b4a403c5774eba82d0ba207a307bab3c90585e58115e53361965568b4db97ac034dcd7380cad4365389460e98a7aa95dd5e3202b2859b6c902c9331c
|
@@ -0,0 +1,17 @@
|
|
1
|
+
name: Rails Unit Tests
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
build:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
|
7
|
+
steps:
|
8
|
+
- uses: actions/checkout@v2
|
9
|
+
- name: Set up Ruby 2.7
|
10
|
+
uses: actions/setup-ruby@v1
|
11
|
+
with:
|
12
|
+
ruby-version: 2.7
|
13
|
+
- name: Build and test with Rake
|
14
|
+
run: |
|
15
|
+
gem install bundler
|
16
|
+
bundle install
|
17
|
+
bundle exec rspec
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
|
4
|
+
Layout/LineLength:
|
5
|
+
Exclude:
|
6
|
+
|
7
|
+
Metrics/BlockLength:
|
8
|
+
Exclude:
|
9
|
+
- signature_dfe.gemspec
|
10
|
+
- spec/xml/signature_dfe_xml_spec.rb
|
11
|
+
- spec/signature_dfe_spec.rb
|
12
|
+
- spec/event/signature_dfe_event_pem_spec.rb
|
13
|
+
- spec/event/signature_dfe_event_pkcs_spec.rb
|
14
|
+
- spec/nfe/signature_dfe_nfe_pem_spec.rb
|
15
|
+
- spec/nfe/signature_dfe_nfe_pkcs_spec.rb
|
16
|
+
- spec/xml/event/signatire_dfe_event_spec.rb
|
17
|
+
|
18
|
+
Style/Documentation:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/FrozenStringLiteralComment:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Metrics/AbcSize:
|
25
|
+
Max: 20
|
26
|
+
Exclude:
|
27
|
+
# - lib/signature_dfe/ssl.rb
|
data/.travis.yml
CHANGED
data/CHANGELOG
CHANGED
data/Gemfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
3
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in signature_dfe.gemspec
|
6
6
|
gemspec
|
data/README.md
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
|
1
|
+
![test](https://github.com/thiaguerd/signature_dfe/workflows/Rails%20Unit%20Tests/badge.svg)
|
2
|
+
[![Travis CI](https://travis-ci.org/thiaguerd/signature_dfe.svg?branch=master)](https://travis-ci.org/thiaguerd/signature_dfe)
|
3
|
+
![rubocop](https://github.com/thiaguerd/signature_dfe/workflows/Rubocop/badge.svg)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/signature_dfe.svg)](https://rubygems.org/gems/signature_dfe)
|
5
|
+
![](https://ruby-gem-downloads-badge.herokuapp.com/signature_dfe?metric=true)
|
6
|
+
![](https://ruby-gem-downloads-badge.herokuapp.com/signature_dfe?type=total)
|
2
7
|
|
3
8
|
# SignatureDfe
|
4
9
|
|
@@ -25,7 +30,7 @@ Or install it yourself as:
|
|
25
30
|
|
26
31
|
$ gem install signature_dfe
|
27
32
|
|
28
|
-
##
|
33
|
+
## Configurando
|
29
34
|
|
30
35
|
Você vai precisar do certificado PKCS12 ou da chave privada e o certificado público.
|
31
36
|
|
@@ -118,7 +123,8 @@ A forma qual vc tem a xml da assinautra completo onde vc passa o seu xml contend
|
|
118
123
|
inf_evento = %{
|
119
124
|
<infEvento Id="ID1101115515151515151515151515156546546546545646544701">
|
120
125
|
...
|
121
|
-
</infEvento>
|
126
|
+
</infEvento>
|
127
|
+
}
|
122
128
|
SignatureDfe::NFe::Event.sign inf_evento
|
123
129
|
```
|
124
130
|
|
@@ -160,6 +166,37 @@ signature_value = SignatureDfe::NFe::Event.signature_value event_id, digest_valu
|
|
160
166
|
x509certificate = SignatureDfe::SSL.cert
|
161
167
|
```
|
162
168
|
|
169
|
+
## Assinatura digital de Inutilização
|
170
|
+
|
171
|
+
Segue-se exatamente como os documentos anteriores
|
172
|
+
|
173
|
+
Para assinar passo a passo
|
174
|
+
|
175
|
+
```
|
176
|
+
inf_inut = %{
|
177
|
+
<infInut Id="ID06546541654654654654654654654654654654879">
|
178
|
+
...
|
179
|
+
</infInut>
|
180
|
+
}
|
181
|
+
digest_value = SignatureDfe::Inutilizacao.digest_value inf_inut
|
182
|
+
inut_id = "ID06546541654654654654654654654654654654879"
|
183
|
+
signature_value = SignatureDfe::Inutilizacao.signature_value inut_id, digest_value
|
184
|
+
x509certificate = SignatureDfe::SSL.cert
|
185
|
+
```
|
186
|
+
|
187
|
+
Ou, para gerar toda a assinatura em único passo
|
188
|
+
|
189
|
+
```
|
190
|
+
inf_inut = %{
|
191
|
+
<infInut Id="ID06546541654654654654654654654654654654879">
|
192
|
+
...
|
193
|
+
</infInut>
|
194
|
+
}
|
195
|
+
SignatureDfe::Inutilizacao.sign inf_inut
|
196
|
+
```
|
197
|
+
|
198
|
+
|
199
|
+
|
163
200
|
## License
|
164
201
|
|
165
202
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'signature_dfe'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "signature_dfe"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start(__FILE__)
|
data/lib/signature_dfe.rb
CHANGED
@@ -1,20 +1,25 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'signature_dfe/version'
|
2
|
+
require 'signature_dfe_xml'
|
3
|
+
require 'signature_dfe_check'
|
4
|
+
require 'openssl'
|
5
|
+
|
6
|
+
GEM_ROOT = File.expand_path('..', __dir__)
|
3
7
|
|
4
8
|
module SignatureDfe
|
5
|
-
|
9
|
+
class Error < StandardError; end
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
end
|
11
|
+
def initialize
|
12
|
+
raise 'this is a abstract class'
|
13
|
+
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
module AbstractClass
|
16
|
+
def initialize
|
17
|
+
raise 'this is a abstract class'
|
18
|
+
end
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
22
|
+
require 'signature_dfe/ssl'
|
23
|
+
require 'signature_dfe/nfe'
|
24
|
+
require 'signature_dfe/evento_nfe'
|
25
|
+
require 'signature_dfe/disabling'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SignatureDfe
|
2
|
+
class Inutilizacao
|
3
|
+
def self.digest_value(xml)
|
4
|
+
SignatureDfe::Xml.calc_digest_value('infInut', xml)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.signature_value(event_id, digest_value)
|
8
|
+
SignatureDfe::Xml.build_signed_info(event_id, digest_value)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.sign(xml)
|
12
|
+
digest_value_ = digest_value xml
|
13
|
+
event_id = SignatureDfe::Xml.namespace_value('Id', xml)
|
14
|
+
options = {
|
15
|
+
id: event_id,
|
16
|
+
digest_value: digest_value_,
|
17
|
+
signature_value: signature_value(event_id, digest_value_)
|
18
|
+
}
|
19
|
+
SignatureDfe::Xml.build_signature(options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -2,71 +2,26 @@ require 'base64'
|
|
2
2
|
require 'nokogiri'
|
3
3
|
|
4
4
|
module SignatureDfe
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
</SignedInfo>
|
28
|
-
}
|
29
|
-
signed_info_canonized = SignatureDfe.canonize signed_info
|
30
|
-
Base64.encode64(SignatureDfe::SSL.sign signed_info_canonized).strip
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.sign something
|
34
|
-
digest_value_ = digest_value something
|
35
|
-
|
36
|
-
event_id = something.scan(/\<infEvento[\s\S]*?\>/i)[0].scan(/\d/).join("")
|
37
|
-
|
38
|
-
signature_value_ = signature_value event_id, digest_value_
|
39
|
-
|
40
|
-
%{
|
41
|
-
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
|
42
|
-
<SignedInfo>
|
43
|
-
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
44
|
-
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
|
45
|
-
<Reference URI="#ID#{event_id}">
|
46
|
-
<Transforms>
|
47
|
-
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
48
|
-
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
49
|
-
</Transforms>
|
50
|
-
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
51
|
-
<DigestValue>#{digest_value_}</DigestValue>
|
52
|
-
</Reference>
|
53
|
-
</SignedInfo>
|
54
|
-
<SignatureValue>#{signature_value_}</SignatureValue>
|
55
|
-
<KeyInfo>
|
56
|
-
<X509Data>
|
57
|
-
<X509Certificate>#{SignatureDfe::SSL.cert.to_s.gsub(/\-\-\-\-\-[A-Z]+ CERTIFICATE\-\-\-\-\-/, "").strip}</X509Certificate>
|
58
|
-
</X509Data>
|
59
|
-
</KeyInfo>
|
60
|
-
</Signature>
|
61
|
-
}.gsub(/\>\s{1,}\</,"><").strip
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.canonize_inf_event inf_nfe
|
65
|
-
tag_inf_nfe = inf_nfe.scan(/\<infevento[\s\S]*?\>/i)[0]
|
66
|
-
SignatureDfe.canonize(tag_inf_nfe.include?(%{xmlns="http://www.portalfiscal.inf.br/nfe"}) ? tag_inf_nfe : inf_nfe.gsub(%{<infEvento},%{<infEvento xmlns="http://www.portalfiscal.inf.br/nfe"}))
|
67
|
-
end
|
68
|
-
|
69
|
-
private_class_method :canonize_inf_event
|
70
|
-
end
|
71
|
-
end
|
5
|
+
class NFe
|
6
|
+
class Event
|
7
|
+
def self.digest_value(xml)
|
8
|
+
SignatureDfe::Xml.calc_digest_value('infEvento', xml)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.signature_value(event_id, digest_value)
|
12
|
+
SignatureDfe::Xml.build_signed_info(event_id, digest_value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.sign(xml)
|
16
|
+
digest_value_ = digest_value xml
|
17
|
+
event_id = SignatureDfe::Xml.namespace_value('Id', xml)
|
18
|
+
options = {
|
19
|
+
id: event_id,
|
20
|
+
digest_value: digest_value_,
|
21
|
+
signature_value: signature_value(event_id, digest_value_)
|
22
|
+
}
|
23
|
+
SignatureDfe::Xml.build_signature(options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
72
27
|
end
|
data/lib/signature_dfe/nfe.rb
CHANGED
@@ -1,67 +1,24 @@
|
|
1
1
|
require 'base64'
|
2
2
|
require 'nokogiri'
|
3
3
|
module SignatureDfe
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
ch_nfe = something.scan(/\<infnfe[\s\S]*?\>/i)[0].scan(/\d{44}/)[0]
|
4
|
+
class NFe
|
5
|
+
def self.digest_value(xml)
|
6
|
+
SignatureDfe::Xml.calc_digest_value('infNFe', xml)
|
7
|
+
end
|
10
8
|
|
11
|
-
|
9
|
+
def self.sign(xml)
|
10
|
+
digest_value_ = digest_value xml
|
11
|
+
ch_nfe = SignatureDfe::Xml.namespace_value('Id', xml).scan(/\d{44}/)[0]
|
12
|
+
options = {
|
13
|
+
id: "NFe#{ch_nfe}",
|
14
|
+
digest_value: digest_value_,
|
15
|
+
signature_value: signature_value(ch_nfe, digest_value_)
|
16
|
+
}
|
17
|
+
SignatureDfe::Xml.build_signature(options)
|
18
|
+
end
|
12
19
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
<Transforms>
|
19
|
-
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
20
|
-
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
21
|
-
</Transforms>
|
22
|
-
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
23
|
-
<DigestValue>#{digest_value_}</DigestValue>
|
24
|
-
</Reference>
|
25
|
-
</SignedInfo>
|
26
|
-
<SignatureValue>#{signature_value_}</SignatureValue>
|
27
|
-
<KeyInfo>
|
28
|
-
<X509Data>
|
29
|
-
<X509Certificate>#{SignatureDfe::SSL.cert.to_s.gsub(/\-\-\-\-\-[A-Z]+ CERTIFICATE\-\-\-\-\-/, "").strip}</X509Certificate>
|
30
|
-
</X509Data>
|
31
|
-
</KeyInfo>
|
32
|
-
</Signature>}.gsub(/\>\s{1,}\</,"><").strip
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.canonize_inf_nfe inf_nfe
|
37
|
-
tag_inf_nfe = inf_nfe.scan(/\<infnfe[\s\S]*?\>/i)[0]
|
38
|
-
SignatureDfe.canonize(tag_inf_nfe.include?(%{xmlns="http://www.portalfiscal.inf.br/nfe"}) ? tag_inf_nfe : inf_nfe.gsub(%{<infNFe},%{<infNFe xmlns="http://www.portalfiscal.inf.br/nfe"}))
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.signature_value ch_nfe, digest_value
|
42
|
-
signed_info = %{
|
43
|
-
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
|
44
|
-
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
45
|
-
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
|
46
|
-
<Reference URI="#NFe#{ch_nfe}">
|
47
|
-
<Transforms>
|
48
|
-
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
49
|
-
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
50
|
-
</Transforms>
|
51
|
-
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
52
|
-
<DigestValue>#{digest_value}</DigestValue>
|
53
|
-
</Reference>
|
54
|
-
</SignedInfo>
|
55
|
-
}
|
56
|
-
signed_info_canonized = SignatureDfe.canonize signed_info
|
57
|
-
Base64.encode64(SignatureDfe::SSL.sign signed_info_canonized).strip
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.digest_value something
|
61
|
-
inf_nfe = something.scan(/\<infnfe[\s\S]*?\<\/infnfe\>/i)[0].gsub(/>\s+</,"><")
|
62
|
-
inf_nfe_canonized = canonize_inf_nfe inf_nfe
|
63
|
-
Base64.encode64(OpenSSL::Digest::SHA1.digest(inf_nfe_canonized)).strip
|
64
|
-
end
|
65
|
-
private_class_method :canonize_inf_nfe
|
66
|
-
end
|
67
|
-
end
|
20
|
+
def self.signature_value(ch_nfe, digest_value)
|
21
|
+
SignatureDfe::Xml.build_signed_info("NFe#{ch_nfe}", digest_value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/signature_dfe/ssl.rb
CHANGED
@@ -1,79 +1,117 @@
|
|
1
1
|
module SignatureDfe
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
2
|
+
class Config
|
3
|
+
include AbstractClass
|
4
|
+
|
5
|
+
attr_accessor :pkcs12, :pkey, :cert
|
6
|
+
|
7
|
+
attr_writer :password
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
clear
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear
|
14
|
+
@pkcs12 = nil
|
15
|
+
@pkey = nil
|
16
|
+
@cert = nil
|
17
|
+
@password = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
super.gsub(/\, \@pass[\s\S]*?\>/, '>')
|
22
|
+
end
|
23
|
+
|
24
|
+
def instance_variables
|
25
|
+
super - [:@password]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class SSL
|
30
|
+
include AbstractClass
|
31
|
+
|
32
|
+
@config = Config.new
|
33
|
+
|
34
|
+
class << self
|
35
|
+
attr_reader :config
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.sign(content, sign_method = OpenSSL::Digest::SHA1.new)
|
39
|
+
self.test unless defined?(@pk)
|
40
|
+
@pk.sign sign_method, content
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.cert
|
44
|
+
self.test unless defined?(@pk)
|
45
|
+
@cert.to_s.gsub(/\-\-\-\-\-[A-Z]+ CERTIFICATE\-\-\-\-\-/, '').strip
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
private
|
50
|
+
|
51
|
+
def error(msg)
|
52
|
+
raise SignatureDfe::Error, msg
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_pkcs12
|
56
|
+
(config.pkcs12.nil? || config.pkcs12.empty?)
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_pem
|
60
|
+
(config.pkey.nil? || config.pkey.empty?)
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_up
|
64
|
+
error 'You must be set up pkcs12 or pkey' if check_pkcs12 && check_pem
|
65
|
+
end
|
66
|
+
|
67
|
+
def load_pkcs12
|
68
|
+
pass = config.instance_variable_get(:@password)
|
69
|
+
aux = OpenSSL::PKCS12.new(File.read(config.pkcs12), pass)
|
70
|
+
@pk = aux.key
|
71
|
+
@cert = aux.certificate
|
72
|
+
rescue OpenSSL::PKCS12::PKCS12Error
|
73
|
+
error "Wrong password for '#{config.pkcs12}'"
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_pkc12
|
77
|
+
if File.exist? config.pkcs12
|
78
|
+
load_pkcs12
|
79
|
+
else
|
80
|
+
error "Your pkcs12 '#{config.pkcs12}' is not a valid file"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def check_cert
|
85
|
+
if config.cert.nil? || config.cert.empty?
|
86
|
+
error 'You must be set up the cert if you chose use pkey'
|
87
|
+
end
|
88
|
+
if File.exist? config.cert
|
89
|
+
@cert = OpenSSL::X509::Certificate.new(File.read(config.cert))
|
90
|
+
else
|
91
|
+
error "Your cert '#{config.cert}' is not a valid file"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_pem
|
96
|
+
if File.exist? config.pkey
|
97
|
+
pass = config.instance_variable_get(:@password)
|
98
|
+
@pk = OpenSSL::PKey::RSA.new File.read(config.pkey), pass
|
99
|
+
check_cert
|
100
|
+
else
|
101
|
+
error "Your pkey '#{config.pkey}' is not a valid file"
|
102
|
+
end
|
103
|
+
rescue OpenSSL::X509::CertificateError
|
104
|
+
error "Your cert '#{config.cert}' is not a valid file"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.test
|
109
|
+
set_up
|
110
|
+
test_pkc12 if config.pkcs12
|
111
|
+
test_pem if config.pkey && !config.pkcs12
|
112
|
+
true
|
113
|
+
rescue OpenSSL::PKey::RSAError
|
114
|
+
error "Wrong password for '#{config.pkey}'"
|
115
|
+
end
|
116
|
+
end
|
79
117
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
|
2
|
+
<SignedInfo>
|
3
|
+
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
4
|
+
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
|
5
|
+
<Reference URI="#:id">
|
6
|
+
<Transforms>
|
7
|
+
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
8
|
+
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
9
|
+
</Transforms>
|
10
|
+
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
11
|
+
<DigestValue>:digest_value</DigestValue>
|
12
|
+
</Reference>
|
13
|
+
</SignedInfo>
|
14
|
+
<SignatureValue>:signature_value</SignatureValue>
|
15
|
+
<KeyInfo>
|
16
|
+
<X509Data>
|
17
|
+
<X509Certificate>:x509_certificate</X509Certificate>
|
18
|
+
</X509Data>
|
19
|
+
</KeyInfo>
|
20
|
+
</Signature>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
|
2
|
+
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
3
|
+
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
|
4
|
+
<Reference URI="#:id">
|
5
|
+
<Transforms>
|
6
|
+
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
7
|
+
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
8
|
+
</Transforms>
|
9
|
+
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
10
|
+
<DigestValue>:digest_value</DigestValue>
|
11
|
+
</Reference>
|
12
|
+
</SignedInfo>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SignatureDfe
|
2
|
+
class Check
|
3
|
+
def self.only_signature_check(xml)
|
4
|
+
signed_info_canonized = Xml.signed_info_canonized xml
|
5
|
+
certificate = Xml.public_cert xml
|
6
|
+
certificate.public_key.verify(
|
7
|
+
OpenSSL::Digest.new(Xml.digest_method_algorithm(signed_info_canonized)),
|
8
|
+
Base64.decode64(Xml.node_content('SignatureValue', xml)),
|
9
|
+
signed_info_canonized
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.signature_check(xml)
|
14
|
+
return false unless digest_check(xml)
|
15
|
+
|
16
|
+
only_signature_check(xml)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.digest_check(xml)
|
20
|
+
uri = Xml.namespace_value('URI', Xml.tag('Reference', xml)).gsub('#', '')
|
21
|
+
xmlns = Xml.namespace_value('xmlns', xml)
|
22
|
+
node_assigned = Xml.get_node_by_namespace_value(uri, xml)
|
23
|
+
node_assigned.gsub!(/>\s+\</, '><')
|
24
|
+
node_name = Xml.node_name(node_assigned)
|
25
|
+
unless Xml.tag(node_name, xml).include?(xmlns)
|
26
|
+
node_assigned.gsub!(node_name, %(#{node_name} xmlns="#{xmlns}"))
|
27
|
+
end
|
28
|
+
dv = OpenSSL::Digest::SHA1.digest(Xml.canonize(node_assigned))
|
29
|
+
Base64.encode64(dv).strip == Xml.node_content('DigestValue', xml)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module SignatureDfe
|
2
|
+
class Xml
|
3
|
+
def self.calc_digest_value(tag, xml)
|
4
|
+
xml = SignatureDfe::Xml.node(tag, xml)
|
5
|
+
note_canonized = SignatureDfe::Xml.canonize_with_ns xml, tag
|
6
|
+
Base64.encode64(OpenSSL::Digest::SHA1.digest(note_canonized)).strip
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.build_signature(options = {})
|
10
|
+
path = "#{GEM_ROOT}/lib/signature_dfe/templates/signature.xml"
|
11
|
+
xml = File.read path
|
12
|
+
xml.gsub!(':id', options[:id])
|
13
|
+
xml.gsub!(':digest_value', options[:digest_value])
|
14
|
+
xml.gsub!(':signature_value', options[:signature_value])
|
15
|
+
cert = SignatureDfe::SSL.cert.to_s
|
16
|
+
cert.to_s.gsub(/\-\-\-\-\-[A-Z]+ CERTIFICATE\-\-\-\-\-/, '').strip!
|
17
|
+
xml.gsub!(':x509_certificate', cert.strip).gsub(/\>\s+\</, '><')
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.build_signed_info(id, digest_value_)
|
21
|
+
path = "#{GEM_ROOT}/lib/signature_dfe/templates/signed_info.xml"
|
22
|
+
signed_info = File.read path
|
23
|
+
signed_info.gsub!(':id', id)
|
24
|
+
signed_info.gsub!(':digest_value', digest_value_)
|
25
|
+
signed_info_canonized = SignatureDfe::Xml.canonize signed_info
|
26
|
+
Base64.encode64(SignatureDfe::SSL.sign(signed_info_canonized)).strip
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.digest_method_algorithm(xml)
|
30
|
+
xml.scan(/(\<DigestMethod[\s\S]*?\#)([\s\S]*?)(\"|\')/)[0][1]
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.node(name, xml)
|
34
|
+
r = %r{\<#{Regexp.escape(name)}[\s\S]*((/\>)|(#{Regexp.escape(name)}\>))}
|
35
|
+
xml.match(r)[0].gsub(/>\s+</, '><')
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.node_content(name, xml)
|
39
|
+
full_node = xml.scan(%r{\<#{name}.*?\>([\s\S]*?)\</#{name}\>})
|
40
|
+
return nil unless full_node[0]
|
41
|
+
|
42
|
+
full_node[0][0]
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.node_name(xml)
|
46
|
+
xml.scan(%r{\<[^/\s\>]*})[0].gsub('<', '')
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.tag(name, xml)
|
50
|
+
xml.scan(%r{\<#{name}[\S\s]*?[/\>|\>]})[0]
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.namespace_value(namespace, xml)
|
54
|
+
matches = xml.match(/#{Regexp.escape(namespace)}\=\"([^"]*)/)
|
55
|
+
return nil unless matches
|
56
|
+
|
57
|
+
matches[1]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.get_node_by_namespace_value(value, xml)
|
61
|
+
a = xml.match(%r{\<[^<]*#{Regexp.escape(value)}[^>]*(\>|/>)})[0]
|
62
|
+
node(node_name(a), xml)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.canonize_inf_nfe(xml)
|
66
|
+
tag_inf_nfe = node('infNFe', xml)
|
67
|
+
ns = 'xmlns="http://www.portalfiscal.inf.br/nfe"'
|
68
|
+
rxg = Regexp.escape(ns)
|
69
|
+
canonize(
|
70
|
+
if tag_inf_nfe.include?(rxg.to_s)
|
71
|
+
tag_inf_nfe
|
72
|
+
else
|
73
|
+
tag_inf_nfe.gsub(%(<infNFe), %(<infNFe #{ns}))
|
74
|
+
end
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.signed_info_canonized(xml)
|
79
|
+
canonize(signed_info_with_ns(xml))
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.canonize(xml, canonize_method = Nokogiri::XML::XML_C14N_1_1)
|
83
|
+
Nokogiri::XML(xml.gsub(/>\s+</, '><')).canonicalize(canonize_method)
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.public_cert(xml)
|
87
|
+
x509_certificate = Xml.node_content('X509Certificate', xml)
|
88
|
+
x509_certificate = [
|
89
|
+
'-----BEGIN CERTIFICATE-----',
|
90
|
+
x509_certificate,
|
91
|
+
'-----END CERTIFICATE-----'
|
92
|
+
].join("\n")
|
93
|
+
OpenSSL::X509::Certificate.new x509_certificate
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.signed_info_with_ns(xml)
|
97
|
+
content = Xml.node 'SignedInfo', xml
|
98
|
+
canonize content.gsub(
|
99
|
+
'<SignedInfo>',
|
100
|
+
%(<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">)
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.canonize_with_ns(xml, tag)
|
105
|
+
ns = %(xmlns="http://www.portalfiscal.inf.br/nfe")
|
106
|
+
canonize(
|
107
|
+
if xml.include?(ns.to_s)
|
108
|
+
xml
|
109
|
+
else
|
110
|
+
xml.gsub(%(<#{tag}), %(<#{tag} #{ns}))
|
111
|
+
end
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/signature_dfe.gemspec
CHANGED
@@ -1,43 +1,35 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require 'signature_dfe/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
|
6
|
+
repo = 'https://github.com/thiaguerd/signature_dfe'
|
7
|
+
spec.name = 'signature_dfe'
|
8
8
|
spec.version = SignatureDfe::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
|
12
|
-
spec.
|
13
|
-
spec.
|
14
|
-
spec.
|
15
|
-
spec.license = "MIT"
|
9
|
+
spec.authors = ['Thiago Feitosa']
|
10
|
+
spec.email = ['mail@thiago.pro']
|
11
|
+
spec.summary = 'Assinatura digital de documentos fiscais eletrônicos'
|
12
|
+
spec.description = 'Assinatura digital de NF-e NFC-e NFA-e CT-e MDF-e BP-e'
|
13
|
+
spec.homepage = repo
|
14
|
+
spec.license = 'MIT'
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
16
|
+
req_mgs = 'RubyGems >= 2.0 is required to protect against public gem pushes.'
|
17
|
+
raise req_mgs unless spec.respond_to?(:metadata)
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
20
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
21
|
+
spec.metadata['source_code_uri'] = repo
|
22
|
+
spec.metadata['changelog_uri'] = repo + '/blob/master/CHANGELOG'
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
rx = %r{^(test|spec|features)/}
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(rx) }
|
27
26
|
end
|
28
|
-
|
29
|
-
# Specify which files should be added to the gem when it is released.
|
30
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
31
|
-
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
32
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
|
-
end
|
34
|
-
spec.bindir = "exe"
|
27
|
+
spec.bindir = 'exe'
|
35
28
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
|
-
spec.require_paths = [
|
37
|
-
|
38
|
-
spec.add_development_dependency
|
39
|
-
spec.add_development_dependency
|
40
|
-
spec.add_development_dependency
|
41
|
-
spec.add_development_dependency
|
42
|
-
spec.add_development_dependency "nokogiri", "~> 1.9.1"
|
29
|
+
spec.require_paths = ['lib']
|
30
|
+
spec.add_development_dependency 'bundler', '~> 2.1.2'
|
31
|
+
spec.add_development_dependency 'nokogiri', '~> 1.10.7'
|
32
|
+
spec.add_development_dependency 'openssl', '~> 2.1.2'
|
33
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
43
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: signature_dfe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thiago Feitosa
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,70 +16,70 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.1.2
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 2.1.2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: nokogiri
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.10.7
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.10.7
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: openssl
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 2.1.2
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 2.1.2
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: '10.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: '10.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: '3.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: '3.0'
|
83
83
|
description: Assinatura digital de NF-e NFC-e NFA-e CT-e MDF-e BP-e
|
84
84
|
email:
|
85
85
|
- mail@thiago.pro
|
@@ -87,8 +87,11 @@ executables: []
|
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
|
+
- ".github/workflows/rubocop.yml"
|
91
|
+
- ".github/workflows/test.yml"
|
90
92
|
- ".gitignore"
|
91
93
|
- ".rspec"
|
94
|
+
- ".rubocop.yml"
|
92
95
|
- ".travis.yml"
|
93
96
|
- CHANGELOG
|
94
97
|
- CODE_OF_CONDUCT.md
|
@@ -103,10 +106,15 @@ files:
|
|
103
106
|
- certs/gen.sh
|
104
107
|
- certs/key.pem
|
105
108
|
- lib/signature_dfe.rb
|
109
|
+
- lib/signature_dfe/disabling.rb
|
106
110
|
- lib/signature_dfe/evento_nfe.rb
|
107
111
|
- lib/signature_dfe/nfe.rb
|
108
112
|
- lib/signature_dfe/ssl.rb
|
113
|
+
- lib/signature_dfe/templates/signature.xml
|
114
|
+
- lib/signature_dfe/templates/signed_info.xml
|
109
115
|
- lib/signature_dfe/version.rb
|
116
|
+
- lib/signature_dfe_check.rb
|
117
|
+
- lib/signature_dfe_xml.rb
|
110
118
|
- signature_dfe.gemspec
|
111
119
|
homepage: https://github.com/thiaguerd/signature_dfe
|
112
120
|
licenses:
|
@@ -131,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
139
|
- !ruby/object:Gem::Version
|
132
140
|
version: '0'
|
133
141
|
requirements: []
|
134
|
-
rubygems_version: 3.
|
142
|
+
rubygems_version: 3.1.2
|
135
143
|
signing_key:
|
136
144
|
specification_version: 4
|
137
145
|
summary: Assinatura digital de documentos fiscais eletrônicos
|