signature_dfe 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c802eaf476623cebfcfb4142191d0497d653b10a7c3d0f7b446a94cc3804e61a
4
- data.tar.gz: 7a88f5520538835ac8cd22fa7b61c94042837d7aaeb3fce8de512d915aaf4388
3
+ metadata.gz: 187cda15b027142a73148dea40fbc77c0c50e29bf2ee52277ed8bef11bceda4e
4
+ data.tar.gz: 583dcd4b1b348b90df234d155898f5a66d3ae9fe60945c5fa3dd07ff3dd4683e
5
5
  SHA512:
6
- metadata.gz: 5d13f7aec53106b1bc3b8aa80ed9a99adcd9c9523037dd5393c9a59484666d78018015f6903d3f72acc6266c5ad3b07b1b886244175fc2e7801ff483c19f0799
7
- data.tar.gz: d1469960231b9af6fb343ac320e3a62ae60173b2d76ee4a0b3cd8405eb11a6c3a6a7e5c9a822b6c6ce5baf00bde671801748512fa15c55ae5c3f4b38dbd9de54
6
+ metadata.gz: f1fce5b56885cb6b1b3bca3206307770a20cfa9152428128ccf446ddee6cacad35829e061bafbc701053bb44f067ccbfca45bdd795d5528e2b76e1ffeae059b8
7
+ data.tar.gz: 82b77da8b4a403c5774eba82d0ba207a307bab3c90585e58115e53361965568b4db97ac034dcd7380cad4365389460e98a7aa95dd5e3202b2859b6c902c9331c
@@ -0,0 +1,12 @@
1
+ name: Rubocop
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - name: Rubocop checks
10
+ uses: gimenete/rubocop-action@1.0
11
+ env:
12
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -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
@@ -3,5 +3,5 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.6.0
7
- before_install: gem install bundler -v 1.17.3
6
+ - 2.7.0
7
+ before_install: gem install bundler -v 2.1.2
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v 0.1.3
2
+ - add support to Inutilização
1
3
  v 0.1.2
2
4
  - fix bug with pkcs12 signature
