HTTPS实践

633 阅读6分钟
//生成.jks秘钥
keytool -genkey -keystore e:\file\a\server_ks.jks -storepass server_password -keyalg RSA -keypass server_password

C:\Program Files\Java\jdk1.8.0_221\bin>
    keytool -genkey -keystore e:\file\a\server_ks.jks -storepass server_password -keyalg RSA -keypass server_password
您的名字与姓氏是什么?
  [Unknown]:  panghu
您的组织单位名称是什么?
  [Unknown]:  test
您的组织名称是什么?
  [Unknown]:  test
您所在的城市或区域名称是什么?
  [Unknown]:  shenzhen
您所在的省/市/自治区名称是什么?
  [Unknown]:  guangdong
该单位的双字母国家/地区代码是什么?
  [Unknown]:  86
CN=panghu, OU=test, O=test, L=shenzhen, ST=guangdong, C=86是否正确?
  [否]:  y

//生成证书
keytool -export -keystore e:\file\a\server_ks.jks -storepass server_password -file e:\file\a\server.cer
C:\Program Files\Java\jdk1.8.0_221\bin>
    keytool -export -keystore e:\file\a\server_ks.jks -storepass server_password -file e:\file\a\server.cer


存储在文件 <e:\file\a\server.cer> 中的证书

单向HTTPS验证

    public static void main(String[] args) {
        new Thread(new ServerOne()).start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new ClientOne()).start();
    }

    public static class ServerOne implements Runnable {
    
    //SSL协议版本
    private static final String TLS = "TLSv1.2";
    //KeyStore的类型
    private static final String PROVIDER = "SunX509";
    //秘钥类型,java默认是JKS,Android不支持JKS,只能用BKS
    private static final String STORE_TYPE = "JKS";
    //秘钥的路径
    private static final String KEY_STORE_NAME = "e:\\file\\a\\server_ks.jks";
    //Server的端口
    private static final int DEFAULT_PORT = 8090; //自定义端口
    //秘钥的密码
    private static final String SERVER_KEY_STORE_PASSWORD = "server_password"; //秘钥的密码

    @Override
    public void run() {
        try {
            //获取SSLContext
            SSLContext sslContext = SSLContext.getInstance(TLS);
            //生成秘钥的manager
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER);
            //加载秘钥
            KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE);
            keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), SERVER_KEY_STORE_PASSWORD.toCharArray());

            //秘钥初始化
            keyManagerFactory.init(keyStoreOne, SERVER_KEY_STORE_PASSWORD.toCharArray());

            //初始化SSLContext
            sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
            //获取SSLContext的SocketFactory
            SSLServerSocket sslServerSocket = 
              (SSLServerSocket) sslContext.getServerSocketFactory()
                .createServerSocket(DEFAULT_PORT);
            //是否开启双向验证
            sslServerSocket.setNeedClientAuth(false);
            System.out.println("服务器已开启,等待连接 .....");
            Socket accept = sslServerSocket.accept();
            System.out.println("客户端 : " + accept.getInetAddress().getHostAddress());

            InputStream inputStream = accept.getInputStream();
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = inputStream.read(bytes)) != -1) {
                System.out.println("服务器收到数据 : " + new String(bytes, 0, len));
            }
            inputStream.close();
            accept.close();
            sslServerSocket.close();
            System.out.println("服务器关闭");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }
    }

    public static class ClientOne implements Runnable {
    
    private static final String TLS = "TLSv1.2";
    private static final String PROVIDER = "SunX509";
    private static final String STORE_TYPE = "JKS";
    private static final String TRUST_STORE_NAME = "e:\\file\\a\\server.cer";

    @Override
    public void run() {
        try {
            SSLContext sslContext = SSLContext.getInstance(TLS);
            
            //生成信任证书Manager,默认系统会信任CA机构颁发的证书,自定的证书需要手动的加载
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER);
            KeyStore keyStore = KeyStore.getInstance(STORE_TYPE);
            keyStore.load(null);
            //这种方式加载需要知道信任签名JKS文件的密码
            //keyStore.load(new FileInputStream(TRUST_STORE_NAME),"password".toCharArray());
            
            //生成验证工厂
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            //生成别名(可以随便填写)
            String certificateAlias = Integer.toString(0);
            //加载证书
            keyStore.setCertificateEntry(
              certificateAlias,
              certificateFactory.generateCertificate(
                new FileInputStream(TRUST_STORE_NAME)));//证书.cer文件
            
            //初始化
            trustManagerFactory.init(keyStore);
            //初始化
            sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
            SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket("192.168.0.10", 8090);
            socket.startHandshake();

            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("这是SSL测试".getBytes());
            outputStream.flush();

            outputStream.close();
            socket.close();
            System.out.println("客户端关闭");

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }
    }
