Java network programming - secure network communication

Introduction to SSL

SSL (Secure Socket Layer) is a protocol that ensures secure communication between two nodes on the network. The IETF (Interet Engineering Task Force) international organization has standardized SSL and formulated the RFC2246 specification, which is called Transport Layer Security (TLS).

Both SSL and TLS are based on TCP/IP. Some application layer protocols, such as HTTP and IMAP, can use SSL to ensure secure communication. HTTP built on the SSL protocol is called the HTTPS protocol. The default port used by HTTP is 80, while the default port used by HTTPS is 443

SSL uses encryption technology to achieve secure communication, ensuring the confidentiality and integrity of communication data, and ensuring that both communicating parties can verify the identity of the other party.

1. Encrypted communication

When the client communicates with the server, the communication data may be illegally intercepted by other computers on the network. SSL uses encryption technology to achieve secure transmission of information between both parties in the session. The basic principle of encryption technology is that when data is sent from one end to another, the sender first encrypts the data and then sends it to the receiver. In this way, encrypted data is transmitted over the network. If someone illegally intercepts this batch of data on the Internet, the real original data cannot be obtained because there is no decryption key. After receiving the encrypted data, the recipient first decrypts the data and then processes it.

2. Security certificate

In addition to encrypting data communication, SSL also uses an identity authentication mechanism to ensure that both parties to the communication can verify the true identity of the other party. This is very similar to how we use ID cards to prove our identity in life. For example, when you go to a bank to withdraw money, and you claim to be Zhang San, how do you make the other party believe your identity? The most effective way is to show your ID card. Everyone has a unique ID card, which records your real information. ID cards are issued by national authorities and are not allowed to be forged. As long as your ID card cannot be counterfeited or copied by others, you can prove your identity by showing it.

Individuals can prove their identity through their ID cards, and for an organization, such as a shopping mall, they can prove their identity through their business license. Business licenses are also issued by national authorities and are not allowed to be forged, which ensures the credibility of the business license

SSL uses a security certificate to prove the identity of the client or server. When the client communicates with the server through a secure connection, the server will first present its security certificate to the client. This certificate declares that the server is safe and is indeed this server. A certificate is unique worldwide and cannot be counterfeited by other illegal servers. The security certificate can be compared to an electronic ID card.

For individual customers, going to a recognized authority to obtain a security certificate is cumbersome. In order to expand the customer base and facilitate customer access, many servers do not require customers to present security certificates. In some cases, the server will also ask the customer to present a security certificate in order to verify the customer's identity. This is mainly in B2B transactions.

There are two ways to obtain a security certificate, one is to obtain a certificate from an authority, and the other is to create a self-signed certificate

2.1 Obtain a certificate from an authority

Security certificates are issued by internationally authoritative certificate authorities, which guarantee the credibility of the certificate. When applying for a security certificate, a fee must be paid. A security certificate is only valid for one IP address. If there are multiple IP addresses in the user's system environment, a security certificate must be purchased for each IP address.

2.2 Create a self-signed certificate

In some cases, the communicating parties only care that the data can be transmitted securely on the network and do not require the other party to authenticate. In this case, a self-signed certificate can be created. Just like a business card made by the user himself, it lacks authority and cannot achieve the purpose of identity authentication. When you hand over your business card to the other party, it claims that you are the CEO of a large company. It is up to the other party to judge whether you believe it or not.

Since a self-signed certificate doesn't effectively prove your identity, what's the point? Technically, whether it is a certificate obtained from an authoritative organization or a certificate made by oneself, the encryption technology used is the same. Using these certificates, securely encrypted communications can be achieved.

3. SSL handshake

A security certificate contains both the key used to encrypt data and the digital signature used to prove identity. The security certificate uses public key encryption technology. Public key encryption refers to using a pair of asymmetric keys for encryption or decryption. Each pair of keys consists of a public key and a private key. The public key is widely published, and the private key is hidden and not made public. Data encrypted with the public key can only be decrypted by the private key, conversely, data encrypted with the private key can only be decrypted by the public key

