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 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