输出:
服务器已开启,等待连接 .....
客户端 : 192.168.0.10
服务器收到数据 : 这是SSL测试
客户端关闭
服务器关闭

双向HTTPS验证

//生成client端的秘钥和证书文件
C:\Program Files\Java\jdk1.8.0_221\bin>
    keytool -genkey -keystore e:\file\a\client_ks.jks -storepass client_password -keyalg RSA 
        -keypass client_password
您的名字与姓氏是什么?
  [Unknown]:  panghu
您的组织单位名称是什么?
  [Unknown]:  test
您的组织名称是什么?
  [Unknown]:  test
您所在的城市或区域名称是什么?
  [Unknown]:  shenzhen
您所在的省/市/自治区名称是什么?
  [Unknown]:  guangdong
该单位的双字母国家/地区代码是什么?
  [Unknown]:  86
CN=panghu, OU=test, O=test, L=shenzhen, ST=guangdong, C=86是否正确?
  [否]:  y

C:\Program Files\Java\jdk1.8.0_221\bin>
    keytool -export -keystore e:\file\a\client_ks.jks -storepass client_password -file e:\file\a\client.cer
存储在文件 <e:\file\a\client.cer> 中的证书

//把server.cer证书添加到serverTrust_ks.jks文件中
C:\Program Files\Java\jdk1.8.0_221\bin>
    keytool -import -keystore e:\file\a\serverTrust_ks.jks -storepass client -file e:\file\a\server.cer
所有者: CN=panghu, OU=test, O=test, L=shenzhen, ST=guangdong, C=86
发布者: CN=panghu, OU=test, O=test, L=shenzhen, ST=guangdong, C=86
序列号: 5d765707
有效期为 Mon Sep 23 18:33:19 CST 2019 至 Sun Dec 22 18:33:19 CST 2019
证书指纹:
         MD5:  9E:9D:33:A2:3C:A7:E5:61:F1:41:A7:DE:DF:66:45:56
         SHA1: E8:D9:F9:F8:03:60:F0:A9:34:D5:1C:99:86:22:AA:B4:B3:61:66:B5
         SHA256: 6C:82:5F:9F:31:C3:32:EC:62:CE:6C:59:C7:13:C1:6A:95:5B:4C:55:63:D0:A1:CE:7A:4B:1A:ED:9A:D0:CF:20
签名算法名称: SHA256withRSA
主体公共密钥算法: 2048 位 RSA 密钥
版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: CC 01 50 17 30 85 97 C8   DC 17 F5 59 52 DE 5F 90  ..P.0......YR._.
0010: 4A 47 13 E4                                        JG..
]
]

是否信任此证书? [否]:  y
证书已添加到密钥库中

// 把client端证书添加到clientTrust_ks.jks文件中
C:\Program Files\Java\jdk1.8.0_221\bin>
    keytool -import -keystore e:\file\a\clientTrust_ks.jks -storepass server -file e:\file\a\client.cer
所有者: CN=panghu, OU=test, O=test, L=shenzhen, ST=guangdong, C=86
发布者: CN=panghu, OU=test, O=test, L=shenzhen, ST=guangdong, C=86
序列号: 2a332242
有效期为 Mon Sep 23 19:06:09 CST 2019 至 Sun Dec 22 19:06:09 CST 2019
证书指纹:
         MD5:  A2:E1:C3:1B:8B:F1:78:C4:7D:EE:65:72:DB:B9:23:01
         SHA1: 42:27:6B:B2:10:5F:80:75:D7:F5:D5:33:01:25:08:8C:33:C6:B4:76
         SHA256: BB:A9:6C:44:2D:A2:30:6F:01:71:42:6E:6B:3F:19:5C:77:8A:05:79:9F:03:0E:42:E8:2E:A2:3C:47:E9:F6:E0
