eassl2 2.0.0

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