Java 是否可以将SSLEngine与*阻塞*I/O一起使用
我正在尝试集成两个代码库。一个代码库使用阻塞I/O。另一个代码库使用非阻塞I/O 我可以集成这两个的钩子是一个普通的老式接受线程 此接受线程从套接字读取子协议信息,然后根据子协议名称转发给相应的处理程序 代码的另一面有自己的选择器线程,但只公开更高级别的构造集 因此,基本上我需要——在接受线程的派生工作线程中——启动SSLEngine验证一些子协议信息,然后将整个过程交给另一个代码库的选择器线程 为了使事情变得更复杂,第二个代码基上有一个后退路径,如果它得到一个未使用Java 是否可以将SSLEngine与*阻塞*I/O一起使用,java,Java,我正在尝试集成两个代码库。一个代码库使用阻塞I/O。另一个代码库使用非阻塞I/O 我可以集成这两个的钩子是一个普通的老式接受线程 此接受线程从套接字读取子协议信息,然后根据子协议名称转发给相应的处理程序 代码的另一面有自己的选择器线程,但只公开更高级别的构造集 因此,基本上我需要——在接受线程的派生工作线程中——启动SSLEngine验证一些子协议信息,然后将整个过程交给另一个代码库的选择器线程 为了使事情变得更复杂,第二个代码基上有一个后退路径,如果它得到一个未使用SocketChannel打
SocketChannel打开的Socket
,它将下降到阻塞模式。。。而这正是引起我问题的原因
也就是说,假设Socket.getChannel()是不安全的=空
因此,我的SSLEngine
代码需要考虑这种可能性,并在不使用非阻塞I/O API的情况下设置SSLEngine
到目前为止,我一直在打阻塞读取呼叫,导致引擎熄火
问题是否有人知道有任何例子表明SSLEngine
用于传统的InputStream/OutputStream
而不是用于握手的SocketChannel是。
遗憾的是,我们无法将SSLSocket.getSession用于SSLEngine。
但我们在阻塞模式下使用SSLEngine。
请参阅jdk1.8.0_112/sample/nio/server/ChannelIOSecure.java
阻塞:
method1(){
....
socketChannel.configureBlocking(true);
SSLEngine engine = sslContext.createSSLEngine();
engine.setUseClientMode(false);//server
engine.setNeedClientAuth(true);
this.outNetBB = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
outNetBB.position(0);
outNetBB.limit(0);
this.inNetBB = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
this.requestBB = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
this.hsBB = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
initialHSComplete = false;
while(initialHSComplete != true)
doHandshake(socketChannel, engine, null);
}
private boolean tryFlush(ByteBuffer bb, SocketChannel socketChannel) throws IOException {
socketChannel.write(bb);
return !bb.hasRemaining();
}
private SSLEngineResult.HandshakeStatus doTasks(SSLEngine sslEngine) {
Runnable runnable;
/*
* We could run this in a separate thread, but
* do in the current for now.
*/
while ((runnable = sslEngine.getDelegatedTask()) != null) {
runnable.run();
}
return sslEngine.getHandshakeStatus();
}
private ByteBuffer outNetBB;
int netBBSize;
private ByteBuffer inNetBB;
int appBBSize;
private ByteBuffer requestBB;
private ByteBuffer hsBB;
private boolean initialHSComplete; // Handshake complete status
HandshakeStatus initialHSStatus = HandshakeStatus.NEED_UNWRAP; //server
private boolean doHandshake(SocketChannel sc, SSLEngine sslEngine, SelectionKey sk) throws IOException {
SSLEngineResult result;
if (initialHSComplete) {
return initialHSComplete;
}
/*
* Flush out the outgoing buffer, if there's anything left in
* it.
*/
if (outNetBB.hasRemaining()) {
System.out.println("doha wtf");
if (!tryFlush(outNetBB, sc)) {
return false;
}
// See if we need to switch from write to read mode.
switch (initialHSStatus) {
// Is this the last buffer?
case FINISHED:
initialHSComplete = true;
// Fall-through to reregister need for a Read.
case NEED_UNWRAP:
if (sk != null) {
sk.interestOps(SelectionKey.OP_READ);
}
break;
}
return initialHSComplete;
}
switch (initialHSStatus) {
case NEED_UNWRAP:
System.out.println("before read");
if (sc.read(inNetBB) == -1) {
sslEngine.closeInbound();
return initialHSComplete;
}
System.out.println("after read");
needIO:
while (initialHSStatus == HandshakeStatus.NEED_UNWRAP) {
System.out.println("initialHSStatus"+initialHSStatus);
resizeRequestBB(); // expected room for unwrap
inNetBB.flip();
result = sslEngine.unwrap(inNetBB, requestBB);
inNetBB.compact();
System.out.println("result"+result);
initialHSStatus = result.getHandshakeStatus();
switch (result.getStatus()) {
case OK:
switch (initialHSStatus) {
case NOT_HANDSHAKING:
throw new IOException(
"Not handshaking during initial handshake");
case NEED_TASK:
initialHSStatus = doTasks(sslEngine);
break;
case FINISHED:
initialHSComplete = true;
break needIO;
}
break;
case BUFFER_UNDERFLOW:
// Resize buffer if needed.
netBBSize = sslEngine.getSession().getPacketBufferSize();
if (netBBSize > inNetBB.capacity()) {
resizeResponseBB();
}
/*
* Need to go reread the Channel for more data.
*/
if (sk != null) {
sk.interestOps(SelectionKey.OP_READ);
}
break needIO;
case BUFFER_OVERFLOW:
// Reset the application buffer size.
appBBSize =
sslEngine.getSession().getApplicationBufferSize();
break;
default: //CLOSED:
throw new IOException("Received" + result.getStatus() +
"during initial handshaking");
}
System.out.println("bottom of needIO");
} // "needIO" block.
System.out.println("after needIO "+initialHSStatus);
/*
* Just transitioned from read to write.
*/
if (initialHSStatus != HandshakeStatus.NEED_WRAP) {
break;
}
// Fall through and fill the write buffers.
case NEED_WRAP:
/*
* The flush above guarantees the out buffer to be empty
*/
outNetBB.clear();
result = sslEngine.wrap(hsBB, outNetBB);
outNetBB.flip();
initialHSStatus = result.getHandshakeStatus();
System.out.println("result wrap="+result);
switch (result.getStatus()) {
case OK:
if (initialHSStatus == HandshakeStatus.NEED_TASK) {
initialHSStatus = doTasks(sslEngine);
}
System.out.println("here");
if (sk != null) {
sk.interestOps(SelectionKey.OP_WRITE);
}
System.out.println("here2");
break;
default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED:
throw new IOException("Received" + result.getStatus() +
"during initial handshaking");
}
break;
default: // NOT_HANDSHAKING/NEED_TASK/FINISHED
throw new RuntimeException("Invalid Handshaking State" +
initialHSStatus);
} // switch
return initialHSComplete;
}
/*private void tryFlush(SocketChannel sc) throws IOException {
System.out.println("flush"+outNetBB);
sc.write(outNetBB);
if (!outNetBB.hasRemaining())
outNetBB.clear();
}*/
//}
//}
private void resizeResponseBB() {
ByteBuffer bb = ByteBuffer.allocate(netBBSize);
inNetBB.flip();
bb.put(inNetBB);
inNetBB = bb;
}
protected void resizeRequestBB() {
int remaining = appBBSize;
if (requestBB.remaining() < remaining) {
// Expand buffer for large request
ByteBuffer bb = ByteBuffer.allocate(requestBB.capacity() * 2);
requestBB.flip();
bb.put(requestBB);
requestBB = bb;
}
}
是的,握手。
遗憾的是,我们无法将SSLSocket.getSession用于SSLEngine。
但我们在阻塞模式下使用SSLEngine。
请参阅jdk1.8.0_112/sample/nio/server/ChannelIOSecure.java
阻塞:
method1(){
....
socketChannel.configureBlocking(true);
SSLEngine engine = sslContext.createSSLEngine();
engine.setUseClientMode(false);//server
engine.setNeedClientAuth(true);
this.outNetBB = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
outNetBB.position(0);
outNetBB.limit(0);
this.inNetBB = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
this.requestBB = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
this.hsBB = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
initialHSComplete = false;
while(initialHSComplete != true)
doHandshake(socketChannel, engine, null);
}
private boolean tryFlush(ByteBuffer bb, SocketChannel socketChannel) throws IOException {
socketChannel.write(bb);
return !bb.hasRemaining();
}
private SSLEngineResult.HandshakeStatus doTasks(SSLEngine sslEngine) {
Runnable runnable;
/*
* We could run this in a separate thread, but
* do in the current for now.
*/
while ((runnable = sslEngine.getDelegatedTask()) != null) {
runnable.run();
}
return sslEngine.getHandshakeStatus();
}
private ByteBuffer outNetBB;
int netBBSize;
private ByteBuffer inNetBB;
int appBBSize;
private ByteBuffer requestBB;
private ByteBuffer hsBB;
private boolean initialHSComplete; // Handshake complete status
HandshakeStatus initialHSStatus = HandshakeStatus.NEED_UNWRAP; //server
private boolean doHandshake(SocketChannel sc, SSLEngine sslEngine, SelectionKey sk) throws IOException {
SSLEngineResult result;
if (initialHSComplete) {
return initialHSComplete;
}
/*
* Flush out the outgoing buffer, if there's anything left in
* it.
*/
if (outNetBB.hasRemaining()) {
System.out.println("doha wtf");
if (!tryFlush(outNetBB, sc)) {
return false;
}
// See if we need to switch from write to read mode.
switch (initialHSStatus) {
// Is this the last buffer?
case FINISHED:
initialHSComplete = true;
// Fall-through to reregister need for a Read.
case NEED_UNWRAP:
if (sk != null) {
sk.interestOps(SelectionKey.OP_READ);
}
break;
}
return initialHSComplete;
}
switch (initialHSStatus) {
case NEED_UNWRAP:
System.out.println("before read");
if (sc.read(inNetBB) == -1) {
sslEngine.closeInbound();
return initialHSComplete;
}
System.out.println("after read");
needIO:
while (initialHSStatus == HandshakeStatus.NEED_UNWRAP) {
System.out.println("initialHSStatus"+initialHSStatus);
resizeRequestBB(); // expected room for unwrap
inNetBB.flip();
result = sslEngine.unwrap(inNetBB, requestBB);
inNetBB.compact();
System.out.println("result"+result);
initialHSStatus = result.getHandshakeStatus();
switch (result.getStatus()) {
case OK:
switch (initialHSStatus) {
case NOT_HANDSHAKING:
throw new IOException(
"Not handshaking during initial handshake");
case NEED_TASK:
initialHSStatus = doTasks(sslEngine);
break;
case FINISHED:
initialHSComplete = true;
break needIO;
}
break;
case BUFFER_UNDERFLOW:
// Resize buffer if needed.
netBBSize = sslEngine.getSession().getPacketBufferSize();
if (netBBSize > inNetBB.capacity()) {
resizeResponseBB();
}
/*
* Need to go reread the Channel for more data.
*/
if (sk != null) {
sk.interestOps(SelectionKey.OP_READ);
}
break needIO;
case BUFFER_OVERFLOW:
// Reset the application buffer size.
appBBSize =
sslEngine.getSession().getApplicationBufferSize();
break;
default: //CLOSED:
throw new IOException("Received" + result.getStatus() +
"during initial handshaking");
}
System.out.println("bottom of needIO");
} // "needIO" block.
System.out.println("after needIO "+initialHSStatus);
/*
* Just transitioned from read to write.
*/
if (initialHSStatus != HandshakeStatus.NEED_WRAP) {
break;
}
// Fall through and fill the write buffers.
case NEED_WRAP:
/*
* The flush above guarantees the out buffer to be empty
*/
outNetBB.clear();
result = sslEngine.wrap(hsBB, outNetBB);
outNetBB.flip();
initialHSStatus = result.getHandshakeStatus();
System.out.println("result wrap="+result);
switch (result.getStatus()) {
case OK:
if (initialHSStatus == HandshakeStatus.NEED_TASK) {
initialHSStatus = doTasks(sslEngine);
}
System.out.println("here");
if (sk != null) {
sk.interestOps(SelectionKey.OP_WRITE);
}
System.out.println("here2");
break;
default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED:
throw new IOException("Received" + result.getStatus() +
"during initial handshaking");
}
break;
default: // NOT_HANDSHAKING/NEED_TASK/FINISHED
throw new RuntimeException("Invalid Handshaking State" +
initialHSStatus);
} // switch
return initialHSComplete;
}
/*private void tryFlush(SocketChannel sc) throws IOException {
System.out.println("flush"+outNetBB);
sc.write(outNetBB);
if (!outNetBB.hasRemaining())
outNetBB.clear();
}*/
//}
//}
private void resizeResponseBB() {
ByteBuffer bb = ByteBuffer.allocate(netBBSize);
inNetBB.flip();
bb.put(inNetBB);
inNetBB = bb;
}
protected void resizeRequestBB() {
int remaining = appBBSize;
if (requestBB.remaining() < remaining) {
// Expand buffer for large request
ByteBuffer bb = ByteBuffer.allocate(requestBB.capacity() * 2);
requestBB.flip();
bb.put(requestBB);
requestBB = bb;
}
}
是的,适用于whiole SSL连接生存期,而不仅仅是握手。注意:您的意思是configureBlocking(false)
。此代码将引发IllegalBlockingModeException
。只有当您的CannelSocket使用选择器注册时,才能引发IllegalBlockingModeException。您可以取消选择键,如下所示:socketChannel.keyFor(readSelector.cancel()
Yes适用于whiole SSL连接生存期,而不仅仅是握手。注意:您的意思是configureBlocking(false)
。此代码将引发IllegalBlockingModeException
。只有当您的CannelSocket使用选择器注册时,才能引发IllegalBlockingModeException。您可以取消选择键,如下所示:socketChannel.keyFor(readSelector.cancel()代码>阻止读取调用不会使发动机失速。它们在read()
中阻塞。你的观点不清楚。在阻塞模式下使用SSLEngine
比在非阻塞模式下容易得多,因为您不必担心诸如可能同时进行的任务之类的操作:您可以在线执行这些操作。阻塞读取调用不会使引擎停止。它们在read()
中阻塞。你的观点不清楚。在阻塞模式下使用SSLEngine
比在非阻塞模式下容易得多,因为您不必担心诸如可能同时进行的任务之类的操作:您可以在线执行这些操作。