Java 是否可以将SSLEngine与*阻塞*I/O一起使用

Java 是否可以将SSLEngine与*阻塞*I/O一起使用,java,Java,我正在尝试集成两个代码库。一个代码库使用阻塞I/O。另一个代码库使用非阻塞I/O 我可以集成这两个的钩子是一个普通的老式接受线程 此接受线程从套接字读取子协议信息,然后根据子协议名称转发给相应的处理程序 代码的另一面有自己的选择器线程,但只公开更高级别的构造集 因此,基本上我需要——在接受线程的派生工作线程中——启动SSLEngine验证一些子协议信息,然后将整个过程交给另一个代码库的选择器线程 为了使事情变得更复杂,第二个代码基上有一个后退路径,如果它得到一个未使用SocketChannel打

我正在尝试集成两个代码库。一个代码库使用阻塞I/O。另一个代码库使用非阻塞I/O

我可以集成这两个的钩子是一个普通的老式接受线程

此接受线程从套接字读取子协议信息,然后根据子协议名称转发给相应的处理程序

代码的另一面有自己的选择器线程,但只公开更高级别的构造集

因此,基本上我需要——在接受线程的派生工作线程中——启动SSLEngine验证一些子协议信息,然后将整个过程交给另一个代码库的选择器线程

为了使事情变得更复杂,第二个代码基上有一个后退路径,如果它得到一个未使用
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
比在非阻塞模式下容易得多,因为您不必担心诸如可能同时进行的任务之类的操作:您可以在线执行这些操作。