When the client communicates with the server, an SSL handshake must first be performed. The SSL handshake mainly completes the following tasks:

  • Negotiate the cipher suite used. The cipher suite includes a set of encryption parameters that specify the encryption algorithm and key length.
  • Verify the identity of the other party, this operation is optional
  • Determine the encryption algorithm used

The SSL handshake process uses asymmetric encryption to transfer data to establish a secure session. After the SSL handshake is completed, the communicating parties will use symmetric encryption to transfer the actual application data. The so-called symmetric encryption means that both communicating parties use the same key to encrypt data.

The specific process of SSL handshake is as follows:

  1. The client sends its SSL version number, encryption parameters, session-related data, and other necessary information to the server
  2. The server sends its own SSL version number, encryption parameters, session-related data and other necessary information to the client, as well as the server's certificate. If the server needs to verify the client's identity, the server also issues a request for a security certificate from the client.
  3. The client verifies the server certificate. If the verification fails, it prompts that the SSL connection cannot be established. If successful, continue to the next step
  4. The client generates a pre-master secret for this session, encrypts it with the public key attached to the server's security certificate, and sends it to the server.
  5. If the server requires verification of the client's identity, the client will also need to sign some additional data and send it to the server together with the client certificate.
  6. If the server requires verification of the client's identity, check that the CA that signed the client's certificate is trusted. If it is not in the trust list, end this session. If the check passes, the server uses its own private key to decrypt the received preliminary master password and uses it to generate the master secret for this session through some algorithm.
  7. Both the client and the server use this master password to generate a session key (symmetric key) for this session. This session key is used for any messages delivered after the SSL handshake between the two parties. The main reason for this is that the computational complexity of symmetric encryption is more than an order of magnitude lower than that of asymmetric encryption, which can significantly improve the computing speed of the conversation between the two parties.
  8. The client notifies the server that all messages sent thereafter will be encrypted using this session key, and notifies the server that the client has completed this SSL handshake.
  9. The server notifies the client that all subsequent messages will be encrypted using this session key and will be communicated as if the server has completed this SSL handshake.
  10. This handshake process is over and the session has been established. During the subsequent session, both parties use the same session key to encrypt and decrypt the sent and received information respectively.
4. Create a self-signed security certificate

JDK provides keytool, a tool for making certificates. Its location is: <JDK root directory>\bin\keytool.exe

The keytool tool proposes the concept of a keystore, which can contain multiple entries, each entry including a self-signed security certificate and a pair of asymmetric keys

The command to create a keystore through the keytool tool is:

keytool -genkeypair -alias weiqin -keyalg RSA -keystore C:\chapter15\test.keystore
  • genkeypair: generate a pair of asymmetric keys
  • alias: specifies the alias of the entry and key pair, which is public
  • keyalg: Specifies the encryption algorithm. In this example, the general RSA algorithm is used
  • keystore: Set the storage path and file name of the keystore file

During the running process of the command, you will first be prompted to enter the password of the keystore, and then prompted to enter personal information, such as name, organizational unit and city, etc. Just enter the real information.

The following command checks the information of the test.keystore keystore and lists the information of the included entries.

keytool -list -v -keystore C:\chapter15\test.keystore -storepass "123456”

The following command exports the entry with the alias weiqin in the test.keystore keystore to a security certificate with the file name weiqin.crt. weiqin.crt The article contains the self-signed security certificate and the public key in the key pair, but does not contain the private key in the key pair.

keytool -export -alias weigin -keystore C:\chapter15\test.keystore
	-file C:\chapter15\weigin.crt -storepass “123456”

The following command deletes the entry with the alias weiqin in the test.keystore keystore

keytool -delete -alias weigin -keystore C:\chapter15\test.keystore -storepass “123456”

The following command imports the weiqin.crt security certificate into the testTrust.keystore keystore to generate an entry with the alias weiqin. This entry contains the public key in the key pair, but does not contain the private key in the key pair.

keytool -import -alias weiqin
		-keystore C:\chapter15\testTrust.keystore
		-file C:\chapter15\weiqin.crt
		-storepass "123456"