签名算法名称: SHA256withRSA
主体公共密钥算法: 2048 位 RSA 密钥
版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 16 AB 67 DE 94 D4 8C 05   56 88 91 72 98 9A D4 FD  ..g.....V..r....
0010: 96 16 C4 F3                                        ....
]
]

是否信任此证书? [否]:  y
证书已添加到密钥库中

new Thread(new Server()).start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
new Thread(new Client()).start();

public static class Server implements Runnable {
    private static final String TLS = "TLSv1.2";
    private static final String PROVIDER = "SunX509";
    private static final String STORE_TYPE = "JKS";
    private static final String KEY_STORE_NAME = "e:\\file\\a\\server_ks.jks";
    private static final String TRUST_STORE_NAME = "e:\\file\\a\\clientTrust_ks.jks";
    private static final int DEFAULT_PORT = 8090; //自定义端口
    private static final String SERVER_KEY_STORE_PASSWORD = "server_password"; //密码
    private static final String SERVER_TRUST_KEY_STORE_PASSWORD = "server";//密码

    @Override
    public void run() {
        try {
            SSLContext sslContext = SSLContext.getInstance(TLS);
            //加载server端的秘钥
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER);
            //加载信任的证书
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER);
            KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE);
            KeyStore keyStoreTwo = KeyStore.getInstance(STORE_TYPE);
            keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), SERVER_KEY_STORE_PASSWORD.toCharArray());
            keyStoreTwo.load(new FileInputStream(TRUST_STORE_NAME), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray());
            keyManagerFactory.init(keyStoreOne, SERVER_KEY_STORE_PASSWORD.toCharArray());
            trustManagerFactory.init(keyStoreTwo);

            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
            SSLServerSocket sslServerSocket = 
                (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(DEFAULT_PORT);
            // 开启双向验证
            sslServerSocket.setNeedClientAuth(true);
            System.out.println("服务器已开启,等待连接 .....");
            Socket accept = sslServerSocket.accept();
            System.out.println("客户端 : " + accept.getInetAddress().getHostAddress());

            InputStream inputStream = accept.getInputStream();
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = inputStream.read(bytes)) != -1) {
                System.out.println("服务器收到数据 : " + new String(bytes, 0, len));
            }
            inputStream.close();
            accept.close();
            sslServerSocket.close();
            System.out.println("服务器关闭");
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

    public static class Client implements Runnable {
    
    private static final String TLS = "TLSv1.2";
    private static final String PROVIDER = "SunX509";
    private static final String STORE_TYPE = "JKS";
    private static final String KEY_STORE_NAME = "e:\\file\\a\\client_ks.jks";
    private static final String TRUST_STORE_NAME = "e:\\file\\a\\serverTrust_ks.jks";
    private static final String CLIENT_KEY_STORE_PASSWORD = "client_password"; //密码
    private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "client";//密码

    @Override
    public void run() {
        try {
            SSLContext sslContext = SSLContext.getInstance(TLS);
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(PROVIDER);
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(PROVIDER);

            KeyStore keyStoreOne = KeyStore.getInstance(STORE_TYPE);
            KeyStore keyStoreTwo = KeyStore.getInstance(STORE_TYPE);

            //加载client端的秘钥
            keyStoreOne.load(new FileInputStream(KEY_STORE_NAME), CLIENT_KEY_STORE_PASSWORD.toCharArray());
            //信任证书
            keyStoreTwo.load(new FileInputStream(TRUST_STORE_NAME), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());

            keyManagerFactory.init(keyStoreOne, CLIENT_KEY_STORE_PASSWORD.toCharArray());
            trustManagerFactory.init(keyStoreTwo);
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
            SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket("192.168.0.10", 8090);
            socket.startHandshake();

            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("这是SSL测试".getBytes());
            outputStream.flush();

            outputStream.close();
            socket.close();
            System.out.println("客户端关闭");


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