Попытка туннелирования через https через прокси-сервер, созданный с нуля браузера, никогда не отвечает? - PullRequest
0 голосов
/ 21 июня 2011

По разным причинам возникла необходимость в создании собственного прокси. Все работает как надо через HTTP. Как только мы получаем CONNECT для туннелирования через SSL, это когда все идет не так. Логически мы выполняем анализ CONNECT для хоста и порта, чтобы мы знали, куда мы отправляем будущие ssl-запросы, и создаем запрос для отправки обратно в браузер, заявляя, что мы успешно сделали ssl-рукопожатие следующим образом:

HTTP / 1.0 200 Установлено соединение \ r \ nПрокси-агент: тест \ r \ n \ r \ n

Мы ожидаем, что браузер, получив это успешное сообщение, отправит нам следующий запрос https. Однако вместо этого нам снова и снова отправляется другой запрос CONNECT. Ясно, что он не похож на ответ, который мы отправляем обратно. Проблема в том, что я не совсем уверен, почему? Нужно ли возвращать ответ через сокет https? Я просто недостаточно понимаю этот процесс, чтобы двигаться вперед.

Вот мой класс сервера:

public class HttpServer extends Observable implements IWebServer, Runnable
{
int Port = -1;
int State = HttpState.IDLE;
ArrayList<WebTransactionEvent> History = new ArrayList<WebTransactionEvent>();
ArrayList<HttpService> myServices = new ArrayList<HttpService>();

SocketChannel myChannel = null;
boolean needResponse = false;
boolean shouldStop;
Logger logger = OpsToolsLogger.getLogger(HttpServer.class.getName());
Selector selector ;
static Hashtable<String, HttpServer> myInstances = new Hashtable<String, HttpServer>(); 
Hashtable<HttpTransaction, HttpService> myTaskTable = new Hashtable<HttpTransaction, HttpService>(); 
Vector<HttpTransaction> transactionQueue = new Vector<HttpTransaction>(); 

private HttpServer(){}

private HttpServer(int Port) 
{
    logger.log(Level.WARNING, "HttpServer: startup - listening to port: " + Port);
    this.Port = Port;
    shouldStop = false;

    // Create the selector
    try {
            selector = Selector.open();
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            serverChannel.configureBlocking(false);
            serverChannel.socket().bind(new InetSocketAddress(Port));
            this.registerSocket(serverChannel);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    new Thread(this).start();
}

public static HttpServer getInstance(String port)
{
    if( !myInstances.containsKey( port ) )
    {   
        myInstances.put( port, new HttpServer(Integer.parseInt(port)));
    }

    return myInstances.get(port);
}

public int getState()
{
    return State;
}

public void stop()
{
    shouldStop = true;
}

public boolean needResponse()
{
    return needResponse;
}

public HttpTransaction getNextTransaction()
{
    if(transactionQueue.isEmpty())
    {
        return null;
    }
    //System.out.println("grabbing next trans");
    HttpTransaction temp = transactionQueue.firstElement();
    transactionQueue.remove(0);//pop trans from  queue
    return temp;
}

public void dropTransaction()
{
    myTaskTable.clear();
    needResponse = false;

}

public synchronized boolean respond(HttpTransaction transaction, IHttpResponse editedResponse, boolean closeConnection)
{
    logger.log(Level.FINE, "HttpServer: responding ");
    needResponse = false;
    if(myTaskTable.isEmpty())
    {
        return false;
    }

    //see if there isn't a service object registered with that transaction
    if(!myTaskTable.containsKey(transaction))
    {
        return false;
    }

    State = HttpState.SENDING_RESPONSE;
    ManipulatedHttpTransaction myTrans = (ManipulatedHttpTransaction) transaction;
    HttpResponse response = (HttpResponse) editedResponse;
    myTrans.setManipulatedResponse( response );
    HttpService serv = myTaskTable.get(transaction);
    if(!serv.respond(myTrans.getManipulatedResponse(), closeConnection))
    {
        History.add( new WebTransactionEvent( myTrans, WebTransactionEvent.TRANSACTION_ERROR ) );
        return false;
    }

    myTaskTable.remove(transaction);

    History.add( new WebTransactionEvent( myTrans, WebTransactionEvent.TRANSACTION_COMPLETED ) );

    needResponse = !myTaskTable.isEmpty();

    return true;
}

public void registerSocket(ServerSocketChannel theSocket)
{
    try {
        theSocket.register(selector, SelectionKey.OP_ACCEPT);
    } catch (ClosedChannelException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

@Override
public void run()
{
    try {

        while (!shouldStop ) {
            // Wait for an event
            selector.select();

            // Get list of selection keys with pending events
            Iterator it = selector.selectedKeys().iterator();

            // Process each key
            while (it.hasNext()) {
                // Get the selection key
                SelectionKey selKey = (SelectionKey)it.next();

                // Remove it from the list to indicate that it is being processed
                it.remove();

                // Check if it's a connection request
                if (selKey.isAcceptable()) {
                    // Get channel with connection request
                   ServerSocketChannel ssChannel = (ServerSocketChannel)selKey.channel();
                   SocketChannel theChannel =  ssChannel.accept();
                   if(theChannel != null)
                    {

                        logger.log(Level.FINEST, "HttpServer: Connection established");

                        try
                        {
                            theChannel.configureBlocking(false);
                        }
                        catch(Exception e)
                        {
                            logger.log(Level.WARNING, "myChannel = null ( configureBlocking() )");
                            //bytesRead = -1;
                        }

                        myServices.add( new HttpService(this, theChannel ) );
                        needResponse = true;

                    }

                    //needResponse = !myTaskTable.isEmpty();                
                    //System.out.println("need response: "+ needResponse);

                }
            }
        }
    } catch (IOException e) {
    }
    //shutdown
    logger.log(Level.WARNING, "Server stopping - " + Port);
}

public ArrayList<WebTransactionEvent> getHistory() 
{
    return new ArrayList<WebTransactionEvent>(History);
}

public boolean switchServerToSSL()
{
    //HttpService tempService = myTaskTable.get(PendingTransaction);
    //tempService.useSSL = true;
    return true;
}

/**
 * Adds the transaction from browser to the transaction queue and also ties it to a service by adding it to myTasks map
 * @param myTrans
 * @param httpService
 */
public void addTransaction(ManipulatedHttpTransaction myTrans,
        HttpService httpService) {
    // TODO Auto-generated method stub
    //ensure vector has room to add another transaction
    if(transactionQueue.capacity() <= transactionQueue.size())
        transactionQueue.ensureCapacity(transactionQueue.size() * 2);

    transactionQueue.add(myTrans);//add transaction to queue
    myTaskTable.put(myTrans, httpService);//tie the transaction toits service
   // System.out.println("server notifying proxy: " + myTrans.getFullURL());
    this.setChanged();
    this.notifyObservers(myTrans);
}

}

Вот часть прокси, которая обрабатывает CONNECT:

if(tempTransaction.getOriginatingRequest().getMethod().contentEquals("CONNECT"))
            {
                /*tell the browser that the connection exists
                 * 
                 * Each time you connect to an SSL-protected website, Burp generates a server certificate for that host, signed by the CA certificate
                 * 
                 * The server certificates presented to the client (i.e. a web browser) are dynamically generated/signed by the proxy and contain most of the same fields as the original webserver certificate. The subject DN, serial number, validity dates, and extensions are preserved. However, the issuer DN is now set to the name of the proxy's self-signed 
                 * certificate and the public/private keys of the proxy are used in creating the forged certificate. These forged certificates are cached (in memory) by the proxy, for better performance
                 */
                HttpResponse tunnelResponse = new HttpResponse("HTTP/1.0 200 Connection established\r\nProxy-agent: Ops Assistant\r\n\r\n");
                tempTransaction.setResponse(tunnelResponse);

                if(!finishResponse2(tempTransaction,tempTransaction.getResponse(), false));
                {
                    //close the connection
                }

                myServer.switchServerToSSL();
            }

Здесь раздел отправляет запрос обратно в браузер:

 public boolean respond(IHttpResponse response, boolean closeConnection)
{
    isCloseConnectionRequested = closeConnection;

    try 
    {
        if(useSSL)
        {
            ByteBuffer tmpBuffer = response.getData();
            tmpBuffer.position(0);
            myConnection.SecureWrite( tmpBuffer );
        }
        else
        {
            ByteBuffer tmpBuffer = response.getData();
            tmpBuffer.position(0);
            myConnection.Write(tmpBuffer);
        }
        if(closeConnection)
        {
            myChannel.close();
            myChannel = null;
        }
    }
    catch (Exception e) 
    {
        isResponded = true;
        return false;
    }

    isResponded = true;
    return true;

}

Вероятно, наиболее важный класс сокета:

 public class SocketConnection implements IConnection
   {

public SocketChannel theSocketChannel;
public InetSocketAddress theRemoteAddress;
public int TimeoutThreshold;

private int TimeOutThreshold = 30;
private SSLEngine theSSLEngine;
private SSLContext theSSLContext;
private ByteBuffer inNetworkDataBuffer;
private ByteBuffer inAppDataBuffer;
private ByteBuffer outNetworkDataBuffer;
private ByteBuffer outAppDataBuffer;

//create outbound connection to host/port
public SocketConnection(String Host, int Port ) throws IOException
{
    theRemoteAddress = new InetSocketAddress( Host, Port);
    theSocketChannel = SocketChannel.open();
    theSocketChannel.configureBlocking(false);
    theSocketChannel.connect( theRemoteAddress );
    theSocketChannel.finishConnect();
}

//use existing socket connection
public SocketConnection(SocketChannel existingChannel) throws IOException
{
    theSocketChannel = existingChannel;
    theSocketChannel.configureBlocking(false);
    theRemoteAddress = new InetSocketAddress( existingChannel.socket().getInetAddress(), existingChannel.socket().getPort() );
}

public boolean setTimeOut(int newTimeOutThreshold) 
{
    TimeOutThreshold = newTimeOutThreshold; 
    return true;
}

public void waitForSocketToConnect() throws Exception
{
    int i = 0;
    while( !this.isConnected() )
    {
        this.finishConnect();
        if(i>=3000)
        {
            throw new Exception();
        }
        i++;

        try{Thread.sleep(10);}catch(Exception e){}
    }
}

public boolean Write( ByteBuffer DataToSend )
{
    try 
    {
        //DataToSend.flip();
        int numBytesWritten = theSocketChannel.write(DataToSend);
        try
        {
            DataToSend.compact();
        }
        catch (ReadOnlyBufferException e)
        {
            DataToSend.rewind();
        }
    } 
    catch (IOException e)
    {
        // Connection may have been closed
    }

    return true;
}

public ByteBuffer Read()
{   
    ByteBuffer ResponseBytes = ByteBuffer.allocateDirect(0);

    try 
    {
        ByteBuffer netBuffer = ByteBuffer.wrap(new byte[10000]);


        // Clear the buffer and read bytes from socket
        netBuffer.clear();

        int numBytesRead = theSocketChannel.read(netBuffer);
        if(numBytesRead == -1)
            return null; //-1 means we done return null as the flag
        netBuffer.flip();

        ByteBuffer tempBuffer = ByteBuffer.wrap(new byte[ ResponseBytes.limit() + netBuffer.limit() ]);
        ResponseBytes.position(0);
        netBuffer.position(0);
        tempBuffer.put(ResponseBytes);
        tempBuffer.put(netBuffer);
        netBuffer.flip();
        ResponseBytes = tempBuffer;

    } 
    catch (IOException e) 
    {
        // Connection may have been closed
        e = e;
        return ByteBuffer.wrap( e.getMessage().getBytes() );
    }

    return (ByteBuffer) ResponseBytes.flip();

}

public boolean SecureWrite( ByteBuffer DataToSend )
{
    boolean writeSuccess = true;

    try
    {
        //if we don't have a SSLEngine make one
        if(theSSLEngine==null)
        {
            setupSSL();
        }

        //Convert Data 
        outAppDataBuffer.clear();
        outAppDataBuffer.put(DataToSend);
        outAppDataBuffer.flip();
        SSLEngineResult sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
        outAppDataBuffer.compact();  
        //outNetworkDataBuffer.flip();
        //int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
        if(sslResult.getStatus() == SSLEngineResult.Status.OK)
        {
            if(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
            {   
                // Write bytes
                outNetworkDataBuffer.flip();
                int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
                outNetworkDataBuffer.compact();

                if(finishHandshake(sslResult))
                {
                    DataToSend.rewind();
                    return SecureWrite(DataToSend);
                }
                else
                {
                    return false;
                }
            }
            else
            {
                // Write bytes
                outNetworkDataBuffer.rewind();
                Write(outNetworkDataBuffer);
            }

        }
        else
        {

        }

    }
    catch(Exception e)
    {
        writeSuccess = false;
    }

    return writeSuccess;

}

public ByteBuffer SecureRead() throws ReadTimedOutException
{
    int timeElapsed = 0;
    ByteBuffer ResponseBytes = ByteBuffer.allocateDirect(0);

    try 
    {
        //if we don't have a SSLEngine make one
        if(theSSLEngine==null)
        {
            setupSSL();
        }

        int consumedCount = 0;
        SSLEngineResult sslResult;
        do
        {
            //inNetworkDataBuffer.clear();
            inNetworkDataBuffer.put( Read() );
            inNetworkDataBuffer.flip();
            sslResult = theSSLEngine.unwrap( inNetworkDataBuffer, inAppDataBuffer );
            consumedCount += sslResult.bytesConsumed();     
            inNetworkDataBuffer.compact();

            if( sslResult.getStatus() == SSLEngineResult.Status.OK ) 
            {

                if(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
                {

                    if(finishHandshake(sslResult))
                    {
                        return SecureRead();
                    }
                    else
                    {
                        return ByteBuffer.allocateDirect(0);
                    }
                }
                else
                {
                    timeElapsed = 0;
                    inAppDataBuffer.flip();
                    ByteBuffer tempBuffer = ByteBuffer.wrap(new byte[ ResponseBytes.limit() + inAppDataBuffer.limit() ]);
                    ResponseBytes.position(0);
                    inAppDataBuffer.position(0);
                    tempBuffer.put(ResponseBytes);
                    tempBuffer.put(inAppDataBuffer);
                    inAppDataBuffer.flip();
                    ResponseBytes = tempBuffer;
                    ResponseBytes.flip();       
                }
            }
            else
            {
                //the status wasn't ok
                timeElapsed++;
            }
        }while(consumedCount < inNetworkDataBuffer.limit() && sslResult.getStatus() != SSLEngineResult.Status.OK);


    }
    catch (Exception e) 
    {
        System.out.println(e.toString());
    }

    if(timeElapsed>=TimeOutThreshold)
    {
        throw new ReadTimedOutException();
    }


    return ResponseBytes;
}

public boolean Disconnect()
{
    try
    {
        theSocketChannel.close();
    }
    catch(Exception e)
    {
        return false;
    }

    return true;
}

public boolean isClosed()
{
    return !theSocketChannel.isOpen();
}

@Override
public String getHost() 
{
    return theRemoteAddress.getHostName();
}

@Override
public int getPort() 
{
    return theRemoteAddress.getPort();
}

public boolean isConnected() 
{
    return theSocketChannel.isConnected();
}


@Override
public boolean hasSecure() 
{
    return true;
}

public boolean finishConnect() throws Exception
{
    return theSocketChannel.finishConnect();
}

private void setupSSL() throws NoSuchAlgorithmException, KeyManagementException
{

    //create a new SSLEngine instance
    System.setProperty( "javax.net.debug", "ssl");
    TrustManager[] tm = new TrustManager[] { new NaiveTrustManager() };
    SSLContext theSSLContext = SSLContext.getInstance ("TLS");
    theSSLContext.init( new KeyManager[0], tm, new SecureRandom( ) );

    theSSLEngine = theSSLContext.createSSLEngine( theRemoteAddress.getHostName(), theRemoteAddress.getPort());
    theSSLEngine.setUseClientMode(true);

    inNetworkDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getPacketBufferSize()]);
    inAppDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getApplicationBufferSize()]);
    outNetworkDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getPacketBufferSize()]);
    outAppDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getApplicationBufferSize()]);

}