Introduction to JSSE

JSSE encapsulates the underlying complex security communication details, allowing developers to easily use it to develop secure network applications.

1. KeyStore, KeyManager and TrustManager classes

When conducting secure communication, both the client and the server are required to support the SSL or TCL protocol. Both the client and the server may need to set up security certificates to prove their identities, and also set which security certificates they trust. A more common situation is that the server only needs to set the security certificate used to prove its identity, and the client only needs to set which security certificates of the server it trusts.

The KeyStore class is used to store a keystore containing a security certificate. The following program code creates a KeyStore object, which loads the security certificate from the test.keystore keystore file.

String passphrase = "123456";
//JKS是JDK 支持的KeyStore的类型
KeyStore keyStore = KeyStore.getInstance("JKS");
char[] password = passphrase.toCharArray();
//password参数用于打开密钥库
keyStore.load(new FileInputStream("test.keystore"), password);

The task of the KeyManager interface is to select the security certificate used to prove its identity and send it to the other party. KeyManagerFactory is responsible for creating KeyManager objects, such as

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, password);
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();

The task of the TrustManager interface is to decide whether to trust the other party's security certificate. TruesManagerFactory is responsible for creating TrustManager objects, such as

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init (keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
2. SSLContext class

The SSLContext class is responsible for setting various information related to secure communication, such as the protocol used (SSL or TLS), its own security certificate and the other party's security certificate. SSLContext is also responsible for constructing SSLServerSocketFactory, SSLSocketFactory and SSLEngine objects

The following program code creates and initializes an SSLContext object, and then creates an SSLServerSocketFactory object from it

SSLContext sslCtx = SSLContext.getInstance("TLS");//采用TLS协议
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLServerSocketFactory ssf = sslCtx.getServerSocketFactory();

The init method of SSLContext is defined as follows

public final void init(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws KeyManagementException
  • The parameter random is used to set a secure random number. If the parameter is null, the init method will use the default SecureRandom implementation.
  • If the parameter km is null, the init method will create a default KeyManager object and its associated KeyStore object. The KeyStore object gets the security certificate from the system property javax.net.ssl.keyStore. If no such system property exists, the KeyStore object content is empty
  • If the parameter tm is null, the init method will create a default TrustManager object and its associated KeyStore object. The KeyStore object obtains the security certificate according to the following steps:
    • First try to get the security certificate from the system property javax.net.ssl.trustStore
    • If the previous step fails, use the <JDK root directory>/lib/security/jssecacerts file as the security certificate
    • If the previous step fails, use the <JDK root directory>/lib/security/cacerts file as the security certificate
    • If the previous step fails, the contents of the KeyStore object are empty
3. SSLServerSocketFactory class

The SSLServerSocketFactory class is responsible for creating SSLServerSocket objects

SSLServerSocket serverSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(8000); // 监听端口8000

There are two ways to create SSLServerSocketFactory objects:

  • Call the getServerSocketFactory method of the SSLContext class
  • Call the static getDefault method of the SSLServerSocketFactory class

The static getDefault method of the SSLServerSocketFactory class returns a default SSLServerSocketFactory object, which is associated with a default SSLContext object. The implementation of the getDefault method initializes the default SSLContext object as follows

sslContext.init(null,null,null);
4. SSLSocketFactory class

The SSLSocketFactory class is responsible for creating SSLSocket objects

SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket("localhost",8000);

There are two ways to create SSLSocketFactory objects

  • Call the getSocketFactory method of the SSLContext class
  • Call the static getDefault method of the SSLSocketFactory class

The static getDefault method of the SSLSocketFactory class returns a default SSLSocketFactory object, which is associated with a default SSLContext object. The implementation of the getDefault method initializes the default SSLContext object as follows

sslContext.init(null,null,null);
5. SSLSocket class

The SSLSocket class is a subclass of the Socket class, so there are many similarities in their usage. Additionally, the SSLSocket class has methods related to secure communication

5.1 Set up encryption suite

The client and server need to negotiate the actual cipher suite used during the handshake phase. The following two situations will cause the handshake to fail:

  • There is no identical cipher suite that both parties can use
  • Although such an cipher suite exists, one or both parties do not have a security certificate to use that cipher suite.

The getSupportedCipherSuites method of the SSLSocket class returns a string array containing the Cipher suite groups supported by the SSLSocket object. The setEnabledCipherSuites(String[] suites) method of the SSLSocket class sets the usable cipher suite groups of the current SSLSocket object. The usable cipher suite groups should be a subset of the supported cipher suite groups.

The following code only enables cipher suites with high encryption strength, which can improve the security of the communication end and prohibit those communication ends that do not support strong encryption from connecting to the current communication end.

String[] strongSuites = {
    
    
	"SSL_RSA_WITH_RC4_128_MD5",
	"SSL_RSA_WITH_RC4_128_SHA",    
	"SSL_RSA_WITH_3DES_EDE_CBC_SHA"
};
sslSocket.setEnabledCipherSuites(strongSuites);
5.2 Handling handshake end events

The SSL handshake takes a long time. When the SSL handshake is completed, a HandshakeCompletedEvent event will be emitted, which is monitored by HandshakeCompletedListener. The addHandshakeCompletedListener method of the SSLSocket class is responsible for registering the HandshakeCompletedListener listener

The following example registers HandshakeCompletedListener for SSLSocket

socket.addHandshakeCompletedListener {
    
    
    new HandshakeCompletedListener() {
    
    
        public void handshakeCompleted(HandshakeCompletedEvent event) {
    
    
            System.out.println("握手结束");
            System.out.println("加密套件为:" + event.getCipherSuite());
            System.out.println("会话为:" + event.getSession());
            System.out.println("通信对方为:" + event.getSession().getPeerHost());
        }
    });
}
5.3 Managing SSL sessions

A client program may open multiple secure sockets to the same port on a server. If an SSL handshake is performed for every secure connection, communication efficiency will be greatly reduced. In order to improve the efficiency of secure communication, the SSL protocol allows multiple SSLSockets to share the same SSL session. In the same session, only the first opened SSLSocket performs the SSL handshake and is responsible for generating keys and exchanging keys. The rest of the SSLSockets share key information. Within a reasonable period of time, if a client opens multiple secure sockets to the same port on a server, JSSE automatically reuses the session

The getSession method of SSLSocket returns the session to which SSLSocket belongs. The setEnableSessionCreation(boolean flag) method of SSLSocket determines whether SSLSocket allows the creation of new sessions. The default value of flag is true. If the flag parameter is true, then for the newly created SSLSocket, if there is currently an available session, join the session directly, if there is no available session, create a new session. If flag is the false parameter, then for the newly created SSLSocket, if there is currently an available session, it will be directly added to the session. If there is no available session, then the SSLSocket cannot communicate securely with the other party.

The startHandshake method of SSLSocket explicitly performs an SSL handshake. This method has the following purposes:

  • Make the session use the new key
  • Make the session use the new cipher suite
  • Restart a session. In order to ensure that the original session is not reused, the original session should be invalidated first.
socket.getSession().invalidate();
socket.startHandshake();
5.4 Client mode

In most cases, the client does not need to confirm its identity to the server, so when a communication terminal does not need to confirm its identity to the other party, it is said to be in client mode, otherwise it is said to be in server mode. Only one party of the communicating parties can be in server mode, and the other party is in client mode.

The setUseClientMode(boolean mode) method of SSLSocket is used to set the client mode or server mode. If the mode parameter is true, it means that you are in client mode, that is, you do not need to prove your identity to the other party; if the mode parameter is false, it means that you are in server mode, that is, you need to prove your identity to the other party.

When the SSL initial handshake has begun, it is not allowed to call the seUseClientMode(boolean mode) method of SSLSocket, otherwise it will cause an exception.

When SSLSocket is in server mode, you can also decide whether to require the other party to provide identity authentication through the following methods:

  • setWantClientAuth(boolean want):: When the want parameter is true, it means that you want the other party to provide identity authentication. If the other party does not present a security certificate, the connection will not be interrupted and communication can continue.
    setNeedClientAuth(boolean need): When the need parameter is true, it means that the other party must provide identity authentication. If the other party does not present a security certificate, the connection is interrupted and communication cannot continue.
6. SSLServerSocket class

The SSLServerSocket class is a subclass of the ServerSocket class, so there are many similarities in their usage. In addition, the SSLServerSocket class also has methods related to secure communication, which have the same effect as the methods of the same name in the SSLSocket class

7. SSLEngine class

The SSLEngine class is used in conjunction with the SocketChannel class to achieve non-blocking secure communication. The SSLEngine class encapsulates the details related to secure communication and packages the application data sent by the application into network data. Packaging refers to encrypting the application data, adding SSL handshake data, and turning it into network data. The SSLEngine class can also expand the received network data into application data. Expansion refers to decrypting the network data and removing the SSL handshake data, thereby restoring it to application data that can be processed by the application. The wrap method of the SSLEngine class is responsible for packaging application data, and the unwrap method is responsible for unfolding network data.

public class SSLEngineDemo {
    
    
    
    private static boolean logging = true;
    private SSLContext sslc;
    
    private SSLEngine clientEngine; //客户端Engine
    private ByteBuffer clientOut; //存放客户端发送的应用数据
    private ByteBuffer clientIn; //存放客户端接收到的应用数据
    
    private SSLEngine serverEngine; //服务器端Engine
    private ByteBuffer serverOut; //存放服务器端发送的应用数据
    private ByteBuffer serverIn; //存放服务器端接收到的应用数据
    
    private ByteBuffer cTOs;//存放客户端向服务器端发送的网络数据
    private ByteBuffer sTOc;//存放服务器向客户海发送的网络数据
        
    //设置密钥库文件和信任库文件以及口令
    private static String keyStoreFile = "test.keystore";
    private static String trustStorefile = "test.keystore";
    private static String passphrase = "123456";
    
    public static void main(String args[]) throws Exception {
    
    
        SSLEngineDemo demo = new SSLEngineDemo();
        demo.runDemo();
    }
    
    /**初始化SSLContext*/
    public SSLEngineDemo() throws Exception {
    
    
        KeyStore ks = KeyStore.getInstance("JKS");
        KeyStore ts = KeyStore.getInstance("JKS");
        
        char[] password = passphrase.toCharArray();
        ks.load(new FileInputStream(keyStoreFile), password);
        ts.load(new FileInputStream(trustStoreFile), password);
        
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, password);
        
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(ts);
        
        SSLContext sslCtx = SSLContext.getInstance("TLS");
        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        sslc = sslCtx;
    }
    
    private void runDemo() throws Exception {
    
    
        boolean dataDone = false;
        
        /*创建客户端以及服务器端的SSLEngine*/
        serverEngine = sslc.createSSLEnqine();
        serverEngine.setUseClientMode(false);
        serverEngine.setNeedClientAuth(true);
        clientEngine = sslc.createSSLEngine("client", 80);
        clientEngine.setUseClientMode(true);
        
        /*创建客户端以及服务器的应用冲区和网络缓冲区*/
        SSLSession session = clientEngine.getsSession();
        int appBufferMax = session.getApplicationBufferSize();
        int netBufferMax = session.getPacketBufferSize();
        clientIn = ByteBuffer.allocate(appBufferMax + 50);
        serverIn = ByteBuffer.allocate(appBufferMax + 50)
            
        cTOs = ByteBuffer.allocateDirect(netBufferMax);
		sTOc = ByteBuffer.allocateDirect(netBufferMax);
        
        clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
        serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
        
        SSLEngineResult clientResult;
        SSLEngineResult serverResult;
        
        while (!isEnqineClosed(clientEngine) || !isEngineClosed(serverEngine)) {
    
    
            log("==================");
            //客户端打包应用数据
            clientResult = clientEngine.wrap(clientOut, cTOs);
            log("client wrap:", clientResult);
            //完成握手任务
            runDelegatedTasks(clientResult, clientEngine);
            //服务器端打包应用数据
            serverResult = serverEngine.wrap(serverOut, sTOc);
            log("server wrap:", serverResult);
            //完成握手任务
            runDelegatedTasks(serverResult, serverEngine);
            
            cTOs.flip();
            sTOc.flip();
            
            log("---------------");
            //客户端展开网络数据
            clientResult = clientEngine.unwrap(sTOc, clientIn);
            log("client unwrap:", clientResult);
            //完成握手任务
            runDelegatedTasks(clientResult, clientEngine);
            //服务器端展开网络数据
            serverResult = serverEngine.unwrap(cTOs, serverIn);
            log("server unwrap:", serverResult);
            //完成握手任务
            runDeleqatedTasks(serverResult, serverEngine);
            
            cTOs.compact();
            sTOc.compact();
            
            if (!dataDone && (clientOut.limit() == serverIn.position()) && (serverOut,limit()=m clientIn.position())) {
    
    
                checkTransfer(serverOut, clientIn);
                checkTransfer(clientOut, serverIn);
                log("\tClosing clientEngine's *OUTBOUND*...");
                clientEngine.closeOutbound();
                dataDone = true;
            }
        }
    }
    
    /**当SSLEngine的输出与输入都关闭时,意味着SSLEngine被关闭*/
    private static boolean isEngineClosed(SSLEngine engine) {
    
    
        return(engine.isOutboundDone() && engine.isInboundDone());
    }
    
    /**执行SSL握手任务*/
    private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) throws Exception {
    
    
        if(result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
    
    
            Runnable runnable;
            while((runnable = engine.getDelegatedTask()) != null) {
    
    
                log("\trunning delegated task...");
                runnable.run();
            }
            HandshakeStatus hsStatus = engine.getHandshakeStatus();
            if(hsStatus == HandshakeStatus.NEED_TASK) {
    
    
                throw new Exception("handshake shouldn't need additional tasks");
            }
            log("\tnew HandshakeStatus:" + hsStatus);
        }
    }
    
    /**判断两个缓冲区内容是否相同*/
    private static void checkTransfer(ByteBuffer a, ByteBuffer b) throws Exception {
    
    
        a.flip();
        b.flip();
        if(!a.equals(b)) {
    
    
            throw new Exception("pata didn't transfer cleanly");
        } else {
    
    
            log("\tData transferred cleanly");
        }
        a.position(a.limit());
        b.position(b.limit());
        a.limit(a.capacity());
        b.limit(b.capacity());
    }
    
    private static boolean resultOnce = true;
    
    /**输出日志,打印SSLEngineResult的结果*/
    private static void log(String str, SSLEngineResult result) {
    
    
        if(resultOnce) {
    
    
            resultOnce = false;
            System.out.println("The format of the SSLEngineResult is: \n" 
                               +"\t\"getStatus() / getHandshakeStatus()\”+\n"
                               +"\t\"bytesConsumed() / bytesProduced()\"\n");
        }
        HandshakeStatus hsStatus = result.getHandshakeStatus();
        log(str + result,getStatus() + "/" + hsStatus + "," + result,bytesConsumed() + "/"
+ result.bytesProduced() + " bytes");
        if (hsStatus == HandshakeStatus.FINISHED) {
    
    
            log("\t...ready for application data");
        }
    }
    
    /**输出日志*/
    private static void log(String str) {
    
    
        System.out.printin(str);
    }
}

