在握手过程中从 SSLEngine Wrap 方法获取 SSLException

2023-12-27

当我在 Java 应用程序上运行客户端握手进程以建立 SSL 连接时,我得到SSLException第二次调用 wrap 方法时。我知道此时客户端发送了CLientKeyExchange and ChangeCipherSpec到服务器。我从异常中得到的错误消息是“General SSLEngine Problem”。也许我需要发送的证书有问题?

这是异常堆栈详细信息:

Details: General SSLEngine problem
Trace detail #0: com.sun.net.ssl.internal.ssl.Handshaker.checkThrown(Handshaker.java:994)
Trace detail #1: com.sun.net.ssl.internal.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:459)
Trace detail #2: com.sun.net.ssl.internal.ssl.SSLEngineImpl.writeAppRecord(SSLEngineImpl.java:1058)
Trace detail #3: com.sun.net.ssl.internal.ssl.SSLEngineImpl.wrap(SSLEngineImpl.java:1030)
Trace detail #4: javax.net.ssl.SSLEngine.wrap(SSLEngine.java:411)
Trace detail #5: epic.clarity.extract.CRSslSocketHandler.doHandshake(CRSslSocketHandler.java:333)
Trace detail #6: epic.clarity.extract.CRSslSocketHandler.readAndUnwrap(CRSslSocketHandler.java:282)
Trace detail #7: epic.clarity.extract.CRSslSocketHandler.doHandshake(CRSslSocketHandler.java:322)
Trace detail #8: epic.clarity.extract.CRSslSocketHandler.readAndUnwrap(CRSslSocketHandler.java:282)
Trace detail #9: epic.clarity.extract.CRSslSocketHandler.doHandshake(CRSslSocketHandler.java:322)
Trace detail #10: epic.clarity.extract.CRSslSocketHandler.ReadExtFile(CRSslSocketHandler.java:159)

这是我的代码,握手开始于Read method:

public class CRSslSocketHandler extends CRSocketHandler{

    private SSLEngine _engine;
    private SSLEngineResult.HandshakeStatus hsStatus;

    /**
    * Stores the result from the last operation performed by the SSLEngine 
    */
    private SSLEngineResult.Status status = null;

    /** Application data decrypted from the data received from the peer.
    * This buffer must have enough space for a full unwrap operation,
    * so we can't use the buffer provided by the application, since we
    * have no control over its size.
    */
    private final ByteBuffer peerAppData;
    /** Network data received from the peer. Encrypted. */  
    private final ByteBuffer peerNetData;
    /** Network data to be sent to the peer. Encrypted. */
    private final ByteBuffer netData;  

    /** Used during handshake, for the operations that don't consume any data */
    private ByteBuffer dummy;   


    private boolean initialHandshake = false;  

    private final String[] AvailProtocol = {"TLSv1"};

    private boolean FirstTime = true;

    //Debug
    private BufferedWriter  debugFile;
    private BufferedWriter  test;


