firma 0.0.1

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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Bruno Aguirre
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+ # Firma
2
+ ![](http://www.duhaime.org/Portals/duhaime/images/Copperman-signature.jpg)
3
+
4
+ Add a secure signature to a pdf file.
5
+
6
+ ## Check if a pdf it's signed
7
+
8
+ ```ruby
9
+ require "firma"
10
+
11
+ Firma.is_signed?("my_file.pdf")
12
+ ```
13
+
14
+ ## Sign a pdf file
15
+
16
+ ```ruby
17
+ require "firma"
18
+
19
+ Firma.sign("my_file.pdf",
20
+ key: "key.pem",
21
+ passphrase: "passphrase",
22
+ certificate: "key.crt"
23
+ )
24
+ ```
25
+
26
+ ## Sign a pdf file with newly generated certificates
27
+
28
+ ```ruby
29
+ require "firma"
30
+
31
+ keys = Firma.generate_keys("passphrase")
32
+
33
+ Firma.sign("my_file.pdf",
34
+ key: keys.fetch(:key).path,
35
+ passphrase: "passphrase",
36
+ certificate: keys.fetch(:certificate)
37
+ )
38
+ ```
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new("spec") do |t|
4
+ t.pattern = "test/**/*_test.rb"
5
+ end
6
+
7
+ task :default => [:test]
8
+ task :test => [:spec]
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "firma"
3
+ s.version = "0.0.1"
4
+ s.summary = "Sign PDF documents the easy way"
5
+ s.description = "Sign PDF documents"
6
+ s.authors = ["elcuervo"]
7
+ s.email = ["yo@brunoaguirre.com"]
8
+ s.homepage = "http://github.com/elcuervo/subskribas"
9
+ s.files = `git ls-files`.split("\n")
10
+ s.test_files = `git ls-files test`.split("\n")
11
+
12
+ s.add_dependency("origami", "~> 1.2.4")
13
+
14
+ s.add_development_dependency("minitest", "~> 3.5.0")
15
+ end
@@ -0,0 +1,80 @@
1
+ require "openssl"
2
+ require "tmpdir"
3
+ require "origami"
4
+
5
+ class Firma < Struct.new(:file)
6
+ def sign(options)
7
+ key = OpenSSL::PKey::RSA.new(
8
+ File.open(options.fetch(:key)),
9
+ options.fetch(:passphrase)
10
+ )
11
+
12
+ certificate = OpenSSL::X509::Certificate.new(
13
+ File.open(options.fetch(:certificate))
14
+ )
15
+
16
+ pdf = Origami::PDF.read(file)
17
+
18
+ pdf.sign(certificate, key)
19
+ pdf.save(file)
20
+ end
21
+
22
+ def is_signed?
23
+ Origami::PDF.read(file).is_signed?
24
+ end
25
+
26
+ class << self
27
+ def is_signed?(file)
28
+ new(file).is_signed?
29
+ end
30
+
31
+ def sign(file, options = {})
32
+ new(file).sign(options)
33
+ end
34
+
35
+ def create_certificate_from_key(key)
36
+ name = "CN=ruby/DC=subskribas"
37
+ certificate = OpenSSL::X509::Certificate.new
38
+
39
+ certificate.version = 2
40
+ certificate.serial = 0
41
+ certificate.not_before = Time.now
42
+ certificate.not_after = Time.now + 3600
43
+ certificate.public_key = key.public_key
44
+ certificate.subject = OpenSSL::X509::Name.parse(name)
45
+
46
+ create_temp_file(["certificate", ".crt"], certificate.to_pem)
47
+ end
48
+
49
+ def generate_keys(passphrase)
50
+ rsa_key = OpenSSL::PKey::RSA.new(2048)
51
+
52
+ key = create_temp_file(["key", ".pem"], rsa_key.to_pem)
53
+
54
+ public_key = create_temp_file(
55
+ ["public_key", ".pem"],
56
+ rsa_key.public_key.to_pem
57
+ )
58
+
59
+ cipher = OpenSSL::Cipher::Cipher.new("AES-128-CBC")
60
+ secure_key = rsa_key.export(cipher, passphrase)
61
+
62
+ private_key = create_temp_file(["private_key", ".pem"], secure_key)
63
+
64
+ {
65
+ key: key,
66
+ public_key: public_key,
67
+ private_key: private_key,
68
+ certificate: create_certificate_from_key(rsa_key)
69
+ }
70
+ end
71
+
72
+ def create_temp_file(name, content)
73
+ filename, extension = name
74
+ random = (0...8).map{65.+(rand(25)).chr}.join
75
+ temp_name = "#{Dir.tmpdir}/#{filename + random + extension}"
76
+
77
+ File.open(temp_name, "w") { |io| io << content }
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,38 @@
1
+ require_relative "test_helper"
2
+
3
+ describe Firma do
4
+ before do
5
+ @pdf_file = sample_pdf_file
6
+ end
7
+
8
+ it "should validate if a pdf is signed" do
9
+ assert !Firma.is_signed?(@pdf_file)
10
+ assert Firma.is_signed?(sample_signed_pdf_file)
11
+ end
12
+
13
+ it "should sign a pdf file with a given keys" do
14
+ assert !Firma.is_signed?(@pdf_file)
15
+
16
+ Firma.sign(@pdf_file,
17
+ key: sample_key,
18
+ passphrase: "passphrase",
19
+ certificate: sample_certificate
20
+ )
21
+
22
+ assert Firma.is_signed?(@pdf_file)
23
+ end
24
+
25
+ it "should sign a pdf with a generated keys" do
26
+ assert !Firma.is_signed?(@pdf_file)
27
+
28
+ keys = Firma.generate_keys("passphrase")
29
+
30
+ Firma.sign(@pdf_file,
31
+ key: keys.fetch(:key).path,
32
+ passphrase: "passphrase",
33
+ certificate: keys.fetch(:certificate)
34
+ )
35
+
36
+ assert Firma.is_signed?(@pdf_file)
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICATCCAWoCCQDk30F2t8GBBTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
3
+ VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
4
+ cyBQdHkgTHRkMB4XDTEyMTAwMjAxMzEzN1oXDTEzMTAwMjAxMzEzN1owRTELMAkG
5
+ A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
6
+ IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwtBT
7
+ PNEg8XLuw2+PqSzVO79HTADFDhc+nKbwaOk7bInxLBGN2Jz2Sgpa2eytNuyAy+8U
8
+ BZ2YCLOyBV62JbfXSWJUexR9x4CH3WNdmKtgX+IxccPMk84Ujy9el5XXWAHfPaOJ
9
+ tSL/IDCaJNeooa6UM69Dpe7DKhrT9ZeVPebQcn8CAwEAATANBgkqhkiG9w0BAQUF
10
+ AAOBgQAzYa+7KxK8nYPI6PXN+gjuBTqYW8TQerNMehbz9f4+qLPRSfI5zq2zCatx
11
+ kbAPgxZdFja2hqXAQ1zMLNv9odb8B3JARPK8EFOC2mJAs4l6XQPq2MUV0jn36I2V
12
+ B9mPkHpgsjVrMqltgcSwRcQn/m1MBYn2wMb6HaqOJ11o7VFdzw==
13
+ -----END CERTIFICATE-----
@@ -0,0 +1,11 @@
1
+ -----BEGIN CERTIFICATE REQUEST-----
2
+ MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEh
3
+ MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
4
+ AQUAA4GNADCBiQKBgQDC0FM80SDxcu7Db4+pLNU7v0dMAMUOFz6cpvBo6TtsifEs
5
+ EY3YnPZKClrZ7K027IDL7xQFnZgIs7IFXrYlt9dJYlR7FH3HgIfdY12Yq2Bf4jFx
6
+ w8yTzhSPL16XlddYAd89o4m1Iv8gMJok16ihrpQzr0Ol7sMqGtP1l5U95tByfwID
7
+ AQABoAAwDQYJKoZIhvcNAQEFBQADgYEAuDxHCppoAdb8ftye7xLy0bEBt/RKxxs2
8
+ tVzEk7mrS7k1HaHMIC+fKbBug13zzaGC4qbXhr2hSdPjPqWieQMPE+PyIYJtAHlG
9
+ b/jXJiwJqXdozevmCE/1WU3IM14koQyjaEf+deq4Ej9wx90ZPmIQXcGBB+kWv1FA
10
+ 6jLZRhnURfo=
11
+ -----END CERTIFICATE REQUEST-----
@@ -0,0 +1,18 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ Proc-Type: 4,ENCRYPTED
3
+ DEK-Info: DES-EDE3-CBC,F1A77E40ED97682B
4
+
5
+ JP5nqe5fUf1sgKpGVsGbls6y5z812L+eLo2XtoddA1pM08zYIfwM71Dfe//8pJcn
6
+ S7uopiNgk5QgD4x0/F/0BLXK+EpXO8PIkSv2XK8vCUz8cgHedNL1HGDy9kCfrTDO
7
+ Y2i0YwAHcqz58fMCmwl16p9Zsrp3NOkvqozS3fs0o32w8M4Re5B3SaFfBYvBEGsF
8
+ 42bBKcsjPL4x8+HSd/iCnQUsagqJRcxnRUM6huXiE1Cm4pwpEqnSR1A84mu1DQy4
9
+ pjh3PUphmidYzsBCdLKNCuSk+HSYUVbCQfiY9j/lYu8DiPhRT9ihHlcUcm3z9fTo
10
+ kTbTm3TAq04lpDfLKQM6N1kjgx3snbgKWPtXHYO25aOGFCLnYctUqd2q6Or2c/a+
11
+ WEWbIZJvM2dm6dvFPknCQDLb63t4NdLolP3lKjy1shkPEAXpxUlCWfZ52ekNj3ve
12
+ jPvoFVMRkpKuHM5NzgRVf7Y0O8e6yQX6pdq+xvKrFs1mNd9XokSKsUpUlDvvgJk/
13
+ nTz60aOvIINZIkeBltqQqPY5r5wBe0ZzLoqfiNca84ZGHnoiVLtHJ9S/5ULjdfxX
14
+ 6lMIvA9Eu+zF4U8VxJ6Y99dZDISop9M9o5gzyXbW4vrKWqThjJAL1OHUz4Y/CEH0
15
+ USjDNNBMAC66tmczKXhR5+Aa21B3phldDiXJWOYyKmNzx1zxq+E8GloxbdPZGLKW
16
+ K0HBlR2GUVtn1vmgENQjz+XhcJH3OcQPiFU66pDNJ285W+0tBMazP0BGs3+65cdq
17
+ g75tF0TWfMG4NfdGm/XSFFGHZ4X0FhU3BrxzDC+sGQsMMyy2igqv8g==
18
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,25 @@
1
+ $: << File.expand_path(File.dirname(__FILE__), "../lib")
2
+
3
+ require "minitest/autorun"
4
+ require "minitest/pride"
5
+ require "tempfile"
6
+ require "firma"
7
+
8
+ def sample_pdf_file
9
+ sample_path = "test/fixtures/sample_pdf.pdf"
10
+ file = Tempfile.new(["tempfile", ".pdf"]) << File.open(sample_path).read
11
+
12
+ file.path
13
+ end
14
+
15
+ def sample_signed_pdf_file
16
+ "test/fixtures/signed_pdf.pdf"
17
+ end
18
+
19
+ def sample_key
20
+ "test/fixtures/sample.key"
21
+ end
22
+
23
+ def sample_certificate
24
+ "test/fixtures/sample.crt"
25
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: firma
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - elcuervo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: origami
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.4
30
+ - !ruby/object:Gem::Dependency
31
+ name: minitest
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 3.5.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.5.0
46
+ description: Sign PDF documents
47
+ email:
48
+ - yo@brunoaguirre.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - LICENSE.md
54
+ - README.md
55
+ - Rakefile
56
+ - firma.gemspec
57
+ - lib/firma.rb
58
+ - test/firma_test.rb
59
+ - test/fixtures/sample.crt
60
+ - test/fixtures/sample.csr
61
+ - test/fixtures/sample.key
62
+ - test/fixtures/sample_pdf.pdf
63
+ - test/fixtures/signed_pdf.pdf
64
+ - test/test_helper.rb
65
+ homepage: http://github.com/elcuervo/subskribas
66
+ licenses: []
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 1.8.23
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Sign PDF documents the easy way
89
+ test_files:
90
+ - test/firma_test.rb
91
+ - test/fixtures/sample.crt
92
+ - test/fixtures/sample.csr
93
+ - test/fixtures/sample.key
94
+ - test/fixtures/sample_pdf.pdf
95
+ - test/fixtures/signed_pdf.pdf
96
+ - test/test_helper.rb