private boolean finishHandshake(SSLEngineResult sslResult)
{   
    boolean bFinished = false;

    while(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.FINISHED)
    {
        if( sslResult.getStatus() == SSLEngineResult.Status.CLOSED ) 
        {   
            bFinished = false;
            //break;
        }


        if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK)
        {
            Runnable task;
            while ((task=theSSLEngine.getDelegatedTask()) != null)
            {
                task.run();
            }

            try 
            {
                //outNetworkDataBuffer.flip();
                sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
                //outNetworkDataBuffer.compact();
            } 
            catch (SSLException e) 
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
        else if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP)
        {
            try 
            {
                outAppDataBuffer.flip();
                sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
                outAppDataBuffer.compact();
            } catch (SSLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if((sslResult.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) || (outNetworkDataBuffer.position() > 0))
            {
                try 
                {
                    outNetworkDataBuffer.flip();
                    int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
                    outNetworkDataBuffer.compact();
                } 
                catch (Exception e) 
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        else if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
        {
            try 
            {
                int numBytes;
                //read data from the socket into inNetworkBuffer                    
                inNetworkDataBuffer.flip();
                sslResult = theSSLEngine.unwrap( inNetworkDataBuffer, inAppDataBuffer );
                inNetworkDataBuffer.compact();
                if(theSSLEngine.isInboundDone())
                {

                }
                else
                {
                    numBytes = theSocketChannel.read(inNetworkDataBuffer);
                    numBytes = numBytes;
                }

            } 
            catch (Exception e) 
            {
                e.printStackTrace();
                return false;
            }
        }
    }

    return true;
}

} * * тысяча двадцать-один

У кого-нибудь есть советы о том, как наилучшим образом установить это рукопожатие с помощью браузера?

1 Ответ

2 голосов
/ 21 июня 2011

Вы читали Интернет-проект ? СОЕДИНЕНИЕ получено в виде открытого текста. Вы формируете восходящее соединение и возвращаете ответ «HTTP / 1.0 200 Соединение установлено». После этого прокси не обрабатывает запросы и ответы, он просто копирует байты в обоих направлениях, какими бы они ни были. В частности, прокси-сервер никоим образом не связан с SSL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...