当前位置: 首页 > 工具软件 > github-client > 使用案例 >

java SSLEngine访问https网站github.com,客户端client-side-demo

詹杰
2023-12-01

java sslengine 访问github.com https协议

关于sslengine,理解为就是你的程序与网站直接的一个门,sslengine负责握手协议handshake与加解密包数据(发送给服务器的数据调用wrap() 解密返回的数据unwrap())

sslengine的两种状态
sslengine.getHandshakeStatus()是握手状态,根据不同状态有不同操作

SSLEngineResult.Status是加解密方法返回的结果,wrap(),unwrap(),不是简单的boolean,是不同结果有不同操作

//MySSLEngineDemo.java

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.security.KeyStore;
import java.security.Security;

public class MySSLEngineDemo {

    public static SSLEngine prepareEngine(String host, int port) throws Exception {
        char[] passphrase = "changeit".toCharArray();

        SSLContext ctx = SSLContext.getInstance("TLSv1.2");
        String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
        if (algorithm == null) {
            algorithm = "SunX509";
        }
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
        KeyStore ks = ks = KeyStore.getInstance("JKS");

        String JAVA_HOME = System.getenv("JAVA_HOME");
        ks.load(new FileInputStream(JAVA_HOME + "/jre/lib/security/cacerts"), passphrase);

        kmf.init(ks, passphrase);
        ctx.init(kmf.getKeyManagers(), null, null);
        SSLEngine sslEngine = ctx.createSSLEngine(host, port);
        sslEngine.setUseClientMode(true);

        return sslEngine;
    }

    public static SocketChannel prepareChannel(String host, int port) throws Exception {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        socketChannel.connect(new InetSocketAddress(host, port));

        return socketChannel;
    }

    public static void doHandshake(SocketChannel socketChannel, SSLEngine engine,
                                   ByteBuffer myNetData, ByteBuffer peerNetData) throws Exception {

        // Create byte buffers to use for holding application data
        int appBufferSize = engine.getSession().getApplicationBufferSize();
        ByteBuffer myAppData = ByteBuffer.allocate(appBufferSize);
        ByteBuffer peerAppData = ByteBuffer.allocate(appBufferSize);

        // Begin handshake
        engine.beginHandshake();
        SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();

        // Process handshaking message
        while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
                hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {

            switch (hs) {

                case NEED_UNWRAP:
                    // Receive handshaking data from peer
                    if (socketChannel.read(peerNetData) < 0) {
                        // The channel has reached end-of-stream
                    }

                    // Process incoming handshaking data
                    peerNetData.flip();
                    SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
//                    MySSlEngine.print(peerAppData);
                    peerNetData.compact();
                    hs = res.getHandshakeStatus();

                    // Check status
                    switch (res.getStatus()) {
                        case OK:
                            // Handle OK status
                            break;

                        // Handle other status: BUFFER_UNDERFLOW, BUFFER_OVERFLOW, CLOSED
                    }
                    break;

                case NEED_WRAP:
                    // Empty the local network packet buffer.
                    myNetData.clear();

                    // Generate handshaking data
                    res = engine.wrap(myAppData, myNetData);
//                    MySSlEngine.print(myNetData);
                    hs = res.getHandshakeStatus();

                    // Check status
                    switch (res.getStatus()) {
                        case OK:
                            myNetData.flip();

                            // Send the handshaking data to peer
                            while (myNetData.hasRemaining()) {
                                socketChannel.write(myNetData);
                            }
                            break;

                        // Handle other status:  BUFFER_OVERFLOW, BUFFER_UNDERFLOW, CLOSED

                    }
                    break;

                case NEED_TASK:
                    // Handle blocking tasks
                    Runnable task;
                    while ((task = engine.getDelegatedTask()) != null) {
                        new Thread(task).start();
                    }
                    hs = engine.getHandshakeStatus();
                    break;

                // Handle other status:  // FINISHED or NOT_HANDSHAKING
            }
        }

        // Processes after handshaking
        System.out.println("after handshaking");
    }

    private static void runDelegatedTasks(SSLEngineResult result,
                                          SSLEngine engine) throws Exception {

        if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
            Runnable runnable;
            while ((runnable = engine.getDelegatedTask()) != null) {
                System.out.println("\trunning delegated task...");
                runnable.run();
            }
            SSLEngineResult.HandshakeStatus hsStatus = engine.getHandshakeStatus();
            if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
                throw new Exception("handshake shouldn't need additional tasks");
            }
            System.out.println("\tnew HandshakeStatus: " + hsStatus);
        }
    }

    public static void sendRequest(String host, int port, String path,
                                   SSLEngine sslEngine, ByteBuffer myAppData, ByteBuffer myNetData, SocketChannel socketChannel)
            throws Exception {
        StringBuilder header = new StringBuilder();
        header.append("GET " + path + " HTTP/1.1\r\n");
        header.append("Host: " + host + "\r\n");
        header.append("Connection: keep-alive\r\n");
        header.append("\r\n");

        myAppData.put(header.toString().getBytes());
        myAppData.flip();

        myNetData.clear();
        SSLEngineResult res = sslEngine.wrap(myAppData, myNetData);
        if (res.getStatus() == SSLEngineResult.Status.OK) {
            myNetData.flip();
            socketChannel.write(myNetData);
        }
    }

    public static void main(String[] args) throws Exception {

//        System.setProperty("javax.net.debug", "all");
        String host = "github.com";
        int port = 443;
        String path = "/";

        SSLEngine sslEngine = MySSLEngineDemo.prepareEngine(host, port);
        SocketChannel socketChannel = MySSLEngineDemo.prepareChannel(host, port);

        while (!socketChannel.finishConnect()) {
//            System.out.println("wait...............");
        }
        System.out.println("connected..............");

        //prepare the four ByteBuffer
        SSLSession session = sslEngine.getSession();
        ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
        ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
        ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
        ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());

        MySSLEngineDemo.doHandshake(socketChannel, sslEngine, myNetData, peerNetData);

        MySSLEngineDemo.sendRequest(host, port, path, sslEngine, myAppData, myNetData, socketChannel);

        int num = 0;
        int readCount=0;
        while ((num = socketChannel.read(peerNetData)) != -1) {

            if (num > 0) {

                peerNetData.flip();
                SSLEngineResult res = sslEngine.unwrap(peerNetData, peerAppData);
                System.out.println(res.getStatus());

                if (res.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
                    peerNetData.compact();
                    continue;
                }
                if (res.getStatus() == SSLEngineResult.Status.OK) {
                    peerNetData.compact();

                    // Use peerAppData
                    int len = peerAppData.position();
                    int count = 0;
                    while (len > 0) {
                        System.out.print((char) peerAppData.get(count));
                        count++;
                        len--;
                    }
                    peerAppData.clear();


                    //uncomment below will cause SSLException Unsupported record version
                    //or Tag mismatch
//                    peerNetData.clear();
                    System.out.println();
                    System.out.println("readCount:"+readCount++ +" num:"+num+".......");
                    System.out.println(peerNetData+"......................"+session.getPacketBufferSize());
                }
            }
        }
    }
}

运行后,控制台可以看到大部分html的代码,但是没有返回全部所有数据

sslEngine.unwrap(peerNetData, peerAppData) 会读取peerNetData内从服务器返回的加密数据,【并不是全部读取解密】因为数据是分段加密了,peerNetData内可能是部分加密数据就会报BUFFER_UNDERFLOW,说明加密数据块不完整,需要从服务器继续读数据,

 类似资料: