ext/puma_http11/org/jruby/puma/MiniSSL.java in piesync-puma-3.12.6.1 vs ext/puma_http11/org/jruby/puma/MiniSSL.java in piesync-puma-5.4.0.1
- old
+ new
@@ -20,19 +20,23 @@
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import java.io.FileInputStream;
+import java.io.InputStream;
import java.io.IOException;
+import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
import static javax.net.ssl.SSLEngineResult.Status;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus;
public class MiniSSL extends RubyObject {
@@ -63,11 +67,11 @@
private MiniSSLBuffer(int capacity) { buffer = ByteBuffer.allocate(capacity); }
private MiniSSLBuffer(byte[] initialContents) { buffer = ByteBuffer.wrap(initialContents); }
public void clear() { buffer.clear(); }
public void compact() { buffer.compact(); }
- public void flip() { buffer.flip(); }
+ public void flip() { ((Buffer) buffer).flip(); }
public boolean hasRemaining() { return buffer.hasRemaining(); }
public int position() { return buffer.position(); }
public ByteBuffer getRawBuffer() {
return buffer;
@@ -87,11 +91,11 @@
* Ensures that newCapacity bytes can be written to this buffer, only re-allocating if necessary
*/
public void resize(int newCapacity) {
if (newCapacity > buffer.capacity()) {
ByteBuffer dstTmp = ByteBuffer.allocate(newCapacity);
- buffer.flip();
+ flip();
dstTmp.put(buffer);
buffer = dstTmp;
} else {
buffer.limit(newCapacity);
}
@@ -99,11 +103,11 @@
/**
* Drains the buffer to a ByteList, or returns null for an empty buffer
*/
public ByteList asByteList() {
- buffer.flip();
+ flip();
if (!buffer.hasRemaining()) {
buffer.clear();
return null;
}
@@ -117,54 +121,93 @@
@Override
public String toString() { return buffer.toString(); }
}
private SSLEngine engine;
+ private boolean closed;
+ private boolean handshake;
private MiniSSLBuffer inboundNetData;
private MiniSSLBuffer outboundAppData;
private MiniSSLBuffer outboundNetData;
public MiniSSL(Ruby runtime, RubyClass klass) {
super(runtime, klass);
}
+ private static Map<String, KeyManagerFactory> keyManagerFactoryMap = new ConcurrentHashMap<String, KeyManagerFactory>();
+ private static Map<String, TrustManagerFactory> trustManagerFactoryMap = new ConcurrentHashMap<String, TrustManagerFactory>();
+
@JRubyMethod(meta = true)
- public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext) {
- RubyClass klass = (RubyClass) recv;
+ public static synchronized IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext)
+ throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
+ // Create the KeyManagerFactory and TrustManagerFactory for this server
+ String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString();
+ char[] password = miniSSLContext.callMethod(context, "keystore_pass").convertToString().asJavaString().toCharArray();
+ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+ InputStream is = new FileInputStream(keystoreFile);
+ try {
+ ks.load(is, password);
+ } finally {
+ is.close();
+ }
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(ks, password);
+ keyManagerFactoryMap.put(keystoreFile, kmf);
+
+ KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
+ is = new FileInputStream(keystoreFile);
+ try {
+ ts.load(is, password);
+ } finally {
+ is.close();
+ }
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ tmf.init(ts);
+ trustManagerFactoryMap.put(keystoreFile, tmf);
+
+ RubyClass klass = (RubyClass) recv;
return klass.newInstance(context,
new IRubyObject[] { miniSSLContext },
Block.NULL_BLOCK);
}
@JRubyMethod
public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
- throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
+ throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
- char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray();
String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
- ks.load(new FileInputStream(keystoreFile), password);
- ts.load(new FileInputStream(keystoreFile), password);
+ KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
+ TrustManagerFactory tmf = trustManagerFactoryMap.get(keystoreFile);
+ if(kmf == null || tmf == null) {
+ throw new KeyStoreException("Could not find KeyManagerFactory/TrustManagerFactory for keystore: " + keystoreFile);
+ }
- 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(), tmf.getTrustManagers(), null);
+ closed = false;
+ handshake = false;
engine = sslCtx.createSSLEngine();
- String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
+ String[] protocols;
+ if(miniSSLContext.callMethod(threadContext, "no_tlsv1").isTrue()) {
+ protocols = new String[] { "TLSv1.1", "TLSv1.2" };
+ } else {
+ protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
+ }
+
+ if(miniSSLContext.callMethod(threadContext, "no_tlsv1_1").isTrue()) {
+ protocols = new String[] { "TLSv1.2" };
+ }
+
engine.setEnabledProtocols(protocols);
engine.setUseClientMode(false);
- long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue();
+ long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger("to_i").getLongValue();
if ((verify_mode & 0x1) != 0) { // 'peer'
engine.setWantClientAuth(true);
}
if ((verify_mode & 0x2) != 0) { // 'force_peer'
engine.setNeedClientAuth(true);
@@ -227,18 +270,25 @@
break;
case BUFFER_UNDERFLOW:
// need to wait for more data to come in before we retry
retryOp = false;
break;
+ case CLOSED:
+ closed = true;
+ retryOp = false;
+ break;
default:
- // other cases are OK and CLOSED. We're done here.
+ // other case is OK. We're done here.
retryOp = false;
}
+ if (res.getHandshakeStatus() == HandshakeStatus.FINISHED) {
+ handshake = true;
+ }
}
// after each op, run any delegated tasks if needed
- if(engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+ if(res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
Runnable runnable;
while ((runnable = engine.getDelegatedTask()) != null) {
runnable.run();
}
}
@@ -258,26 +308,29 @@
MiniSSLBuffer inboundAppData = new MiniSSLBuffer(engine.getSession().getApplicationBufferSize());
doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
boolean done = false;
+ SSLEngineResult res = null;
while (!done) {
switch (handshakeStatus) {
case NEED_WRAP:
- doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
+ res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
break;
case NEED_UNWRAP:
- SSLEngineResult res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
+ res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
// need more data before we can shake more hands
done = true;
}
break;
default:
done = true;
}
- handshakeStatus = engine.getHandshakeStatus();
+ if (!done) {
+ handshakeStatus = res.getHandshakeStatus();
+ }
}
if (inboundNetData.hasRemaining()) {
inboundNetData.compact();
} else {
@@ -345,8 +398,25 @@
public IRubyObject peercert() throws CertificateEncodingException {
try {
return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
} catch (SSLPeerUnverifiedException ex) {
return getRuntime().getNil();
+ }
+ }
+
+ @JRubyMethod(name = "init?")
+ public IRubyObject isInit(ThreadContext context) {
+ return handshake ? getRuntime().getFalse() : getRuntime().getTrue();
+ }
+
+ @JRubyMethod
+ public IRubyObject shutdown() {
+ if (closed || engine.isInboundDone() && engine.isOutboundDone()) {
+ if (engine.isOutboundDone()) {
+ engine.closeOutbound();
+ }
+ return getRuntime().getTrue();
+ } else {
+ return getRuntime().getFalse();
}
}
}