firma 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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