天天看點

java SSLEngine初探,demo代碼幫助了解

Class SSLEngine

java keystore 工具

圖解SSL/TLS協定

SSLEngine的示例 内,所引用的原代碼樣例位址【已不可用】

SSLEngineSimpleDemo.java

簡介:SSLEngine簡單了解為,就是資料發送前wrap打包加密,資料接收時unwrap解包解密,這樣一個https通過SSLEngine的過程。如下:

你的程式《———》SSLEngine《———》網絡

過程:先建立http連接配接,然後是使用ssl的handshake握手協定,比普通http的三次握手複雜的多,之後的http互動是一樣的。

主要是【注意】連接配接過程中ssl連接配接的【不同狀态】SSLEngineResult.HandshakeStatus。

FINISHED            The SSLEngine has just finished handshaking.
NEED_TASK           The SSLEngine needs the results of one (or more) delegated tasks before handshaking can continue.
NEED_UNWRAP         The SSLEngine needs to receive data from the remote side before handshaking can continue.
NEED_WRAP           The SSLEngine must send data to the remote side before handshaking can continue, so SSLEngine.wrap() should be called.
NOT_HANDSHAKING     The SSLEngine is not currently handshaking.
           

SSLEngineSimpleDemo.java 這個樣例模拟了服務端與用戶端,運作成功後分析代碼,有助于更好的使用。

需要先自己生成keystore.jks(代碼注釋中也加了生成的方法)(keytool 是jdk自帶的工具C:\Program Files\Java\jdk1.8.0_144\bin,加入環境變量可以直接使用)(-alias selfsigned别名是唯一的不會重複)

keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 
一直回車,直到  是否正确,輸入“是”,再回車,生成keystore.jks檔案

檢視證書清單
keytool -list -keystore  keystore.jks -storepass password

導入證書
keytool -import -alias github -keystore  keystore.jks -file github.cer -trustcacerts -storepass password
           
java SSLEngine初探,demo代碼幫助了解

删除keystore

keytool -delete -alias selfsigned -keystore keystore.jks -storepass password
           
/* 
 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 * 
 * -Redistribution of source code must retain the above copyright 
 *  notice, this list of conditions and the following disclaimer. 
 * 
 * -Redistribution in binary form must reproduce the above copyright 
 *  notice, this list of conditions and the following disclaimer in the 
 *  documentation and/or other materials provided with the 
 *  distribution. 
 * 
 * Neither the name of Oracle nor the names of 
 * contributors may be used to endorse or promote products derived from 
 * this software without specific prior written permission. 
 * 
 * This software is provided "AS IS," without a warranty of any kind. 
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, 
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN 
 * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR 
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR 
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN 
 * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR 
 * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE 
 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, 
 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF 
 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
 * 
 * You acknowledge that this software is not designed, licensed or 
 * intended for use in the design, construction, operation or 
 * maintenance of any nuclear facility. 
 */

/**
 * A SSLEngine usage example which simplifies the presentation 
 * by removing the I/O and multi-threading concerns. 
 *
 * The demo creates two SSLEngines, simulating a client and server. 
 * The "transport" layer consists two ByteBuffers:  think of them 
 * as directly connected pipes. 
 *
 * Note, this is a *very* simple example: real code will be much more 
 * involved.  For example, different threading and I/O models could be 
 * used, transport mechanisms could close unexpectedly, and so on. 
 *
 * When this application runs, notice that several messages 
 * (wrap/unwrap) pass before any application data is consumed or 
 * produced.  (For more information, please see the SSL/TLS 
 * specifications.)  There may several steps for a successful handshake, 
 * so it's typical to see the following series of operations: 
 *
 *      client          server          message 
 *      ======          ======          ======= 
 *      wrap()          ...             ClientHello 
 *      ...             unwrap()        ClientHello 
 *      ...             wrap()          ServerHello/Certificate 
 *      unwrap()        ...             ServerHello/Certificate 
 *      wrap()          ...             ClientKeyExchange 
 *      wrap()          ...             ChangeCipherSpec 
 *      wrap()          ...             Finished 
 *      ...             unwrap()        ClientKeyExchange 
 *      ...             unwrap()        ChangeCipherSpec 
 *      ...             unwrap()        Finished 
 *      ...             wrap()          ChangeCipherSpec 
 *      ...             wrap()          Finished 
 *      unwrap()        ...             ChangeCipherSpec 
 *      unwrap()        ...             Finished 
 */


/**
 * 先生成keystore
 * keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360
 * 一直回車,直到  是否正确,輸入“是”,再回車,生成keystore.jks檔案
 */

import javax.net.ssl.*;
import javax.net.ssl.SSLEngineResult.*;
import java.io.*;
import java.security.*;
import java.nio.*;

public class SSLEngineSimpleDemo {

    /* 
     * Enables logging of the SSLEngine operations. 
     */
    private static boolean logging = true;

    /* 
     * Enables the JSSE system debugging system property: 
     *  
     * -Djavax.net.debug=all 
     *  
     * This gives a lot of low-level information about operations underway, 
     * including specific handshake messages, and might be best examined after 
     * gaining some familiarity with this application. 
     */
    private static boolean debug = true;

    private SSLContext sslc;

    private SSLEngine clientEngine; // client Engine  
    private ByteBuffer clientOut; // write side of clientEngine  
    private ByteBuffer clientIn; // read side of clientEngine  

    private SSLEngine serverEngine; // server Engine  
    private ByteBuffer serverOut; // write side of serverEngine  
    private ByteBuffer serverIn; // read side of serverEngine  