    /** Creates a new instance of CRSslSocketHandler */
   public CRSslSocketHandler(SocketChannel channel, DualKeyHashtable queryCollection, DualKeyHashtable routineCollection, long sn, String hostName, int portNum) throws Exception {
       super(channel, queryCollection, routineCollection, sn);


       // Debug purposes
       debugFile = new BufferedWriter(new FileWriter("SslDebug.txt",true));
       test = new BufferedWriter(new FileWriter("ssltest.txt",true));
       try{
           System.out.println("Create Ssl Context");
            test.write("Create Ssl Context");  

            SSLContext sslContext = SSLContext.getInstance("TLSv1");
            System.out.println("Init Ssl Context");
            test.write("Init Ssl Context");

            /String Pass = "password";
            char[] passphrase = Pass.toCharArray();

            System.out.println("Get keys");
            test.write("Get keys");
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("keystore.ks"), passphrase);
             System.out.println("Get trust");
            test.write("Get trust");          
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            System.out.println("Init trust");
            test.write("Init trust");           
            tmf.init(ks); 

            System.out.println("Init context");
            test.write("Init context");         
            sslContext.init(null, tmf.getTrustManagers(), null);
             System.out.println("Create engine host name: " + hostName + " port number: " + portNum);
            test.write("Create engine host name: " + hostName + " port number: " + portNum);
            _engine = sslContext.createSSLEngine(hostName, portNum);
            // _engine = sslContext.createSSLEngine();
            System.out.println("Set client mode");
            test.write("Set client mode");            
            _engine.setUseClientMode(true);
            _engine.setEnabledProtocols(AvailProtocol);



            test.write("Begin Hanshaking");
            System.out.println("Begin Hanshaking");
            _engine.beginHandshake();
            hsStatus = _engine.getHandshakeStatus();
       }
       catch (IOException e){

       }
       catch (Exception e) {
           System.out.println("Exception: " + e.getMessage());
           test.write("Exception: " + e.getMessage());
           test.close();
       }
     System.out.println("Alocate buffers");
      test.write("Alocate buffers");
      SSLSession  session = _engine.getSession();
      peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
      peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
      netData = ByteBuffer.allocate(session.getPacketBufferSize());
      // Change the position of the buffers so that a 
      // call to hasRemaining() returns false. A buffer is considered
      // empty when the position is set to its limit, that is when
      // hasRemaining() returns false.      
      peerAppData.position(peerAppData.limit());
      netData.position(netData.limit());
      char c = (char)(28);
      String msg = "OK" + c;
      dummy = ByteBuffer.wrap(msg.getBytes());
      // dummy = ByteBuffer.allocate(0);
      initialHandshake = true;
      test.close();
    }

    protected int ReadExtFile(ByteBuffer buffer) throws IOException{
        if (initialHandshake) {
            doHandshake();
        }        
        // Check if the stream is closed.
        if (_engine.isInboundDone()) {
            // We reached EOF.
            return EOF;
        }
        // First check if there is decrypted data waiting in the buffers
        if (!peerAppData.hasRemaining()) {
            int appBytesProduced = readAndUnwrap();
            if (appBytesProduced == EOF){
                debugFile.write("Failed to read");
                debugFile.close();
                return appBytesProduced;
            }
            if (appBytesProduced == 0) {
                return appBytesProduced;
            } 
        }

        // It's not certain that we will have some data decrypted ready to 
        // be sent to the application. Anyway, copy as much data as possible
        int limit = Math.min(peerAppData.remaining(), buffer.remaining());
        for (int i = 0; i < limit; i++) {
            buffer.put(peerAppData.get());
        }
        return limit;
    }
    private int readAndUnwrap() throws IOException {
        // No decrypted data left on the buffers.
        // Try to read from the socket. There may be some data
        // on the peerNetData buffer, but it might not be sufficient.
        int bytesRead = _client.read(peerNetData);
        // decoder.decode(peerNetData, charBuffer, false);
        // charBuffer.flip();               
        System.out.println("Read bytes " + bytesRead);


        if (bytesRead == EOF) {
            // We will not receive any more data. Closing the engine
            // is a signal that the end of stream was reached.
            _engine.closeInbound();         
            // EOF. But do we still have some useful data available? 
            if (peerNetData.position() == 0 ||
            status == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
                // Yup. Either the buffer is empty or it's in underflow,
                // meaning that there is not enough data to reassemble a
                // TLS packet. So we can return EOF.
                return EOF;
            }
            // Although we reach EOF, we still have some data left to
            // be decrypted. We must process it 
        }

        // Prepare the application buffer to receive decrypted data
        peerAppData.clear();

        // Prepare the net data for reading. 
        peerNetData.flip();
        SSLEngineResult res;
        System.out.println("Do unwrap " + bytesRead);
        try{
            do {
                res = _engine.unwrap(peerNetData, peerAppData);
                System.out.println("Read status" + res.getHandshakeStatus().toString());
                // During an handshake renegotiation we might need to perform
                // several unwraps to consume e handshake data.
            } while (res.getStatus() == SSLEngineResult.Status.OK &&
            res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&
            res.bytesProduced() == 0);

            System.out.println("Read status" + res.getHandshakeStatus().toString());
            // If the initial handshake finish after an unwrap, we must activate
            // the application interestes, if any were set during the handshake
            if (res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {
                initialHandshake = false;
            }
           // If no data was produced, and the status is still ok, try to read once more
           if (peerAppData.position() == 0 && 
            res.getStatus() == SSLEngineResult.Status.OK &&
            peerNetData.hasRemaining()) {
               res = _engine.unwrap(peerNetData, peerAppData);                             
            }
           /*
            * The status may be:
            * OK - Normal operation
            * OVERFLOW - Should never happen since the application buffer is 
            *   sized to hold the maximum packet size.
            * UNDERFLOW - Need to read more data from the socket. It's normal.
            * CLOSED - The other peer closed the socket. Also normal.
            */
            status = res.getStatus();
            hsStatus = res.getHandshakeStatus();
            // Should never happen, the peerAppData must always have enough space
            // for an unwrap operation
            assert status != SSLEngineResult.Status.BUFFER_OVERFLOW : 
            "Buffer should not overflow: " + res.toString();  
        }
        catch (Exception e){
            System.out.println("Error: " + e.getMessage());
        }




        // The handshake status here can be different than NOT_HANDSHAKING
        // if the other peer closed the connection. So only check for it
        // after testing for closure.
        if (status == SSLEngineResult.Status.CLOSED) {
            debugFile.write("Connection is being closed by peer.");
            return EOF;
        }   

        // Prepare the buffer to be written again.
        peerNetData.compact();
        // And the app buffer to be read.
        peerAppData.flip();

        if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK ||
                hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP ||
                hsStatus == SSLEngineResult.HandshakeStatus.FINISHED) 
        {
            debugFile.write("Rehandshaking...");
            doHandshake();
        }

        return peerAppData.remaining();
    }

        public void closeSocket(boolean timeout){

            super.closeSocket(timeout);
            try{
                debugFile.write("Close");
                debugFile.close();
            }catch(IOException e){}
        }

    /**
     * Execute delegated tasks in the main thread. These are compute
     * intensive tasks, so there's no point in scheduling them in a different
     * thread.
     */
    private void doTasks() {
        Runnable task;
        while ((task = _engine.getDelegatedTask()) != null) {
            task.run();
        }
        hsStatus = _engine.getHandshakeStatus();
    }
        private void doHandshake() throws IOException {
            while (true) {
                SSLEngineResult res;
                System.out.println("Handshake status: " + hsStatus.toString());
                switch (hsStatus) {
                    case FINISHED:
                        initialHandshake = false;
                        return; 
                    case NEED_TASK:
                        doTasks();
                        // The hs status was updated, so go back to the switch
                        break;
                    case NEED_UNWRAP:
                        readAndUnwrap();
                        return;
                    case NEED_WRAP:

                        if (netData.hasRemaining()) {
                            return;
                        }
                        // Prepare to write
                            netData.clear();

                            try{
                            res = _engine.wrap(dummy, netData);

                            if (res.bytesProduced() == 0){
                                System.out.println("No net data produced during handshake wrap.");
                            }
                            else{
                                System.out.println("Result status: " + res.getStatus() + "Bytes porduced: " + res.bytesProduced());
                            }
                            if (res.bytesConsumed() != 0){
                                System.out.println("App data consumed during handshake wrap.");
                            }
                            hsStatus = res.getHandshakeStatus();
                        }catch(SSLException se){

                            System.out.println("Error: " + se.getMessage());
                            System.out.println("Details: " + se.getLocalizedMessage());
                            throw se;
                        }

                        System.out.println(hsStatus.toString());
                        netData.flip();
                        try{
                            int writebytes = _client.write(netData);
                            System.out.println("Number of bytes sent: " + writebytes);
                            if (netData.hasRemaining()) {
                                System.out.println("netdata has remaining");
                            }
                         }
                         catch(Exception e){
                             System.out.println(e.getMessage());
                         } 
                        break;
                    case NOT_HANDSHAKING:
                        assert false : "doHandshake() should never reach the NOT_HANDSHAKING state";
                        return;
            }
        }
    }


}

