eassl2 2.0.0

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,55 @@
1
+ require 'openssl'
2
+ require 'eassl'
3
+ module EaSSL
4
+ # Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
5
+ # Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
6
+ # Copyright:: Copyright (c) 2006 WebPower Design
7
+ # License:: Distributes under the same terms as Ruby
8
+ class SigningRequest
9
+ def initialize(options = {})
10
+ @options = {
11
+ :name => {}, #required, CertificateName
12
+ :key => nil, #required
13
+ }.update(options)
14
+ @options[:key] ||= Key.new(@options)
15
+ end
16
+
17
+ def ssl
18
+ unless @ssl
19
+ @ssl = OpenSSL::X509::Request.new
20
+ @ssl.version = 0
21
+ @ssl.subject = CertificateName.new(@options[:name].options).name
22
+ @ssl.public_key = key.public_key
23
+ @ssl.sign(key.private_key, OpenSSL::Digest::SHA1.new)
24
+ end
25
+ @ssl
26
+ end
27
+
28
+ def key
29
+ @options[:key]
30
+ end
31
+
32
+ def to_pem
33
+ ssl.to_pem
34
+ end
35
+
36
+ # This method is used to intercept and pass-thru calls to openSSL methods and instance
37
+ # variables.
38
+ def method_missing(method)
39
+ ssl.send(method)
40
+ end
41
+
42
+ def self.load(pem_file_path)
43
+ new.load(File.read(pem_file_path))
44
+ end
45
+
46
+ def load(pem_string)
47
+ begin
48
+ @ssl = OpenSSL::X509::Request.new(pem_string)
49
+ rescue
50
+ raise "SigningRequestLoader: Error loading signing request"
51
+ end
52
+ self
53
+ end
54
+ end
55
+ end
data/lib/eassl.rb ADDED
@@ -0,0 +1,71 @@
1
+ require 'openssl'
2
+ require 'fileutils'
3
+ $:.unshift File.expand_path(File.dirname(__FILE__))
4
+ # = About EaSSL
5
+ #
6
+ # Author:: Paul Nicholson (mailto:paul@webpowerdesign.net)
7
+ # Co-Author:: Adam Williams (mailto:adam@thewilliams.ws)
8
+ # Copyright:: Copyright (c) 2006 WebPower Design
9
+ # License:: Distributes under the same terms as Ruby
10
+ #
11
+ # By requiring <tt>eassl</tt>, you can load the full set of EaSSL classes.
12
+ #
13
+ # For a full list of features and instructions, see the #README.
14
+ #
15
+ # EaSSL is a module containing all of the great EaSSL classes for creating
16
+ # and managing openSSL keys, signing request, and certificates.
17
+ #
18
+ # * EaSSL::Key: the class for loading and creating SSL keys
19
+ # * EaSSL::SigningRequest: the class for creating SSL signing requests
20
+
21
+ module EaSSL
22
+ VERSION = '2.0.0'
23
+
24
+ def self.generate_self_signed(options)
25
+ ca = CertificateAuthority.new({:bits => 1024}.update(options[:ca_options]||{}))
26
+ sr = SigningRequest.new(options)
27
+ cert = ca.create_certificate(sr)
28
+ [ca, sr, cert]
29
+ end
30
+
31
+ def self.config_webrick(webrick_config, options = {})
32
+ hostname = `hostname`.strip
33
+ eassl_host_dir = "#{File.expand_path('~')}/.eassl/#{hostname}"
34
+ ca_cert_file = "#{eassl_host_dir}/ca.crt"
35
+ ca_key_file = "#{eassl_host_dir}/ca.key"
36
+ server_key_file = "#{eassl_host_dir}/server.key"
37
+ server_cert_file = "#{eassl_host_dir}/server.crt"
38
+ FileUtils.rm_rf(eassl_host_dir) if options[:force_regeneration]
39
+
40
+ if File.exist?(server_cert_file)
41
+ key = Key.load(server_key_file, 'countinghouse1234')
42
+ cert = Certificate.load(server_cert_file)
43
+ else
44
+ ca, sr, cert = self.generate_self_signed({:name => {:common_name => hostname}, :bits => 1024}.update(options))
45
+ key = sr.key
46
+ FileUtils.makedirs(eassl_host_dir)
47
+ File.open(%(#{ca_cert_file}.pem), "w", 0777) {|f| f << ca.certificate.to_pem }
48
+ File.open(%(#{ca_cert_file}.der), "w", 0777) {|f| f << ca.certificate.to_der }
49
+ File.open(ca_key_file, "w", 0777) {|f| f << ca.key.to_pem }
50
+ File.open(server_key_file, "w", 0777) {|f| f << key.to_pem }
51
+ File.open(server_cert_file, "w", 0777) {|f| f << cert.to_pem }
52
+ end
53
+
54
+ webrick_config.update({
55
+ :SSLEnable => true,
56
+ :SSLPrivateKey => key.ssl,
57
+ :SSLCertificate => cert.ssl,
58
+ :SSLExtraChainCert => [Certificate.load(%(#{ca_cert_file}.pem)).ssl],
59
+ :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
60
+ :SSLStartImmediately => true,
61
+ })
62
+ end
63
+ end
64
+
65
+ require 'eassl/key'
66
+ require 'eassl/certificate_name'
67
+ require 'eassl/signing_request'
68
+ require 'eassl/certificate'
69
+ require 'eassl/authority_certificate'
70
+ require 'eassl/certificate_authority'
71
+ require 'eassl/serial'
@@ -0,0 +1,17 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICyzCCAjSgAwIBAgIBADANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzEO
3
+ MAwGA1UECgwFVmVuZGExEDAOBgNVBAsMB2F1dG8tQ0ExCzAJBgNVBAMMAkNBMB4X
4
+ DTExMTIwNjE3NDE1M1oXDTIxMTIwMzE3NDE1M1owPDELMAkGA1UEBhMCVVMxDjAM
5
+ BgNVBAoMBVZlbmRhMRAwDgYDVQQLDAdhdXRvLUNBMQswCQYDVQQDDAJDQTCBnzAN
6
+ BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAu8QKXQjfp9mbf8GLzBy95l4QWJspeLiv
7
+ GvYUgxDl9q3q+C37s/px8LIdDhSXp+bL0gUTzL1/DUNKoMkYZZ2Lozdlg0gp7eQ6
8
+ 1M7baDveuKeD86U1pCdBZiPIlBAUny8qxe1AvetSrLYH1RV4An68+lKKlj8o/pOQ
9
+ T6u4XnHIwNkCAwEAAaOB3DCB2TAPBgNVHRMBAf8EBTADAQH/MDEGCWCGSAGG+EIB
10
+ DQQkFiJSdWJ5L09wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW
11
+ BBT+n8Ml3oKlSBBaeaaDrWFS9THk5TAOBgNVHQ8BAf8EBAMCAQYwZAYDVR0jBF0w
12
+ W4AU/p/DJd6CpUgQWnmmg61hUvUx5OWhQKQ+MDwxCzAJBgNVBAYTAlVTMQ4wDAYD
13
+ VQQKDAVWZW5kYTEQMA4GA1UECwwHYXV0by1DQTELMAkGA1UEAwwCQ0GCAQAwDQYJ
14
+ KoZIhvcNAQEFBQADgYEABpz5uxouNMgKxVtjsiLDaD8XfpfRgM8J7H6uP9dpzZf1
15
+ GkCNWN9DPI/uTF9sXkZ9nXA8U85MX9EfgBL0E9gyIocKeGn24X32X3CtbP1fH0n1
16
+ dL2rzIwcDTHJahnkXu2icQbp59DKx1+Od/vfvQwKwZxMWrUWjzB+O8+kgKoMOlg=
17
+ -----END CERTIFICATE-----
data/test/CA/cakey.pem ADDED
@@ -0,0 +1,18 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ Proc-Type: 4,ENCRYPTED
3
+ DEK-Info: DES-EDE3-CBC,69D5C965A334A6CF
4
+
5
+ EzKzR7BN1Eiye5Qv3Dwqsqcg6L6XK2xjjBR6thfAmGolOPPkMgKDAVo6owNeMpqc
6
+ x+VyH4yQ2kKn7hHTznAb7pdyPXCHZkX+o1glJ2MpMWtXvRzQDVsC9ZA83aIYUIKI
7
+ CmzbTd7jwtTzefybkHM5TYG2L6dNHGQXb4mm2SXuH9996AIb06alHljav7SKbwuR
8
+ RmAulGPolxWTBU5LQTQO/u89NB6xpADzqli8GZzoO+76OqNqyPmDmx1E1s7GMLow
9
+ mls2hqrUNSRF78fvGDkFdM7gzpde/RFeqB6h5CRi65xUWZRgRwbIa+gBgHp+SgfD
10
+ EwgZgKS7o2bEA4RI+0cpHUQYYiyxow9vfRCaAhAWe3N7jmGa/tzH0zmQ8GkYpTE+
11
+ 8y+y8xD6qL53uPcQOtCPYUIgKlNf7Bj+z9yW94qHfLmKqT0weHHBRl+exuvXBLyg
12
+ djdtSnXZ/NpUQFoIsTricqh2E/NqAzJpY0DEJijSffGnUrPl0dwDwK2HfLoh8N4X
13
+ t2t4SBLYZIoVa/AvSBxVS4rRJldkANPmIPNVxk6aXSVsWqmb7/U1dzk1QbblZIY5
14
+ 3DpdkcDtsLLJX3eiluomkkz+d0BJ7wJBYd+G6W+LJn9aja77PwxJGlZM10ccZkg0
15
+ dglULaBkPgwitLwZV3wexOLsnw2nPtFIf1qimXYEJAfTTloaDnQHuRyiO9WLCaUn
16
+ pWDgNcVdoN6zS7Cn1duHtR0HCiQuJv5ur5F5g5BcFLpuaH1JU+x33d9w1s+EEkQB
17
+ 8Dxj59BsbmOFbTiK3Tf9Z1u0RyRbI3PGC/T7Q309vK3x+r9T2AqPog==
18
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1 @@
1
+ 000B
@@ -0,0 +1,23 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDzzCCAzigAwIBAgIBAjANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzEO
3
+ MAwGA1UECgwFVmVuZGExEDAOBgNVBAsMB2F1dG8tQ0ExCzAJBgNVBAMMAkNBMB4X
4
+ DTExMTIwNzE5MTIxN1oXDTE2MTIwNTE5MTIxN1owgakxCzAJBgNVBAYTAlVTMRcw
5
+ FQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEWMBQGA1UEBxMNRnVxdWF5IFZhcmluYTEY
6
+ MBYGA1UECgwPV2ViUG93ZXIgRGVzaWduMRUwEwYDVQQLDAxXZWIgU2VjdXJpdHkx
7
+ FDASBgNVBAMMC2Zvby5iYXIuY29tMSIwIAYJKoZIhvcNAQkBDBNlYXNzbEBydWJ5
8
+ Zm9yZ2Uub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyqWgYizb
9
+ EaafCYheeaTCGLK4FOq42e2CavOComQlWXEGR2YHYOL/cPK9Lpc+f/4qxse8SChx
10
+ 1maDuUh+iT+fNa/jqbBExmK7h914mXW2pcZCfbboND0Va9wLm63HsMVwY2FGDC9P
11
+ Qh5hviVfIoGVbC2ZDI1pt98pexPsSOSHn2ch1q4s/9pfICnWN+KsEyNJuBwlo24t
12
+ Eg+zvnVE9w3YzlSQ7NCgPFf1aX2VBWZi50gbAwoxoKyrtZFQ/tIrF6WtMxYTpfYq
13
+ LYWLMsb9+xZHkhEc+XvvipD6Y25tlyDWoFOR3sy0B5SZGoik9ZD1bTCWHdEtNRzG
14
+ cRoChZSCv9+LeQIDAQABo4HuMIHrMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMB0G
15
+ A1UdDgQWBBT6dj30hJuziSwhPx9PnsTyGCi3BjATBgNVHSUEDDAKBggrBgEFBQcD
16
+ ATA3BglghkgBhvhCAQ0EKhYoUnVieS9PcGVuU1NML0VhU1NMIEdlbmVyYXRlZCBD
17
+ ZXJ0aWZpY2F0ZTBkBgNVHSMEXTBbgBT+n8Ml3oKlSBBaeaaDrWFS9THk5aFApD4w
18
+ PDELMAkGA1UEBhMCVVMxDjAMBgNVBAoMBVZlbmRhMRAwDgYDVQQLDAdhdXRvLUNB
19
+ MQswCQYDVQQDDAJDQYIBADANBgkqhkiG9w0BAQUFAAOBgQBjN8LEARLiWjxV0o6U
20
+ XSM4ubws0pAXya34TIAQnlDKEEssZ0i1IYyyqieCkdaH+n0wnhGLwGf21yyrqCLd
21
+ +nDavx/2EBrDcF0yE7aapzXcfeXZ2gZxkZycuwc8dKR6IEXLWrMYS7HKyT490G0R
22
+ XBbgCxQiIndLwRnNMavd+vx0Wg==
23
+ -----END CERTIFICATE-----
data/test/csr.pem ADDED
@@ -0,0 +1,11 @@
1
+ -----BEGIN CERTIFICATE REQUEST-----
2
+ MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
3
+ MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
4
+ AQUAA4GNADCBiQKBgQC+RvNakUHlmlT3jMtkVx0Eajv6sxtzyk0qmSRKHU9/2q+1
5
+ 3/jUM9fnc18hDBoI9PsObJc8CueXFnOVN9fyaQQXyr/mesvYgNn+XTSkE8HWiFSP
6
+ CMD3Sc8picEFEW5G/ZDrkqmygIY9E/kk9tQmWFolfIjWCTQPe/xh0f9kK/MkYwID
7
+ AQABoAAwDQYJKoZIhvcNAQEFBQADgYEAp5Bf2vGSzAB9uhWZ3bDPmAcvFDgXRSrk
8
+ 3qlsOLDFy2uxHZxrJROo89YstwHMEDPHN2uNMpMaAfT2aiAVwQbjeu7/wQ5rnf35
9
+ LY18Mf/fqkFIqSolbHhaV3j1MvBMseAj3GidItX/HZiwzU2dSsb36o8KthkO5IX1
10
+ 9R2JzARogT0=
11
+ -----END CERTIFICATE REQUEST-----
@@ -0,0 +1,9 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ Proc-Type: 4,ENCRYPTED
3
+ DEK-Info: DES-EDE3-CBC,95157FEDE26860DF
4
+
5
+ QtQcPFoYz58qBAE1BgrhZriIF8CFvMYgK5p92fSSHt9V2ySeEuBMwLJncp4tBJGG
6
+ IbjBVK9v4VB8NxrGoC7Qs/0JI5PkMVxwUIuzRC+KAXnImRaV258t+ydboYIwnsfl
7
+ 2Do9eQonjPOWHvU1vWCQMXa/Jku9cqJnL3a7quZaGPHDW0ch/v2zPbF2LOFFJV8v
8
+ YvdYo7ml27+Zrr0rmnhF/XVtDwkQd/K0I3sXIr92fHk=
9
+ -----END RSA PRIVATE KEY-----
data/test/helper.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+
11
+ #require 'simplecov'
12
+ #SimpleCov.start
13
+
14
+ require 'test/unit'
15
+
16
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
17
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
18
+ require 'eassl'
19
+
20
+ class Test::Unit::TestCase
21
+ end
@@ -0,0 +1,33 @@
1
+ require 'helper'
2
+
3
+ class TestEassl < Test::Unit::TestCase
4
+
5
+ def test_generate_self_signed_defaults
6
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
7
+ ca, sr, cert = EaSSL.generate_self_signed(:name => name)
8
+
9
+ assert ca
10
+ assert_equal EaSSL::CertificateAuthority, ca.class
11
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=CA/emailAddress=eassl@rubyforge.org", ca.certificate.subject.to_s
12
+
13
+ assert sr
14
+ assert_equal EaSSL::SigningRequest, sr.class
15
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=foo.bar.com/emailAddress=eassl@rubyforge.org", sr.subject.to_s
16
+
17
+ assert cert
18
+ assert_equal EaSSL::Certificate, cert.class
19
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=foo.bar.com/emailAddress=eassl@rubyforge.org", cert.subject.to_s
20
+
21
+ key = sr.key
22
+ assert key
23
+ assert_equal EaSSL::Key, key.class
24
+ assert_equal 2048, key.length
25
+ end
26
+
27
+ def test_config_webrick
28
+ #webrick_config = {}
29
+ #name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
30
+ #EaSSL.config_webrick(webrick_config, :name => name)
31
+ end
32
+
33
+ end
@@ -0,0 +1,60 @@
1
+ require 'helper'
2
+
3
+ class TestEasslCertificateAuthority < Test::Unit::TestCase
4
+
5
+ def test_new_certificate
6
+ key = EaSSL::Key.new
7
+ cacert = EaSSL::AuthorityCertificate.new(:key => key)
8
+ assert cacert
9
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=CA/emailAddress=eassl@rubyforge.org", cacert.subject.to_s
10
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=CA/emailAddress=eassl@rubyforge.org", cacert.issuer.to_s
11
+ end
12
+
13
+ def test_load_certificate
14
+ cacert_path = File.join(File.dirname(__FILE__), 'CA', 'cacert.pem')
15
+ cacert = EaSSL::AuthorityCertificate.load(cacert_path)
16
+ assert cacert
17
+ assert_equal "/C=US/O=Venda/OU=auto-CA/CN=CA", cacert.subject.to_s
18
+ assert_equal "/C=US/O=Venda/OU=auto-CA/CN=CA", cacert.issuer.to_s
19
+ end
20
+
21
+ def test_certificate_from_text
22
+ cacert_text = <<CACERT
23
+ -----BEGIN CERTIFICATE-----
24
+ MIICyzCCAjSgAwIBAgIBADANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzEO
25
+ MAwGA1UECgwFVmVuZGExEDAOBgNVBAsMB2F1dG8tQ0ExCzAJBgNVBAMMAkNBMB4X
26
+ DTExMTIwNjE3NDE1M1oXDTIxMTIwMzE3NDE1M1owPDELMAkGA1UEBhMCVVMxDjAM
27
+ BgNVBAoMBVZlbmRhMRAwDgYDVQQLDAdhdXRvLUNBMQswCQYDVQQDDAJDQTCBnzAN
28
+ BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAu8QKXQjfp9mbf8GLzBy95l4QWJspeLiv
29
+ GvYUgxDl9q3q+C37s/px8LIdDhSXp+bL0gUTzL1/DUNKoMkYZZ2Lozdlg0gp7eQ6
30
+ 1M7baDveuKeD86U1pCdBZiPIlBAUny8qxe1AvetSrLYH1RV4An68+lKKlj8o/pOQ
31
+ T6u4XnHIwNkCAwEAAaOB3DCB2TAPBgNVHRMBAf8EBTADAQH/MDEGCWCGSAGG+EIB
32
+ DQQkFiJSdWJ5L09wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW
33
+ BBT+n8Ml3oKlSBBaeaaDrWFS9THk5TAOBgNVHQ8BAf8EBAMCAQYwZAYDVR0jBF0w
34
+ W4AU/p/DJd6CpUgQWnmmg61hUvUx5OWhQKQ+MDwxCzAJBgNVBAYTAlVTMQ4wDAYD
35
+ VQQKDAVWZW5kYTEQMA4GA1UECwwHYXV0by1DQTELMAkGA1UEAwwCQ0GCAQAwDQYJ
36
+ KoZIhvcNAQEFBQADgYEABpz5uxouNMgKxVtjsiLDaD8XfpfRgM8J7H6uP9dpzZf1
37
+ GkCNWN9DPI/uTF9sXkZ9nXA8U85MX9EfgBL0E9gyIocKeGn24X32X3CtbP1fH0n1
38
+ dL2rzIwcDTHJahnkXu2icQbp59DKx1+Od/vfvQwKwZxMWrUWjzB+O8+kgKoMOlg=
39
+ -----END CERTIFICATE-----
40
+ CACERT
41
+ cacert = EaSSL::AuthorityCertificate.new({}).load(cacert_text)
42
+ assert cacert
43
+ assert_equal "/C=US/O=Venda/OU=auto-CA/CN=CA", cacert.subject.to_s
44
+ assert_equal "/C=US/O=Venda/OU=auto-CA/CN=CA", cacert.issuer.to_s
45
+ end
46
+
47
+ def test_load_nonexistent_file
48
+ assert_raises Errno::ENOENT do
49
+ key = EaSSL::AuthorityCertificate.load('./foo')
50
+ end
51
+ end
52
+
53
+ def test_load_bad_file
54
+ file = File.join(File.dirname(__FILE__), '..', 'Rakefile')
55
+ assert_raises RuntimeError do
56
+ key = EaSSL::AuthorityCertificate.load(file)
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,109 @@
1
+ require 'helper'
2
+
3
+ class TestEasslCertificate < Test::Unit::TestCase
4
+
5
+ def test_new_certificate_self_signed
6
+ key = EaSSL::Key.new
7
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
8
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
9
+
10
+ cert = EaSSL::Certificate.new(:signing_request => csr)
11
+ assert cert
12
+ assert cert.ssl
13
+ assert_equal cert.subject.to_s, csr.subject.to_s
14
+ assert_equal cert.subject.to_s, cert.issuer.to_s
15
+ end
16
+
17
+ def test_certificate_to_pem
18
+ key = EaSSL::Key.new
19
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
20
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
21
+
22
+ cert = EaSSL::Certificate.new(:signing_request => csr)
23
+ assert cert.to_pem =~ /BEGIN CERTIFICATE/
24
+ end
25
+
26
+ def test_new_server_certificate_ca_signed
27
+ ca_path = File.join(File.dirname(__FILE__), 'CA')
28
+ ca = EaSSL::CertificateAuthority.load(:ca_path => ca_path, :ca_password => '1234')
29
+ key = EaSSL::Key.new
30
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
31
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
32
+
33
+ cert = EaSSL::Certificate.new(:signing_request => csr, :ca_certificate => ca.certificate)
34
+ cert.sign(ca.key)
35
+ assert cert.to_pem =~ /BEGIN CERTIFICATE/
36
+ assert_equal cert.subject.to_s, csr.subject.to_s
37
+ assert_equal cert.issuer.to_s, ca.certificate.subject.to_s
38
+ ext_key_usage = cert.extensions.select {|e| e.oid == 'extendedKeyUsage' }
39
+ assert_equal "TLS Web Server Authentication", ext_key_usage[0].value
40
+ end
41
+
42
+ def test_new_client_certificate_ca_signed
43
+ ca_path = File.join(File.dirname(__FILE__), 'CA')
44
+ ca = EaSSL::CertificateAuthority.load(:ca_path => ca_path, :ca_password => '1234')
45
+ key = EaSSL::Key.new
46
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
47
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
48
+
49
+ cert = EaSSL::Certificate.new(:type => 'client', :signing_request => csr, :ca_certificate => ca.certificate)
50
+ cert.sign(ca.key)
51
+ assert cert.to_pem =~ /BEGIN CERTIFICATE/
52
+ assert_equal cert.subject.to_s, csr.subject.to_s
53
+ assert_equal cert.issuer.to_s, ca.certificate.subject.to_s
54
+ ext_key_usage = cert.extensions.select {|e| e.oid == 'extendedKeyUsage' }
55
+ assert_equal "TLS Web Client Authentication, E-mail Protection", ext_key_usage[0].value
56
+ end
57
+
58
+ def test_load_certificate_file
59
+ file = File.join(File.dirname(__FILE__), 'certificate.pem')
60
+ cert = EaSSL::Certificate.load(file)
61
+ assert cert
62
+ assert_equal "55:27:E8:46:50:03:39:F4:A3:24:3D:88:57:BA:67:5C:F1:E8:84:1D", cert.sha1_fingerprint
63
+ end
64
+
65
+ def test_load_certificate_text
66
+ cert_text = <<CERT
67
+ -----BEGIN CERTIFICATE-----
68
+ MIIDzzCCAzigAwIBAgIBAjANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzEO
69
+ MAwGA1UECgwFVmVuZGExEDAOBgNVBAsMB2F1dG8tQ0ExCzAJBgNVBAMMAkNBMB4X
70
+ DTExMTIwNzE5MTIxN1oXDTE2MTIwNTE5MTIxN1owgakxCzAJBgNVBAYTAlVTMRcw
71
+ FQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEWMBQGA1UEBxMNRnVxdWF5IFZhcmluYTEY
72
+ MBYGA1UECgwPV2ViUG93ZXIgRGVzaWduMRUwEwYDVQQLDAxXZWIgU2VjdXJpdHkx
73
+ FDASBgNVBAMMC2Zvby5iYXIuY29tMSIwIAYJKoZIhvcNAQkBDBNlYXNzbEBydWJ5
74
+ Zm9yZ2Uub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyqWgYizb
75
+ EaafCYheeaTCGLK4FOq42e2CavOComQlWXEGR2YHYOL/cPK9Lpc+f/4qxse8SChx
76
+ 1maDuUh+iT+fNa/jqbBExmK7h914mXW2pcZCfbboND0Va9wLm63HsMVwY2FGDC9P
77
+ Qh5hviVfIoGVbC2ZDI1pt98pexPsSOSHn2ch1q4s/9pfICnWN+KsEyNJuBwlo24t
78
+ Eg+zvnVE9w3YzlSQ7NCgPFf1aX2VBWZi50gbAwoxoKyrtZFQ/tIrF6WtMxYTpfYq
79
+ LYWLMsb9+xZHkhEc+XvvipD6Y25tlyDWoFOR3sy0B5SZGoik9ZD1bTCWHdEtNRzG
80
+ cRoChZSCv9+LeQIDAQABo4HuMIHrMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMB0G
81
+ A1UdDgQWBBT6dj30hJuziSwhPx9PnsTyGCi3BjATBgNVHSUEDDAKBggrBgEFBQcD
82
+ ATA3BglghkgBhvhCAQ0EKhYoUnVieS9PcGVuU1NML0VhU1NMIEdlbmVyYXRlZCBD
83
+ ZXJ0aWZpY2F0ZTBkBgNVHSMEXTBbgBT+n8Ml3oKlSBBaeaaDrWFS9THk5aFApD4w
84
+ PDELMAkGA1UEBhMCVVMxDjAMBgNVBAoMBVZlbmRhMRAwDgYDVQQLDAdhdXRvLUNB
85
+ MQswCQYDVQQDDAJDQYIBADANBgkqhkiG9w0BAQUFAAOBgQBjN8LEARLiWjxV0o6U
86
+ XSM4ubws0pAXya34TIAQnlDKEEssZ0i1IYyyqieCkdaH+n0wnhGLwGf21yyrqCLd
87
+ +nDavx/2EBrDcF0yE7aapzXcfeXZ2gZxkZycuwc8dKR6IEXLWrMYS7HKyT490G0R
88
+ XBbgCxQiIndLwRnNMavd+vx0Wg==
89
+ -----END CERTIFICATE-----
90
+ CERT
91
+ cert = EaSSL::Certificate.new({}).load(cert_text)
92
+ assert cert
93
+ assert_equal "55:27:E8:46:50:03:39:F4:A3:24:3D:88:57:BA:67:5C:F1:E8:84:1D", cert.sha1_fingerprint
94
+ end
95
+
96
+ def test_load_nonexistent_file
97
+ assert_raises Errno::ENOENT do
98
+ key = EaSSL::Certificate.load('./foo')
99
+ end
100
+ end
101
+
102
+ def test_load_bad_file
103
+ file = File.join(File.dirname(__FILE__), '..', 'Rakefile')
104
+ assert_raises RuntimeError do
105
+ key = EaSSL::Certificate.load(file)
106
+ end
107
+ end
108
+
109
+ end
@@ -0,0 +1,126 @@
1
+ require 'helper'
2
+
3
+ class TestEasslCertificateAuthority < Test::Unit::TestCase
4
+
5
+ def test_new_ca
6
+ ca = EaSSL::CertificateAuthority.new
7
+ assert ca
8
+ assert ca.key
9
+ assert ca.certificate
10
+
11
+ assert_equal 2048, ca.key.length
12
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=CA/emailAddress=eassl@rubyforge.org", ca.certificate.subject.to_s
13
+ end
14
+
15
+ def test_load_ca
16
+ ca_path = File.join(File.dirname(__FILE__), 'CA')
17
+ ca = EaSSL::CertificateAuthority.load(:ca_path => ca_path, :ca_password => '1234')
18
+ assert ca
19
+ assert ca.key
20
+ assert ca.certificate
21
+
22
+ assert_equal 1024, ca.key.length
23
+ assert_equal "/C=US/O=Venda/OU=auto-CA/CN=CA", ca.certificate.subject.to_s
24
+ end
25
+
26
+ def test_new_ca_specified_name
27
+ ca = EaSSL::CertificateAuthority.new(:name => {
28
+ :country => 'GB',
29
+ :state => 'London',
30
+ :city => 'London',
31
+ :organization => 'Venda Ltd',
32
+ :department => 'Development',
33
+ :common_name => 'CA',
34
+ :email => 'dev@venda.com'
35
+ })
36
+ key = EaSSL::Key.new
37
+ name = EaSSL::CertificateName.new(
38
+ :country => 'GB',
39
+ :state => 'London',
40
+ :city => 'London',
41
+ :organization => 'Venda Ltd',
42
+ :department => 'Development',
43
+ :common_name => 'foo.bar.com',
44
+ :email => 'dev@venda.com'
45
+ )
46
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
47
+ cert = ca.create_certificate(csr)
48
+ assert cert
49
+ assert_equal "/C=GB/ST=London/L=London/O=Venda Ltd/OU=Development/CN=foo.bar.com/emailAddress=dev@venda.com", cert.subject.to_s
50
+ assert_equal "/C=GB/ST=London/L=London/O=Venda Ltd/OU=Development/CN=CA/emailAddress=dev@venda.com", cert.issuer.to_s
51
+ ext_key_usage = cert.extensions.select {|e| e.oid == 'extendedKeyUsage' }
52
+ assert_equal "TLS Web Server Authentication", ext_key_usage[0].value
53
+ end
54
+
55
+ def test_new_ca_sign_cert
56
+ ca = EaSSL::CertificateAuthority.new
57
+ key = EaSSL::Key.new
58
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
59
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
60
+ cert = ca.create_certificate(csr)
61
+ assert cert
62
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=foo.bar.com/emailAddress=eassl@rubyforge.org", cert.subject.to_s
63
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=CA/emailAddress=eassl@rubyforge.org", cert.issuer.to_s
64
+ ext_key_usage = cert.extensions.select {|e| e.oid == 'extendedKeyUsage' }
65
+ assert_equal "TLS Web Server Authentication", ext_key_usage[0].value
66
+ end
67
+
68
+ def test_new_ca_sign_client_cert
69
+ ca = EaSSL::CertificateAuthority.new
70
+ key = EaSSL::Key.new
71
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
72
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
73
+ cert = ca.create_certificate(csr, 'client')
74
+ assert cert
75
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=foo.bar.com/emailAddress=eassl@rubyforge.org", cert.subject.to_s
76
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=CA/emailAddress=eassl@rubyforge.org", cert.issuer.to_s
77
+ ext_key_usage = cert.extensions.select {|e| e.oid == 'extendedKeyUsage' }
78
+ assert_equal "TLS Web Client Authentication, E-mail Protection", ext_key_usage[0].value
79
+ end
80
+
81
+ def test_new_ca_sign_client_cert_with_expiry
82
+ ca = EaSSL::CertificateAuthority.new
83
+ key = EaSSL::Key.new
84
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
85
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
86
+ t = Time.now
87
+ cert = ca.create_certificate(csr, 'client', 10)
88
+ assert cert
89
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=foo.bar.com/emailAddress=eassl@rubyforge.org", cert.subject.to_s
90
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=CA/emailAddress=eassl@rubyforge.org", cert.issuer.to_s
91
+ ext_key_usage = cert.extensions.select {|e| e.oid == 'extendedKeyUsage' }
92
+ assert_equal "TLS Web Client Authentication, E-mail Protection", ext_key_usage[0].value
93
+ assert_equal (t + (24 * 60 * 60 * 10)).to_i, cert.ssl.not_after.to_i
94
+ end
95
+
96
+ def test_loaded_ca_sign_cert
97
+ ca_path = File.join(File.dirname(__FILE__), 'CA')
98
+ ca = EaSSL::CertificateAuthority.load(:ca_path => ca_path, :ca_password => '1234')
99
+ key = EaSSL::Key.new
100
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
101
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
102
+ cert = ca.create_certificate(csr)
103
+ assert cert
104
+ assert_equal "/C=US/ST=North Carolina/L=Fuquay Varina/O=WebPower Design/OU=Web Security/CN=foo.bar.com/emailAddress=eassl@rubyforge.org", cert.subject.to_s
105
+ assert_equal "/C=US/O=Venda/OU=auto-CA/CN=CA", cert.issuer.to_s
106
+ end
107
+
108
+ def test_loaded_ca_sign_certs_with_serial
109
+ ca_path = File.join(File.dirname(__FILE__), 'CA')
110
+ ca = EaSSL::CertificateAuthority.load(:ca_path => ca_path, :ca_password => '1234')
111
+
112
+ next_serial = ca.serial.next
113
+
114
+ key = EaSSL::Key.new
115
+ name = EaSSL::CertificateName.new(:common_name => 'foo.bar.com')
116
+ csr = EaSSL::SigningRequest.new(:name => name, :key => key)
117
+ cert = ca.create_certificate(csr)
118
+ assert cert
119
+ assert cert.serial.to_i == next_serial
120
+ assert ca.serial.next == next_serial + 1
121
+
122
+ ca = EaSSL::CertificateAuthority.load(:ca_path => ca_path, :ca_password => '1234')
123
+ assert ca.serial.next == next_serial + 1
124
+ end
125
+
126
+ end
@@ -0,0 +1,106 @@
1
+ require 'helper'
2
+
3
+ class TestEasslKey < Test::Unit::TestCase
4
+
5
+ def test_new_keys_ssl
6
+ key = EaSSL::Key.new
7
+ assert key.ssl
8
+ assert_equal OpenSSL::PKey::RSA, key.ssl.class
9
+ end
10
+
11
+ def test_new_keys_private_key
12
+ key = EaSSL::Key.new
13
+ assert key.private_key
14
+ assert_equal OpenSSL::PKey::RSA, key.private_key.class
15
+ end
16
+
17
+ def test_new_key_defaults_bit_length
18
+ key = EaSSL::Key.new
19
+ assert_equal 2048, key.length
20
+ end
21
+
22
+ def test_new_key_defaults_password
23
+ key = EaSSL::Key.new
24
+ enckey = key.to_pem
25
+ key2 = OpenSSL::PKey::RSA::new(enckey, 'ssl_password')
26
+ assert_equal key2.to_s, key.ssl.to_s
27
+ end
28
+
29
+ def test_override_bit_length
30
+ key = EaSSL::Key.new(:bits => 1024)
31
+ assert_equal 1024, key.length
32
+ end
33
+
34
+ def test_override_password
35
+ key = EaSSL::Key.new(:password => 'xyzzy')
36
+ enckey = key.to_pem
37
+ key2 = OpenSSL::PKey::RSA::new(enckey, 'xyzzy')
38
+ assert_equal key2.to_s, key.ssl.to_s
39
+ end
40
+
41
+ def test_to_pem_string
42
+ key = EaSSL::Key.new(:password => 'xyzzy')
43
+ enckey = key.to_pem
44
+ assert_equal String, enckey.class
45
+ assert enckey =~ /BEGIN RSA PRIVATE KEY/
46
+ assert enckey =~ /ENCRYPTED/
47
+ end
48
+
49
+ def test_load_encrypted_key_text
50
+ key_text = <<KEY
51
+ -----BEGIN RSA PRIVATE KEY-----
52
+ Proc-Type: 4,ENCRYPTED
53
+ DEK-Info: DES-EDE3-CBC,95157FEDE26860DF
54
+
55
+ QtQcPFoYz58qBAE1BgrhZriIF8CFvMYgK5p92fSSHt9V2ySeEuBMwLJncp4tBJGG
56
+ IbjBVK9v4VB8NxrGoC7Qs/0JI5PkMVxwUIuzRC+KAXnImRaV258t+ydboYIwnsfl
57
+ 2Do9eQonjPOWHvU1vWCQMXa/Jku9cqJnL3a7quZaGPHDW0ch/v2zPbF2LOFFJV8v
58
+ YvdYo7ml27+Zrr0rmnhF/XVtDwkQd/K0I3sXIr92fHk=
59
+ -----END RSA PRIVATE KEY-----
60
+ KEY
61
+ key = EaSSL::Key.new.load(key_text, 'ssl_password')
62
+ assert key
63
+ assert_equal 256, key.length
64
+ end
65
+
66
+ def test_load_encrypted_key_file
67
+ file = File.join(File.dirname(__FILE__), 'encrypted_key.pem')
68
+ key = EaSSL::Key.load(file, 'ssl_password')
69
+ assert key
70
+ assert_equal 256, key.length
71
+ end
72
+
73
+ def test_load_unencrypted_key_text
74
+ key_text = <<KEY
75
+ -----BEGIN RSA PRIVATE KEY-----
76
+ MIGsAgEAAiEAy57X7ZFkqicM+Nb9kOjCBs0Fz3dc3F3nhqx9cDnwHaMCAwEAAQIh
77
+ ALOYKsOzVaJuRxbEKWpCob5hIpOCJqwmdA9cFbrEv9zhAhEA/B/sb8dzCvaFM/p5
78
+ Bt6Y7QIRAM7AD/gt+xiWUH8z+ra7js8CEQCXelqkofFloc1P+GnkjbLVAhAriPXT
79
+ 5JrDCqPYpTFd2RCxAhEA+WMGuSLXT3xK5XP/LHIiVg==
80
+ -----END RSA PRIVATE KEY-----
81
+ KEY
82
+ key = EaSSL::Key.new.load(key_text)
83
+ assert key
84
+ assert_equal 256, key.length
85
+ end
86
+
87
+ def test_load_unencrypted_key_file
88
+ file = File.join(File.dirname(__FILE__), 'unencrypted_key.pem')
89
+ key = EaSSL::Key.load(file)
90
+ assert key
91
+ assert_equal 256, key.length
92
+ end
93
+
94
+ def test_load_nonexistent_file
95
+ assert_raises Errno::ENOENT do
96
+ key = EaSSL::Key.load('./foo')
97
+ end
98
+ end
99
+
100
+ def test_load_bad_file
101
+ file = File.join(File.dirname(__FILE__), '..', 'Rakefile')
102
+ assert_raises RuntimeError do
103
+ key = EaSSL::Key.load(file)
104
+ end
105
+ end
106
+ end