是否可以将SSLEngine与* Blocking * I / O

时间:2016-03-04 17:46:23

标签: java

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

我可以整合其中两个的钩子是一个普通的老式接受者线程。

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

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

所以基本上我需要 - 在接受者线程的衍生工作线程中 - 启动SSLEngine验证一些子协议信息,然后将整个事务交给另一个代码库的选择器线程。

为了使事情变得更复杂,第二个代码库上有一个回退路径,如果它得到的Socket没有用SocketChannel打开,它将下拉到阻塞模式。 ..这就是导致我出问题的一点......

即我假设Socket.getChannel()!=null

是不安全的

因此我的SSLEngine代码需要考虑这种可能性,并在不使用非阻塞I / O API的情况下设置SSLEngine ...

到目前为止,我一直在阻止阻止引擎的阻塞读取调用......

问题有没有人知道SSLEngine与传统InputStream/OutputStream使用而不是SocketChannel

的任何示例

1 个答案:

答案 0 :(得分:0)

是握手。 很遗憾我们不能将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;
    }
}

要更改为非阻止添加:

socketChannel.configureBlocking(true);
Selector selector = Selector.open();
SelectionKey sk = socketChannel.register(selector, SelectionKey.OP_READ);

while(initialHSComplete != true){
    selector.select();
    doHandshake(socketChannel, engine, sk);