The SSLEngine严重难以使用。您的代码有几个问题。

例如NEED_WRAP意味着引擎想要写一些东西。如果传出的网络缓冲区中有数据,它不会写入任何内容,因此,如果是这种情况,您需要把它写出来从而清空缓冲区,然后进行换行。仅仅忽略条件是不够的。

其中还有一个微妙之处FINISHEDstatus:在每次返回的握手状态中设置SSLEngine method except getHandshakeStatus():即这是一个非常短暂的情况。

可能还有更多,但我没有时间看,抱歉。我可以推荐SSLEngine中的章节。它包含一个工作的 SSLEngine 管理器。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在握手过程中从 SSLEngine Wrap 方法获取 SSLException 的相关文章

  • UnknownHostException:名称或服务未知

    我正在尝试使用 com squareup okhttp 中的 OkHttpClient 从 API 返回一些数据 我遇到了一些错误 我最终能够克服这些错误 但我无法克服这个主机异常错误 并且这里似乎没有任何内容足够具体到我的情况能够解决 下
  • Java中使用正则表达式确定字符串是否为URL [重复]

    这个问题在这里已经有答案了 可能的重复 检查字符串是否为有效 URL 的最佳正则表达式是什么 https stackoverflow com questions 161738 what is the best regular express
  • 点击 Java Web 服务:curl 或 URLConnection

    我使用的 Java 服务器在以下 URL 上公开 RESTful API http localhost 8080 my server 文档建议使用curl用于提交简单的PUT请求 文件上传 并强烈建议用户使用与示例中提供的完全相同的参数 所
  • 向下投射通用元素类型

    public class ConfigControlBase
  • 将数据传递到表单时的重定向后获取?

    我有几个场景 servlet 需要将数据从数据库检索到的记录传递到 JSP 中的表单 目前 我将此信息存储在请求中 使用 RequestDispatcher 转发到页面 一切都很好 然而 这不符合 PRG 模式 AFAIK 并且当然意味着刷
  • Spring Security 中 Web 忽略和 Http 允许之间的区别?

    这两种方法有什么区别 Override protected void configure HttpSecurity http throws Exception http authorizeRequests antMatchers api p
  • Java如何从字符串实例化一个类[重复]

    这个问题在这里已经有答案了 可能的重复 在 Java 中从变量创建新类 https stackoverflow com questions 1268817 create new class from a variable in java 我
  • 搜索 JTable 时 - 未获得正确的 ID

    所以我尝试在搜索名称后单击表 然后在其他表中编辑它 问题是我没有获得正确的 ID 而只获得第一个 ID JTable https i stack imgur com TnNIq png 搜索行动 https i stack imgur co
  • Java 应用程序可以检测到调试器已连接吗?

    我知道 jvm 启动选项可以让 jvm 等待附加调试器 这不是我在这里的意思 是否有可能从 Java 代码中也检测调试器的附件 以便我可以例如编写一个正在执行某些操作的 脚本 然后在某个时刻让我的应用程序等待调试器 不会 这些选项是 JVM
  • 未找到证书管理器证书并且未创建挑战

    我跟着https docs cert manager io en venafi tutorials quick start index html https docs cert manager io en venafi tutorials
  • SSL 和会话劫持/固定

    快问 SSL 是否完全防止会话劫持 固定 谢谢 不可以 例如 在以下情况下可能会发生劫持 被黑客入侵的 CA 根签署无效证书 该证书可用于发起中间人攻击 被黑客攻击的域名所有者电子邮件收件箱使黑客有可能购买经过域名验证的证书 错误的密钥策略
  • 此代码签名证书对于签名小程序有效吗?

    我们购买了代码签名证书来签名小程序 但在签名小程序时出现以下错误 C CM WEB INF gt jarsigner keystore code signing keystore C CM SweetApplet jar code sign
  • 如何仅使用命令行运行 Maven 创建的 jar 文件

    我需要一些帮助来尝试使用命令行运行以下 Maven 项目 https github com sarxos webcam capture https github com sarxos webcam capture webcam captur
  • Preg_match PHP 到 java 的翻译

    我在将 php preg match 转换为 java 时遇到一些问题 我以为我的一切都是正确的 但它似乎不起作用 这是代码 原始PHP Pattern for 44 Character UUID pattern 0 9A F 44 if
  • Java:字符串连接和变量替换的最佳实践[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 在 Java 中连接字符串和添加变量值的方法有太多 我应该如何选择一个 优点 缺点 最佳用例等 MessageFormat forma
  • Java - 修剪字节数组中的尾随空格

    我有与此类似的字节数组 77 83 65 80 79 67 32 32 32 32 32 32 32 大致等于 M S A P O C when printed as chars 现在我想修剪尾随空白 使其看起来像 77 83 65 80
  • Eiffel 中的错误处理示例

    我在 Eiffel 中找不到任何实际的错误处理示例 我只找到一些例子 要么是微不足道的 要么完全忽略错误 要么将错误处理留给读者 我有兴趣了解在没有异常的情况下错误如何通过调用堆栈 例如 我想知道发送网络请求的应用程序如何通知用户在调用链中
  • 注意通知持续时间

    是否可以将抬头通知的持续时间设置为无限 现在它只显示 5 秒 已经尝试过不同的事情 例如更改类别 但持续时间始终为 5 秒 这是我的代码 Notification notification notificationBuilder setCa
  • 使用 Jsoup 选择没有类的 HTML 元素

    考虑一个像这样的 html 文档 div p p p p p class random class name p div 我们怎样才能选择所有p元素 但不包括p元素与random class name class Elements ps b
  • Jsplitpane 自动调整大小

    我有一个 JSPlitPane 它们之间有 50 的分隔线 这工作正常 但是 当我在右侧添加一些 JLabels 时 jsplitpane 会忽略我的 50 分隔符 左侧窗格会增加其大小 并会挤压右侧窗格 为什么会发生这种情况以及如何解决

随机推荐