Create SSL-based secure servers and secure clients

public class EchoServer {
    
    
    
    private int port = 8000;
    private SSLServerSocket serverSocket;
    
    public EchoServer() throws Exception {
    
    
        //输出跟踪日志
        SSLContext context = createSSLContext();
        SSLServerSocketFactory factory = context.getServerSocketFactory();
        serverSocket = (SSLServerSocket)factory.createServerSocket(poxt);
        String[] supported = serverSocket.getSupportedCipherSuites();
        serverSocket.setEnabledCipherSuites(supported);
    }
    
    public SSLContext createSSLContext() throws Exception {
    
    
        //服务器用于证实自己身份的安全证书所在的密钥库
        String keyStoreFile = "test.keystore";
        String passphrase = "123456";
        KeyStore ks = KeyStore.getInstance("JKS");
        char[] password = passphrase.toCharArray();
        ks.load(new FileInputStream(keyStoreFile), password);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, password);
        
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(kmf.getKeyManagers(), null, null);
        
        //当要求客户端提供安全证书时,服务器端可创建TrustManagerFactory,
        //并由它创建TrustManager,TrustManger根据与之关联的KeyStore中的信息
        //决定是否相信客户提供的安全证书
        
        //客户端用于证实自己身份的安全证书所在的密钥库
        //String trustStorefile = "test.keystore";
        //KeyStore ts = KeyStore.getInstance("JKS");
        //ts.load(new FileInputStream(trustStoreFile), password);
        //TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        //tmf.init(ts);
        //sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        
        return sslContext;
    }
    
    private PrintWriter getWriter(Socket socket) throws IOException {
    
    
        OutputStream socketOut = socket.getOutputStream();
        return new PrintWriter(socketOut, true);
    }
    
    private BufferedReader getReader(Socket socket) throws IOException {
    
    
        InputStream socketIn = socket.getInputstream();
        return new BufferedReader(new InputStreamReader(socketIn));
    }
    
    public void service() {
    
    
        while (true) {
    
    
            Socket socket = null;
            try {
    
    
                //等特客户连接
                socket = serverSocket.accept();
                
                BufferedReader br = getReader(socket);
                PrintWriter pw = getWriter(socket);
                
                String msg = null;
                while ((msg = br.readLine()) != null) {
    
    
                    System.out.println(msg);
                    if (msg.equals("bye")) {
    
    
                        break;
                    }
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                try {
    
    
                    if(socket != null) socket.close(); //断开连接
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
    
    public static void main(String args[]) throws Exception {
    
    
        new EchoServer().service();
    }
}
public class EchoClient {
    
    
    
    private String host = "localhost";
    private int port = 8000;
    private SSLSocket socket;
    
    public EchoClient() throws IOException {
    
    
        SSLContext context = createSSLContext();
        SSLSocketFactory factory = context.getSocketFactory();
        socket = (SSLSocket)factory.createSocket(host,port);
        Stringl] supported = socket.getSupportedCipherSuites();
        socket.setEnabledCipherSuites(supported);
    }
    
    public SSLContext createSSLContext() throws Exception {
    
    
        String passphrase = "123456";
        char[] password = passphrase.toCharArray();
        
        //设置客户端所信任的安全证书所在的密钥库
        String trustStoreFile = "test.keystore";
        KeyStore ts = KeyStore.getInstance("JKS");
        ts.load(new FileInputStream(trustStoreFile), password));
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(ts);
        
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, tmf.getTrustManagers(), null);
        return sslContext;
    }
    
    public static void main(String args[]) throws IOException {
    
    
        new EchoClient().talk();
    }
    
    public void talk()throws IOException {
    
    
        try {
    
    
            BufferedReader br = getReader(socket);
            PrintWriter pw = getWriter(socket);
            
            BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
            
            String msg = null;
            while((msg = localReader.readLine()) != null) {
    
    
                pw.println(msg);
                System.out.println(br.readline());
                if(msg.equals("bye")) {
    
    
                    break;
                }
            }
        } catch(IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            try {
    
    
                if(socket != null) socket.close(); //断开连接
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
    
    private PrintWriter getWriter(Socket socket) throws IOException {
    
    
        OutputStream socketOut = socket.getoutputstream();
        return new PrintWriter(socketOut, true);
    }
    
    private BufferedReader getReader(Socket socket) throws IOException {
    
    
        InputStream socketIn = socket.getInputstream();
        return new BufferedReader(new InputStreamReader(socketIn));
    }
}

Guess you like

Origin blog.csdn.net/CSDN_handsome/article/details/131407552