2011-01-28 18:46:47 -05:00
|
|
|
require 'rubygems/test_case'
|
2011-01-18 19:08:49 -05:00
|
|
|
require 'rubygems/security'
|
2011-03-01 04:41:32 -05:00
|
|
|
require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2013-09-14 04:59:02 -04:00
|
|
|
unless defined?(OpenSSL::SSL) then
|
|
|
|
warn 'Skipping Gem::Security tests. openssl not found.'
|
|
|
|
end
|
|
|
|
|
2011-01-28 18:46:47 -05:00
|
|
|
class TestGemSecurity < Gem::TestCase
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
CHILD_KEY = load_key 'child'
|
|
|
|
|
|
|
|
ALTERNATE_CERT = load_cert 'child'
|
|
|
|
CHILD_CERT = load_cert 'child'
|
|
|
|
EXPIRED_CERT = load_cert 'expired'
|
|
|
|
|
2012-04-18 19:14:26 -04:00
|
|
|
def setup
|
|
|
|
super
|
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
@SEC = Gem::Security
|
2012-04-18 19:14:26 -04:00
|
|
|
end
|
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
def test_class_create_cert
|
|
|
|
name = PUBLIC_CERT.subject
|
|
|
|
key = PRIVATE_KEY
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
cert = @SEC.create_cert name, key, 60, Gem::Security::EXTENSIONS, 5
|
2011-01-18 19:08:49 -05:00
|
|
|
|
|
|
|
assert_kind_of OpenSSL::X509::Certificate, cert
|
|
|
|
|
|
|
|
assert_equal 2, cert.version
|
2012-11-29 01:52:18 -05:00
|
|
|
assert_equal 5, cert.serial
|
2011-01-18 19:08:49 -05:00
|
|
|
assert_equal key.public_key.to_pem, cert.public_key.to_pem
|
|
|
|
assert_in_delta Time.now, cert.not_before, 10
|
|
|
|
assert_in_delta Time.now + 60, cert.not_after, 10
|
|
|
|
assert_equal name.to_s, cert.subject.to_s
|
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
assert_equal 3, cert.extensions.length,
|
|
|
|
cert.extensions.map { |e| e.to_a.first }
|
2011-01-18 19:08:49 -05:00
|
|
|
|
|
|
|
constraints = cert.extensions.find { |ext| ext.oid == 'basicConstraints' }
|
|
|
|
assert_equal 'CA:FALSE', constraints.value
|
|
|
|
|
|
|
|
key_usage = cert.extensions.find { |ext| ext.oid == 'keyUsage' }
|
|
|
|
assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
|
|
|
|
key_usage.value
|
|
|
|
|
|
|
|
key_ident = cert.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
|
|
|
|
assert_equal 59, key_ident.value.length
|
2012-12-08 01:01:49 -05:00
|
|
|
assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
|
2012-11-29 01:52:18 -05:00
|
|
|
key_ident.value
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
assert_equal '', cert.issuer.to_s
|
2011-01-18 19:08:49 -05:00
|
|
|
assert_equal name.to_s, cert.subject.to_s
|
|
|
|
end
|
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
def test_class_create_cert_self_signed
|
|
|
|
subject = PUBLIC_CERT.subject
|
|
|
|
|
|
|
|
cert = @SEC.create_cert_self_signed subject, PRIVATE_KEY, 60
|
|
|
|
|
|
|
|
assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_create_cert_email
|
2011-01-18 19:08:49 -05:00
|
|
|
email = 'nobody@example'
|
2012-11-29 01:52:18 -05:00
|
|
|
name = PUBLIC_CERT.subject
|
|
|
|
key = PRIVATE_KEY
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
cert = @SEC.create_cert_email email, key, 60
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
assert_kind_of OpenSSL::X509::Certificate, cert
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
assert_equal 2, cert.version
|
|
|
|
assert_equal 1, cert.serial
|
|
|
|
assert_equal key.public_key.to_pem, cert.public_key.to_pem
|
|
|
|
assert_in_delta Time.now, cert.not_before, 10
|
|
|
|
assert_in_delta Time.now + 60, cert.not_after, 10
|
|
|
|
assert_equal name.to_s, cert.subject.to_s
|
|
|
|
assert_equal name.to_s, cert.issuer.to_s
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
assert_equal 5, cert.extensions.length,
|
|
|
|
cert.extensions.map { |e| e.to_a.first }
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
constraints = cert.extensions.find { |ext| ext.oid == 'subjectAltName' }
|
|
|
|
assert_equal 'email:nobody@example', constraints.value
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
constraints = cert.extensions.find { |ext| ext.oid == 'basicConstraints' }
|
|
|
|
assert_equal 'CA:FALSE', constraints.value
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
key_usage = cert.extensions.find { |ext| ext.oid == 'keyUsage' }
|
|
|
|
assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
|
|
|
|
key_usage.value
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
key_ident = cert.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
|
|
|
|
assert_equal 59, key_ident.value.length
|
2012-12-08 01:01:49 -05:00
|
|
|
assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
|
2012-11-29 01:52:18 -05:00
|
|
|
key_ident.value
|
|
|
|
end
|
2011-01-18 19:08:49 -05:00
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
def test_class_create_key
|
2013-09-14 04:59:02 -04:00
|
|
|
key = @SEC.create_key 1024
|
2012-11-29 01:52:18 -05:00
|
|
|
|
|
|
|
assert_kind_of OpenSSL::PKey::RSA, key
|
2011-01-18 19:08:49 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_email_to_name
|
|
|
|
assert_equal '/CN=nobody/DC=example',
|
2012-11-29 01:52:18 -05:00
|
|
|
@SEC.email_to_name('nobody@example').to_s
|
2011-01-18 19:08:49 -05:00
|
|
|
|
|
|
|
assert_equal '/CN=nobody/DC=example/DC=com',
|
2012-11-29 01:52:18 -05:00
|
|
|
@SEC.email_to_name('nobody@example.com').to_s
|
2011-01-18 19:08:49 -05:00
|
|
|
|
|
|
|
assert_equal '/CN=no.body/DC=example',
|
2012-11-29 01:52:18 -05:00
|
|
|
@SEC.email_to_name('no.body@example').to_s
|
2011-01-18 19:08:49 -05:00
|
|
|
|
|
|
|
assert_equal '/CN=no_body/DC=example',
|
2012-11-29 01:52:18 -05:00
|
|
|
@SEC.email_to_name('no+body@example').to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_re_sign
|
|
|
|
re_signed = Gem::Security.re_sign EXPIRED_CERT, PRIVATE_KEY, 60
|
|
|
|
|
|
|
|
assert_in_delta Time.now, re_signed.not_before, 10
|
|
|
|
assert_in_delta Time.now + 60, re_signed.not_after, 10
|
2012-12-08 01:01:49 -05:00
|
|
|
assert_equal EXPIRED_CERT.serial + 1, re_signed.serial
|
2012-11-29 01:52:18 -05:00
|
|
|
|
|
|
|
assert re_signed.verify PUBLIC_KEY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_re_sign_not_self_signed
|
|
|
|
e = assert_raises Gem::Security::Exception do
|
|
|
|
Gem::Security.re_sign CHILD_CERT, CHILD_KEY
|
|
|
|
end
|
|
|
|
|
2012-12-08 01:01:49 -05:00
|
|
|
child_alt_name = CHILD_CERT.extensions.find do |extension|
|
|
|
|
extension.oid == 'subjectAltName'
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_equal "#{child_alt_name.value} is not self-signed, contact " +
|
2012-11-29 01:52:18 -05:00
|
|
|
"#{ALTERNATE_CERT.issuer} to obtain a valid certificate",
|
|
|
|
e.message
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_re_sign_wrong_key
|
|
|
|
e = assert_raises Gem::Security::Exception do
|
|
|
|
Gem::Security.re_sign ALTERNATE_CERT, PRIVATE_KEY
|
|
|
|
end
|
|
|
|
|
2012-12-06 00:26:46 -05:00
|
|
|
assert_equal "incorrect signing key for re-signing " +
|
2012-11-29 01:52:18 -05:00
|
|
|
"#{ALTERNATE_CERT.subject}",
|
|
|
|
e.message
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_reset
|
|
|
|
trust_dir = @SEC.trust_dir
|
|
|
|
|
|
|
|
@SEC.reset
|
|
|
|
|
|
|
|
refute_equal trust_dir, @SEC.trust_dir
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_sign
|
|
|
|
issuer = PUBLIC_CERT.subject
|
|
|
|
signee = OpenSSL::X509::Name.parse "/CN=signee/DC=example"
|
|
|
|
|
|
|
|
key = PRIVATE_KEY
|
|
|
|
cert = OpenSSL::X509::Certificate.new
|
|
|
|
cert.subject = signee
|
|
|
|
|
|
|
|
cert.subject = signee
|
|
|
|
cert.public_key = key.public_key
|
|
|
|
|
|
|
|
signed = @SEC.sign cert, key, PUBLIC_CERT, 60
|
|
|
|
|
|
|
|
assert_equal key.public_key.to_pem, signed.public_key.to_pem
|
|
|
|
assert_equal signee.to_s, signed.subject.to_s
|
|
|
|
assert_equal issuer.to_s, signed.issuer.to_s
|
|
|
|
|
|
|
|
assert_in_delta Time.now, signed.not_before, 10
|
|
|
|
assert_in_delta Time.now + 60, signed.not_after, 10
|
|
|
|
|
|
|
|
assert_equal 4, signed.extensions.length,
|
|
|
|
signed.extensions.map { |e| e.to_a.first }
|
|
|
|
|
|
|
|
constraints = signed.extensions.find { |ext| ext.oid == 'issuerAltName' }
|
|
|
|
assert_equal 'email:nobody@example', constraints.value, 'issuerAltName'
|
|
|
|
|
|
|
|
constraints = signed.extensions.find { |ext| ext.oid == 'basicConstraints' }
|
|
|
|
assert_equal 'CA:FALSE', constraints.value
|
|
|
|
|
|
|
|
key_usage = signed.extensions.find { |ext| ext.oid == 'keyUsage' }
|
|
|
|
assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
|
|
|
|
key_usage.value
|
|
|
|
|
|
|
|
key_ident =
|
|
|
|
signed.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
|
|
|
|
assert_equal 59, key_ident.value.length
|
2012-12-08 01:01:49 -05:00
|
|
|
assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
|
2012-11-29 01:52:18 -05:00
|
|
|
key_ident.value
|
|
|
|
|
|
|
|
assert signed.verify key
|
2011-01-18 19:08:49 -05:00
|
|
|
end
|
|
|
|
|
2012-11-29 01:52:18 -05:00
|
|
|
def test_class_sign_AltName
|
|
|
|
issuer = PUBLIC_CERT.subject
|
|
|
|
signee = OpenSSL::X509::Name.parse "/CN=signee/DC=example"
|
|
|
|
|
|
|
|
cert = @SEC.create_cert_email 'signee@example', PRIVATE_KEY
|
|
|
|
|
|
|
|
signed = @SEC.sign cert, PRIVATE_KEY, PUBLIC_CERT, 60
|
|
|
|
|
|
|
|
assert_equal PUBLIC_KEY.to_pem, signed.public_key.to_pem
|
|
|
|
assert_equal signee.to_s, signed.subject.to_s
|
|
|
|
assert_equal issuer.to_s, signed.issuer.to_s
|
|
|
|
|
|
|
|
assert_in_delta Time.now, signed.not_before, 10
|
|
|
|
assert_in_delta Time.now + 60, signed.not_after, 10
|
|
|
|
|
|
|
|
assert_equal 5, signed.extensions.length,
|
|
|
|
signed.extensions.map { |e| e.to_a.first }
|
|
|
|
|
|
|
|
constraints = signed.extensions.find { |ext| ext.oid == 'issuerAltName' }
|
|
|
|
assert_equal 'email:nobody@example', constraints.value, 'issuerAltName'
|
|
|
|
|
|
|
|
constraints = signed.extensions.find { |ext| ext.oid == 'subjectAltName' }
|
|
|
|
assert_equal 'email:signee@example', constraints.value, 'subjectAltName'
|
|
|
|
|
|
|
|
constraints = signed.extensions.find { |ext| ext.oid == 'basicConstraints' }
|
|
|
|
assert_equal 'CA:FALSE', constraints.value
|
|
|
|
|
|
|
|
key_usage = signed.extensions.find { |ext| ext.oid == 'keyUsage' }
|
|
|
|
assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
|
|
|
|
key_usage.value
|
|
|
|
|
|
|
|
key_ident =
|
|
|
|
signed.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
|
|
|
|
assert_equal 59, key_ident.value.length
|
2012-12-08 01:01:49 -05:00
|
|
|
assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
|
2012-11-29 01:52:18 -05:00
|
|
|
key_ident.value
|
|
|
|
|
|
|
|
assert signed.verify PUBLIC_KEY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_trust_dir
|
|
|
|
trust_dir = @SEC.trust_dir
|
|
|
|
|
|
|
|
expected = File.join Gem.user_home, '.gem/trust'
|
|
|
|
|
|
|
|
assert_equal expected, trust_dir.dir
|
|
|
|
end
|
|
|
|
|
2013-09-14 04:59:02 -04:00
|
|
|
def test_class_write
|
|
|
|
key = @SEC.create_key 1024
|
|
|
|
|
|
|
|
path = File.join @tempdir, 'test-private_key.pem'
|
|
|
|
|
|
|
|
@SEC.write key, path
|
|
|
|
|
|
|
|
assert_path_exists path
|
|
|
|
|
|
|
|
key_from_file = File.read path
|
|
|
|
|
|
|
|
assert_equal key.to_pem, key_from_file
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_write_encrypted
|
|
|
|
key = @SEC.create_key 1024
|
|
|
|
|
|
|
|
path = File.join @tempdir, 'test-private_encrypted_key.pem'
|
|
|
|
|
|
|
|
passphrase = 'It should be long.'
|
|
|
|
|
|
|
|
@SEC.write key, path, 0600, passphrase
|
|
|
|
|
|
|
|
assert_path_exists path
|
|
|
|
|
|
|
|
key_from_file = OpenSSL::PKey::RSA.new File.read(path), passphrase
|
|
|
|
|
|
|
|
assert_equal key.to_pem, key_from_file.to_pem
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_class_write_encrypted_cipher
|
|
|
|
key = @SEC.create_key 1024
|
|
|
|
|
|
|
|
path = File.join @tempdir, 'test-private_encrypted__with_non_default_cipher_key.pem'
|
|
|
|
|
|
|
|
passphrase = 'It should be long.'
|
|
|
|
|
|
|
|
cipher = OpenSSL::Cipher.new 'AES-192-CBC'
|
|
|
|
|
|
|
|
@SEC.write key, path, 0600, passphrase, cipher
|
|
|
|
|
|
|
|
assert_path_exists path
|
|
|
|
|
|
|
|
key_file_contents = File.read(path)
|
|
|
|
|
|
|
|
assert key_file_contents.split("\n")[2].match(cipher.name)
|
|
|
|
|
|
|
|
key_from_file = OpenSSL::PKey::RSA.new key_file_contents, passphrase
|
|
|
|
|
|
|
|
assert_equal key.to_pem, key_from_file.to_pem
|
|
|
|
end
|
|
|
|
|
|
|
|
end if defined?(OpenSSL::SSL)
|
2012-11-29 01:52:18 -05:00
|
|
|
|