firma 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.md +20 -0
- data/README.md +38 -0
- data/Rakefile +8 -0
- data/firma.gemspec +15 -0
- data/lib/firma.rb +80 -0
- data/test/firma_test.rb +38 -0
- data/test/fixtures/sample.crt +13 -0
- data/test/fixtures/sample.csr +11 -0
- data/test/fixtures/sample.key +18 -0
- data/test/fixtures/sample_pdf.pdf +0 -0
- data/test/fixtures/signed_pdf.pdf +0 -0
- data/test/test_helper.rb +25 -0
- metadata +96 -0
data/LICENSE.md
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
+
```
|
data/Rakefile
ADDED
data/firma.gemspec
ADDED
@@ -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
|
data/lib/firma.rb
ADDED
@@ -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
|
data/test/firma_test.rb
ADDED
@@ -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-----
|
Binary file
|
Binary file
|
data/test/test_helper.rb
ADDED
@@ -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
|