1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00

Merge pull request #833 from joemiller/jruby-client-tls-auth

support TLS client auth (verify_mode) in jruby
This commit is contained in:
Evan Phoenix 2016-01-27 19:44:01 -08:00
commit 611d86f302
4 changed files with 85 additions and 64 deletions

Binary file not shown.

Binary file not shown.

View file

@ -13,6 +13,7 @@ import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
@ -136,23 +137,36 @@ public class MiniSSL extends RubyObject {
public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray();
ks.load(new FileInputStream(miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString()),
password);
String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
ks.load(new FileInputStream(keystoreFile), password);
ts.load(new FileInputStream(keystoreFile), password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), null, null);
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
engine = sslCtx.createSSLEngine();
String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
engine.setEnabledProtocols(protocols);
engine.setUseClientMode(false);
long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue();
if ((verify_mode & 0x1) != 0) { // 'peer'
engine.setWantClientAuth(true);
}
if ((verify_mode & 0x2) != 0) { // 'force_peer'
engine.setNeedClientAuth(true);
}
SSLSession session = engine.getSession();
inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize());

View file

@ -132,92 +132,99 @@ class TestPumaServerSSL < Test::Unit::TestCase
end
# client-side TLS authentication tests
unless defined?(JRUBY_VERSION)
class TestPumaServerSSLClient < Test::Unit::TestCase
class TestPumaServerSSLClient < Test::Unit::TestCase
def assert_ssl_client_error_match(error, subject=nil, &blk)
@port = 3212
@host = "127.0.0.1"
def assert_ssl_client_error_match(error, subject=nil, &blk)
@port = 3212
@host = "127.0.0.1"
@app = lambda { |env| [200, {}, [env['rack.url_scheme']]] }
@app = lambda { |env| [200, {}, [env['rack.url_scheme']]] }
@ctx = Puma::MiniSSL::Context.new
@ctx = Puma::MiniSSL::Context.new
if defined?(JRUBY_VERSION)
@ctx.keystore = File.expand_path "../../examples/puma/client-certs/keystore.jks", __FILE__
@ctx.keystore_pass = 'blahblah'
else
@ctx.key = File.expand_path "../../examples/puma/client-certs/server.key", __FILE__
@ctx.cert = File.expand_path "../../examples/puma/client-certs/server.crt", __FILE__
@ctx.ca = File.expand_path "../../examples/puma/client-certs/ca.crt", __FILE__
@ctx.verify_mode = Puma::MiniSSL::VERIFY_PEER | Puma::MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
end
@ctx.verify_mode = Puma::MiniSSL::VERIFY_PEER | Puma::MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
events = SSLEventsHelper.new STDOUT, STDERR
@server = Puma::Server.new @app, events
@server.add_ssl_listener @host, @port, @ctx
@server.run
events = SSLEventsHelper.new STDOUT, STDERR
@server = Puma::Server.new @app, events
@server.add_ssl_listener @host, @port, @ctx
@server.run
@http = Net::HTTP.new @host, @port
@http.use_ssl = true
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
@http = Net::HTTP.new @host, @port
@http.use_ssl = true
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
blk.call(@http)
blk.call(@http)
client_error = false
begin
@http.start do
req = Net::HTTP::Get.new "/", {}
@http.request(req)
end
rescue OpenSSL::SSL::SSLError
client_error = true
client_error = false
begin
@http.start do
req = Net::HTTP::Get.new "/", {}
@http.request(req)
end
rescue OpenSSL::SSL::SSLError
client_error = true
end
sleep 0.1
assert_equal !!error, client_error
sleep 0.1
assert_equal !!error, client_error
# The JRuby MiniSSL implementation lacks error capturing currently, so we can't inspect the
# messages here
unless defined?(JRUBY_VERSION)
assert_match error, events.error.message if error
assert_equal @host, events.addr if error
assert_equal subject, events.cert.subject.to_s if subject
ensure
@server.stop(true)
end
ensure
@server.stop(true)
end
def test_verify_fail_if_no_client_cert
return if DISABLE_SSL
def test_verify_fail_if_no_client_cert
return if DISABLE_SSL
assert_ssl_client_error_match 'peer did not return a certificate' do |http|
# nothing
end
assert_ssl_client_error_match 'peer did not return a certificate' do |http|
# nothing
end
end
def test_verify_fail_if_client_unknown_ca
return if DISABLE_SSL
def test_verify_fail_if_client_unknown_ca
return if DISABLE_SSL
assert_ssl_client_error_match('self signed certificate in certificate chain', '/DC=net/DC=puma/CN=ca-unknown') do |http|
key = File.expand_path "../../examples/puma/client-certs/client_unknown.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client_unknown.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/unknown_ca.crt", __FILE__
end
assert_ssl_client_error_match('self signed certificate in certificate chain', '/DC=net/DC=puma/CN=ca-unknown') do |http|
key = File.expand_path "../../examples/puma/client-certs/client_unknown.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client_unknown.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/unknown_ca.crt", __FILE__
end
end
def test_verify_fail_if_client_expired_cert
return if DISABLE_SSL
assert_ssl_client_error_match('certificate has expired', '/DC=net/DC=puma/CN=client-expired') do |http|
key = File.expand_path "../../examples/puma/client-certs/client_expired.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client_expired.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/ca.crt", __FILE__
end
def test_verify_fail_if_client_expired_cert
return if DISABLE_SSL
assert_ssl_client_error_match('certificate has expired', '/DC=net/DC=puma/CN=client-expired') do |http|
key = File.expand_path "../../examples/puma/client-certs/client_expired.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client_expired.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/ca.crt", __FILE__
end
end
def test_verify_client_cert
return if DISABLE_SSL
assert_ssl_client_error_match(nil) do |http|
key = File.expand_path "../../examples/puma/client-certs/client.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/ca.crt", __FILE__
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
def test_verify_client_cert
return if DISABLE_SSL
assert_ssl_client_error_match(nil) do |http|
key = File.expand_path "../../examples/puma/client-certs/client.key", __FILE__
crt = File.expand_path "../../examples/puma/client-certs/client.crt", __FILE__
http.key = OpenSSL::PKey::RSA.new File.read(key)
http.cert = OpenSSL::X509::Certificate.new File.read(crt)
http.ca_file = File.expand_path "../../examples/puma/client-certs/ca.crt", __FILE__
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
end
end