3
5
  - add event for NF-e NFA-e NFC-e
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
- source "https://rubygems.org"
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
- [![Travis CI](https://travis-ci.org/thiaguerd/signature_dfe.svg?branch=master)](https://travis-ci.org/thiaguerd/signature_dfe) [![Gem Version](https://badge.fury.io/rb/signature_dfe.svg)](https://rubygems.org/gems/signature_dfe)
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
- ## Usando
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
@@ -1,6 +1,6 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "signature_dfe"
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 "irb"
13
+ require 'irb'
14
14
  IRB.start(__FILE__)
data/lib/signature_dfe.rb CHANGED
@@ -1,20 +1,25 @@
1
- require "signature_dfe/version"
2
- require "openssl"
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
- class Error < StandardError; end
9
+ class Error < StandardError; end
6
10
 
7
- module AbstractClass
8
- def initialize
9
- raise "this is a abstract class"
10
- end
11
- end
11
+ def initialize
12
+ raise 'this is a abstract class'
13
+ end
12
14
 
13
- def self.canonize xml, canonize_method=Nokogiri::XML::XML_C14N_1_1
14
- Nokogiri::XML(xml.gsub(/>\s+</,"><")).canonicalize(canonize_method)
15
- end
15
+ module AbstractClass
16
+ def initialize
17
+ raise 'this is a abstract class'
18
+ end
19
+ end
16
20
  end
17
21
 
18
- require "signature_dfe/ssl"
19
- require "signature_dfe/nfe"
20
- require "signature_dfe/evento_nfe"
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
- class NFe
6
- class Event
7
-
8
- def self.digest_value something
9
- inf_event = something.scan(/\<infevento[\s\S]*?\<\/infevento\>/i)[0].gsub(/>\s+</,"><")
10
- inf_event_canonized = canonize_inf_event inf_event
11
- Base64.encode64(OpenSSL::Digest::SHA1.digest(inf_event_canonized)).strip
12
- end
13
-
14
- def self.signature_value event_id, digest_value
15
- signed_info = %{
16
- <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
17
- <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
18
- <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
19
- <Reference URI="#ID#{event_id}">
20
- <Transforms>
21
- <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
22
- <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
23
- </Transforms>
24
- <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
25
- <DigestValue>#{digest_value}</DigestValue>
26
- </Reference>
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
@@ -1,67 +1,24 @@
1
1
  require 'base64'
2
2
  require 'nokogiri'
3
3
  module SignatureDfe
4
- class NFe
5
- def self.sign something
6
- if something.is_a? String
7
- digest_value_ = digest_value something
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
- signature_value_ = signature_value ch_nfe, digest_value_
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
- %{<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
14
- <SignedInfo>
15
- <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
16
- <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
17
- <Reference URI="#NFe#{ch_nfe}">
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
@@ -1,79 +1,117 @@
1
1
  module SignatureDfe
2
- class Config
3
- include AbstractClass
4
-
5
- attr_accessor :pkcs12, :pkey, :cert
6
-
7
- attr_writer :password
8
-
9
- def initialize
10
- @pkcs12 = nil
11
- @pkey = nil
12
- @cert = nil
13
- @password = nil
14
- end
15
-
16
- def inspect
17
- super.gsub(/\, \@pass[\s\S]*?\>/,">")
18
- end
19
-
20
- def instance_variables
21
- super-[:@password]
22
- end
23
- end
24
-
25
- class SSL
26
- include AbstractClass
27
-
28
- @@config = Config.new
29
-
30
- def self.config
31
- @@config
32
- end
33
-
34
- def self.sign content, sign_method=OpenSSL::Digest::SHA1.new
35
- self.test unless defined?(@@pk)
36
- @@pk.sign sign_method, content
37
- end
38
-
39
- def self.cert
40
- self.test unless defined?(@@pk)
41
- @@cert.to_s.gsub(/\-\-\-\-\-[A-Z]+ CERTIFICATE\-\-\-\-\-/, "").strip
42
- end
43
-
44
- def self.test
45
- raise SignatureDfe::Error.new "You must be set up pkcs12 or pkey" if (config.pkcs12 == nil || config.pkcs12.empty?) and (config.pkey == nil || config.pkey.empty?)
46
- if config.pkcs12
47
- if File.exist? config.pkcs12
48
- begin
49
- aux = OpenSSL::PKCS12.new(File.read(config.pkcs12), config.instance_variable_get(:@password))
50
- @@pk = aux.key
51
- @@cert = aux.certificate
52
- rescue OpenSSL::PKCS12::PKCS12Error => e
53
- raise SignatureDfe::Error.new "Wrong password for '#{config.pkcs12}'"
54
- end
55
- else
56
- raise SignatureDfe::Error.new "Your pkcs12 '#{config.pkcs12}' is not a valid file"
57
- end
58
- elsif config.pkey
59
- if File.exist? config.pkey
60
- begin
61
- @@pk = OpenSSL::PKey::RSA.new File.read(config.pkey), config.instance_variable_get(:@password)
62
- begin
63
- raise SignatureDfe::Error.new "You must be set up the cert if you chose use pkey" if config.cert == nil || config.cert.empty?
64
- raise SignatureDfe::Error.new "Your cert '#{config.cert}' is not a valid file" unless File.exist? config.cert
65
- @@cert = OpenSSL::X509::Certificate.new(File.read(config.cert))
66
- rescue OpenSSL::X509::CertificateError => e
67
- raise SignatureDfe::Error.new "Your cert '#{config.cert}' is not a valid file"
68
- end
69
- rescue OpenSSL::PKey::RSAError => e
70
- raise SignatureDfe::Error.new "Wrong password for '#{config.pkey}'"
71
- end
72
- else
73
- raise SignatureDfe::Error.new "Your pkey '#{config.pkey}' is not a valid file"
74
- end
75
- end
76
- true
77
- end
78
- end
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>
@@ -1,3 +1,3 @@
1
1
  module SignatureDfe
2
- VERSION = "0.1.2"
2
+ VERSION = '0.1.3'.freeze
3
3
  end
@@ -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
@@ -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 "signature_dfe/version"
3
+ require 'signature_dfe/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "signature_dfe"
6
+ repo = 'https://github.com/thiaguerd/signature_dfe'
7
+ spec.name = 'signature_dfe'
8
8
  spec.version = SignatureDfe::VERSION
9
- spec.authors = ["Thiago Feitosa"]
10
- spec.email = ["mail@thiago.pro"]
11
-
12
- spec.summary = %q{Assinatura digital de documentos fiscais eletrônicos}
13
- spec.description = %q{Assinatura digital de NF-e NFC-e NFA-e CT-e MDF-e BP-e}
14
- spec.homepage = "https://github.com/thiaguerd/signature_dfe"
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
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
- # to allow pushing to a single host or delete this section to allow pushing to any host.
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
- spec.metadata["homepage_uri"] = spec.homepage
23
- spec.metadata["source_code_uri"] = "https://github.com/thiaguerd/signature_dfe"
24
- spec.metadata["changelog_uri"] = "https://github.com/thiaguerd/signature_dfe/blob/master/CHANGELOG"
25
- else
26
- raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
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 = ["lib"]
37
-
38
- spec.add_development_dependency "bundler", "~> 1.17"
39
- spec.add_development_dependency "rake", "~> 10.0"
40
- spec.add_development_dependency "rspec", "~> 3.0"
41
- spec.add_development_dependency "openssl", "~> 2.1.2"
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.2
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: 2019-01-07 00:00:00.000000000 Z
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: '1.17'
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: '1.17'
26
+ version: 2.1.2
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: nokogiri
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
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: '10.0'
40
+ version: 1.10.7
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: openssl
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
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: '3.0'
54
+ version: 2.1.2
55
55
  - !ruby/object:Gem::Dependency
56
- name: openssl
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 2.1.2
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: 2.1.2
68
+ version: '10.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: nokogiri
70
+ name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 1.9.1
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: 1.9.1
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.0.1
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