1. 程式人生 > >ava SSL 證書細節

ava SSL 證書細節

關於SSL這塊,網上很多,但很多都是講原理或怎麼生成證書實現簡單通訊,沒有講到實踐時的諸多細節。 SSL, 即Secure Sockets Layer 安全套接層。本文介紹SSL的一些常見問題,用的語言主要是Java。

問題1:證書生成和格式

網上搜索SSL,有很多生成證書的教程。可是會發現有的是用Java的Keytool生成的cer,有的是用openssl來生成的pem。這是為什麼呢?其實這些證書本質是一樣的,只是檔案格式不一樣。所以如果想在Java裡使用pem格式的證書,就要轉化。轉化的方式在這個問題裡已有回答:Convert a PEM-formatted String to a java.security.cert.X509Certificate

另外,公鑰和私鑰分別以什麼形式存在?公鑰存在於證書檔案中,私鑰存在於Keystore檔案中。注意Java裡的Keystore類可以是有私鑰的Keystore,也可以是隻有公鑰的Keystore。 還有,為什麼使用Java keytool生成的證書要設定密碼?這個密碼是JKS檔案的密碼,注意不是公鑰或私鑰哦,是用於防範別人隨便亂拿的。

問題2:既然是加密,那麼可不可以脫離Socket存在?

可以。可以直接使用公鑰來加密資料,再用私鑰解密資料。具體方法參考:Java加密技術(八)——數字證書

問題3:單向認證,即只確認服務端是否真實可靠的話,要做什麼?以SSLSocket舉例。

如果只需要信任自己生成的證書

程式碼


    public class TestSSLSocketClient {  
        private static String path = "e:\\keytool\\sslclient.keystore";  
        private static char[] password = "aaaaaaa".toCharArray();  

        /** 
         * @param args 
         */  
        public static void main(String[] args) {  
            SSLContext context = null;  
            try {  
                KeyStore ts = KeyStore.getInstance("JKS");  
                ts.load(new FileInputStream(path), password);  
                TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");  
                tmf.init(ts);  
                TrustManager [] tm = tmf.getTrustManagers();  
                context = SSLContext.getInstance("SSL");  
                context.init(null, tm, null);  
            } catch (...... e) {         //省略捕獲的異常資訊  
                e.printStackTrace();  
            }   
            SSLSocketFactory ssf = context.getSocketFactory();  
            try {  
                SSLSocket ss = (SSLSocket) ssf.createSocket("localhost", 8000);  
                System.out.println("客戶端就緒。");  
                ObjectInputStream br = new ObjectInputStream(ss.getInputStream());  
                try {  
                    System.out.println(br.readObject());  
                } catch (ClassNotFoundException e) {  
                    e.printStackTrace();  
                }  
                br.close();  
                ss.close();  
                System.out.println("客戶端測試ok");  
            } catch (UnknownHostException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

可以從程式碼看到,對於SSLContext,在做單向認證時,只需要TrustManagerFactory即可, context.init(null, tm, null); TrustManagerFactory是使用了服務端的證書的,即只使用了公鑰來加密資料。

如果只需要信任系統自帶的證書

            SSLContext sslContext = SSLContext.getDefault();
  • 1

TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Using null here initialises the TMF with the default trust store.
tmf.init((KeyStore) null);

// Get hold of the default trust manager
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        defaultTm = (X509TrustManager) tm;
        break;
    }
}

FileInputStream myKeys = new FileInputStream("truststore.jks");

// Do the same with your trust store this time
// Adapt how you load the keystore to your needs
KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
myTrustStore.load(myKeys, "password".toCharArray());

myKeys.close();

tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(myTrustStore);

// Get hold of the default trust manager
X509TrustManager myTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        myTm = (X509TrustManager) tm;
        break;
    }
}

// Wrap it in your own class.
final X509TrustManager finalDefaultTm = defaultTm;
final X509TrustManager finalMyTm = myTm;
X509TrustManager customTm = new X509TrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        // If you're planning to use client-cert auth,
        // merge results from "defaultTm" and "myTm".
        return finalDefaultTm.getAcceptedIssuers();
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        try {
            finalMyTm.checkServerTrusted(chain, authType);
        } catch (CertificateException e) {
            // This will throw another CertificateException if this fails too.
            finalDefaultTm.checkServerTrusted(chain, authType);
        }
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        // If you're planning to use client-cert auth,
        // do the same as checking the server.
        finalDefaultTm.checkClientTrusted(chain, authType);
    }
};


SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { customTm }, null);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

問題4:可以看到SSLContext.init()的引數有KeyManager和TrustManager,它倆的差異是?

問題5:伺服器端會在連線時向客戶端傳送證書,那麼如何從程式碼上獲取?

import java.io.InputStream;
import java.io.OutputStream;

import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class RetrieveSSLCert {
    public static void main(String[] args) throws Exception {
    if (args.length < 2) {
        System.out.println("Usage: java RetrieveSSLCert <host> <port>");
        return;
    }

    String host = args[0];
    int port = Integer.parseInt(args[1]);

    // create custom trust manager to ignore trust paths
    TrustManager trm = new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
        return null;
        }

        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
    };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, new TrustManager[] { trm }, null);
        SSLSocketFactory factory =sc.getSocketFactory();
        SSLSocket socket =(SSLSocket)factory.createSocket(host, port);
        socket.startHandshake();
    SSLSession session = socket.getSession();
    java.security.cert.Certificate[] servercerts = session.getPeerCertificates();
    for (int i = 0; i < servercerts.length; i++) {
            System.out.print("-----BEGIN CERTIFICATE-----\n");
            System.out.print(new sun.misc.BASE64Encoder().encode(servercerts[i].getEncoded()));
            System.out.print("\n-----END CERTIFICATE-----\n");
    }

    socket.close();
    }
}

--------------------- 本文來自 光子質量 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/fzl562410663/article/details/71775530?utm_source=copy