    /* 
     * For data transport, this example uses local ByteBuffers. This isn't 
     * really useful, but the purpose of this example is to show SSLEngine 
     * concepts, not how to do network transport. 
     */
    private ByteBuffer cTOs; // "reliable" transport client->server  
    private ByteBuffer sTOc; // "reliable" transport server->client  

    /* 
     * The following is to set up the keystores. 
     */
    private static String keyStoreFile = "keystore.jks";
    private static String trustStoreFile = "keystore.jks";

    /* 
     * Main entry point for this demo. 
     */
    public static void main(String args[]) throws Exception {
        if (debug) {
            System.setProperty("javax.net.debug", "all");
        }

        SSLEngineSimpleDemo demo = new SSLEngineSimpleDemo();
        demo.runDemo();

        System.out.println("Demo Completed.");
    }

    /* 
     * Create an initialized SSLContext to use for this demo. 
     */
    public SSLEngineSimpleDemo() throws Exception {

        KeyStore ks = KeyStore.getInstance("JKS");
        KeyStore ts = KeyStore.getInstance("JKS");

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

        File file = new File(keyStoreFile);
        System.out.println("put keystore in this path:"+file.getAbsolutePath());

        ks.load(new FileInputStream(keyStoreFile), passphrase);
        ts.load(new FileInputStream(trustStoreFile), passphrase);

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, passphrase);

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(ts);

        SSLContext sslCtx = SSLContext.getInstance("TLS");

        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        sslc = sslCtx;
    }

    /* 
     * Run the demo. 
     *  
     * Sit in a tight loop, both engines calling wrap/unwrap regardless of 
     * whether data is available or not. We do this until both engines report 
     * back they are closed. 
     *  
     * The main loop handles all of the I/O phases of the SSLEngine's lifetime: 
     *  
     * initial handshaking application data transfer engine closing 
     *  
     * One could easily separate these phases into separate sections of code. 
     */
    private void runDemo() throws Exception {
        boolean dataDone = false;

        createSSLEngines();
        createBuffers();

        SSLEngineResult clientResult; // results from client's last operation  
        SSLEngineResult serverResult; // results from server's last operation  

        /* 
         * Examining the SSLEngineResults could be much more involved, and may 
         * alter the overall flow of the application. 
         *  
         * For example, if we received a BUFFER_OVERFLOW when trying to write to 
         * the output pipe, we could reallocate a larger pipe, but instead we 
         * wait for the peer to drain it. 
         */
        while (!isEngineClosed(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);
            runDelegatedTasks(serverResult, serverEngine);

            cTOs.compact();
            sTOc.compact();  

            /* 
             * After we've transfered all application data between the client 
             * and server, we close the clientEngine's outbound stream. This 
             * generates a close_notify handshake message, which the server 
             * engine receives and responds by closing itself. 
             *  
             * In normal operation, each SSLEngine should call closeOutbound(). 
             * To protect against truncation attacks, SSLEngine.closeInbound() 
             * should be called whenever it has determined that no more input 
             * data will ever be available (say a closed input stream). 
             */
            if (!dataDone && (clientOut.limit() == serverIn.position())
                    && (serverOut.limit() == clientIn.position())) {  

                /* 
                 * A sanity check to ensure we got what was sent. 
                 */
                checkTransfer(serverOut, clientIn);
                checkTransfer(clientOut, serverIn);

                log("\tClosing clientEngine's *OUTBOUND*...");
                clientEngine.closeOutbound();
                // serverEngine.closeOutbound();  
                dataDone = true;
            }
        }
    }

    /* 
     * Using the SSLContext created during object creation, create/configure the 
     * SSLEngines we'll use for this demo. 
     */
    private void createSSLEngines() throws Exception {  
        /* 
         * Configure the serverEngine to act as a server in the SSL/TLS 
         * handshake. Also, require SSL client authentication. 
         */
        serverEngine = sslc.createSSLEngine();
        serverEngine.setUseClientMode(false);
        serverEngine.setNeedClientAuth(true);  

        /* 
         * Similar to above, but using client mode instead. 
         */
        clientEngine = sslc.createSSLEngine("client", );
        clientEngine.setUseClientMode(true);
    }

    /* 
     * Create and size the buffers appropriately. 
     */
    private void createBuffers() {  

        /* 
         * We'll assume the buffer sizes are the same between client and server. 
         */
        SSLSession session = clientEngine.getSession();
        int appBufferMax = session.getApplicationBufferSize();
        int netBufferMax = session.getPacketBufferSize();  

        /* 
         * We'll make the input buffers a bit bigger than the max needed size, 
         * so that unwrap()s following a successful data transfer won't generate 
         * BUFFER_OVERFLOWS. 
         *  
         * We'll use a mix of direct and indirect ByteBuffers for tutorial 
         * purposes only. In reality, only use direct ByteBuffers when they give 
         * a clear performance enhancement. 
         */
        clientIn = ByteBuffer.allocate(appBufferMax + );
        serverIn = ByteBuffer.allocate(appBufferMax + );

        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());
    }

    /* 
     * If the result indicates that we have outstanding tasks to do, go ahead 
     * and run them in this thread. 
     */
    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 boolean isEngineClosed(SSLEngine engine) {
        return (engine.isOutboundDone() && engine.isInboundDone());
    }

    /* 
     * Simple check to make sure everything came across as expected. 
     */
    private static void checkTransfer(ByteBuffer a, ByteBuffer b)
            throws Exception {
        a.flip();
        b.flip();

        if (!a.equals(b)) {
            throw new Exception("Data 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());
    }

    /* 
     * Logging code 
     */
    private static boolean resultOnce = true;

    private static void log(String str, SSLEngineResult result) {
        if (!logging) {
            return;
        }
        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) {
        if (logging) {
            System.out.println(str);
        }
    }
}