07-HTTPS双向认证及Java案例

1.双向认证流程

  1. 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务端;
  2. 服务器端将本机的公钥证书(server.crt)发送给客户端;
  3. 客户端读取公钥证书(server.crt),取出了服务端公钥;
  4. 客户端将客户端公钥证书(client.crt)发送给服务器端;
  5. 服务器端使用根证书(root.crt)解密客户端公钥证书,拿到客户端公钥;
  6. 客户端发送自己支持的加密方案给服务器端;
  7. 服务器端根据自己和客户端的能力,选择一个双方都能接受的加密方案,使用客户端的公钥加密后发送给客户端;
  8. 客户端使用自己的私钥解密加密方案,生成一个随机数R,使用服务器公钥加密后传给服务器端;
  9. 服务端用自己的私钥去解密这个密文,得到了密钥R
  10. 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。

2. 证书准备

从上一章内容中,我们可以总结出来,整个双向认证的流程需要六个证书文件:

  • 服务器端公钥证书:server.crt
  • 服务器端私钥文件:server.key
  • 根证书:root.crt
  • 客户端公钥证书:client.crt
  • 客户端私钥文件:client.key
  • 客户端集成证书(包括公钥和私钥,用于浏览器访问场景):client.p12

所有的这些证书,我们都可以向证书机构去申请签发,一般需要收取一定的证书签发费用,此时我们需要选择大型的证书机构去购买。如果只是企业内部使用,不是给公众使用,也可以自行颁发自签名证书

3. 自签名证书

3.1 、根证书

(1)创建根证书私钥

openssl genrsa -out root.key 1024

(2)创建根证书请求文件:

openssl req -new -out root.csr -key root.key

后续参数请自行填写,下面是一个例子:

Country Name (2 letter code) [XX]:cn
State or Province Name (full name) []:bj
Locality Name (eg, city) [Default City]:bj
Organization Name (eg, company) [Default Company Ltd]:alibaba
Organizational Unit Name (eg, section) []:test
Common Name (eg, your name or your servers hostname) []:root
Email Address []:a.alibaba.com
A challenge password []:
An optional company name []:

(3)创建根证书

openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 3650

在创建证书请求文件的时候需要注意三点,下面生成服务器请求文件和客户端请求文件均要注意这三点:

  • 根证书的Common Name填写root就可以,所有客户端和服务器端的证书这个字段需要填写域名,一定要注意的是,根证书的这个字段和客户端证书、服务器端证书不能一样;
  • 其他所有字段的填写,根证书、服务器端证书、客户端证书需保持一致
  • 最后的密码可以直接回车跳过。

经过上面三个命令行,我们最终可以得到一个签名有效期为10年的根证书root.crt,后面我们可以用这个根证书去颁发服务器证书和客户端证书。

3.2、 生成自签名服务器端证书

(1)生成服务器端证书私钥

openssl genrsa -out server.key 1024

(2) 生成服务器证书请求文件,过程和注意事项参考根证书,本节不详述:

openssl req -new -out server.csr -key server.key

(3) 生成服务器端公钥证书

openssl x509 -req -in server.csr -out server.crt -signkey server.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650

经过上面的三个命令,我们得到:
server.key:服务器端的密钥文件 server.crt:有效期十年的服务器端公钥证书,使用根证书和服务器端私钥文件一起生成

3.3、 生成自签名客户端证书

(1)生成客户端证书密钥:

openssl genrsa -out client.key 1024
openssl genrsa -out client2.key 1024

(2) 生成客户端证书请求文件,过程和注意事项参考根证书,本节不详述:

openssl req -new -out client.csr -key client.key
openssl req -new -out client2.csr -key client2.key

(3) 生客户端证书

openssl x509 -req -in client.csr -out client.crt -signkey client.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650
openssl x509 -req -in client2.csr -out client2.crt -signkey client2.key -CA root.crt -CAkey root.key -CAcreateserial -days 3650

(4) 生客户端p12格式证书,需要输入一个密码,选一个好记的,比如123456

openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
openssl pkcs12 -export -clcerts -in client2.crt -inkey client2.key -out client2.p12

重复使用上面的命令,我们得到两套客户端证书:

  • client.key / client2.key:客户端的私钥文件
  • client.crt / client2.key:有效期十年的客户端证书
    使用根证书和客户端私钥一起生成 client.p12/client2.p12,这个证书文件包含客户端的公钥和私钥,主要用来给浏览器访问使用

4. keytool生成证书

使用双向认证的SSL/TLS协议通信,客户端和服务器端都要设置用于证实自己身份的安全证书,并且还要设置信任对方的哪些安全证书。
理论上一共需要准备四个文件,两个keystore文件和两个truststore文件。
通信双方分别拥有一个keystore和一个truststore,keystore用于存放自己的密钥和公钥,truststore用于存放所有需要信任方的公钥。

keytool -genkey -alias catserver -keyalg rsa -keysize 1024 -sigalg sha256withrsa -keypass huawei -keystore catserver.keystore -storepass huawei

keytool -genkey -alias foxclient -keyalg dsa -keysize 512 -sigalg sha1withdsa -keypass huawei -keystore foxclient.keystore -storepass huawei
keytool -export -alias catserver -keystore catserver.keystore -storepass huawei -file catserver.cer
keytool -export -alias foxclient -keystore foxclient.keystore -storepass huawei -file foxclient.cer
keytool -import -alias foxclient -keystore catservertrust.keystore -storepass huawei -file foxclient.cer
keytool -import -alias catserver -keystore foxclienttrust.keystore -storepass huawei -file catserver.cer

5. 使用Java调用

5.1、server代码

public class CatServer implements Runnable, HandshakeCompletedListener {

    public static final int SERVER_PORT = 11123;

    private final Socket _s;
    private String peerCerName;

    public CatServer(Socket s) {
        _s = s;
    }

    public static void main(String[] args) throws Exception {
        String serverKeyStoreFile = "D:\\code\\mycode\\java-study\\https\\src\\main\\resources\\double\\catserver.keystore";
        String serverKeyStorePwd = "huawei";
        String catServerKeyPwd = "huawei";
        String serverTrustKeyStoreFile = "D:\\code\\mycode\\java-study\\https\\src\\main\\resources\\double\\catservertrust.keystore";
        String serverTrustKeyStorePwd = "huawei";

        KeyStore serverKeyStore = KeyStore.getInstance("JKS");
        serverKeyStore.load(new FileInputStream(serverKeyStoreFile), serverKeyStorePwd.toCharArray());

        KeyStore serverTrustKeyStore = KeyStore.getInstance("JKS");
        serverTrustKeyStore.load(new FileInputStream(serverTrustKeyStoreFile), serverTrustKeyStorePwd.toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(serverKeyStore, catServerKeyPwd.toCharArray());

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

        SSLContext sslContext = SSLContext.getInstance("TLSv1");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
        SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(SERVER_PORT);
        sslServerSocket.setNeedClientAuth(true);

        while (true) {
            SSLSocket s = (SSLSocket) sslServerSocket.accept();
            CatServer cs = new CatServer(s);
            s.addHandshakeCompletedListener(cs);
            new Thread(cs).start();
        }
    }

    @Override
    public void run() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(_s.getInputStream()));
            PrintWriter writer = new PrintWriter(_s.getOutputStream(), true);

            writer.println("Welcome~, enter exit to leave.");
            String s;
            while ((s = reader.readLine()) != null && !s.trim().equalsIgnoreCase("exit")) {
                writer.println("Echo: " + s);
            }
            writer.println("Bye~, " + peerCerName);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                _s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void handshakeCompleted(HandshakeCompletedEvent event) {
        try {
            X509Certificate cert = (X509Certificate) event.getPeerCertificates()[0];
            peerCerName = cert.getSubjectX500Principal().getName();
                } catch (SSLPeerUnverifiedException ex) {
                ex.printStackTrace();
                }
                }

                }

5.2、client代码

public class FoxClient {
    public static void main(String[] args) throws Exception {
        String clientKeyStoreFile = "D:\\code\\mycode\\java-study\\https\\src\\main\\resources\\double\\foxclient.keystore";
        String clientKeyStorePwd = "huawei";
        String foxclientKeyPwd = "huawei";
        String clientTrustKeyStoreFile = "D:\\code\\mycode\\java-study\\https\\src\\main\\resources\\double\\foxclienttrust.keystore";
        String clientTrustKeyStorePwd = "huawei";

        KeyStore clientKeyStore = KeyStore.getInstance("JKS");
        clientKeyStore.load(new FileInputStream(clientKeyStoreFile), clientKeyStorePwd.toCharArray());

        KeyStore clientTrustKeyStore = KeyStore.getInstance("JKS");
        clientTrustKeyStore.load(new FileInputStream(clientTrustKeyStoreFile), clientTrustKeyStorePwd.toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(clientKeyStore, foxclientKeyPwd.toCharArray());

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

        SSLContext sslContext = SSLContext.getInstance("TLSv1");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        SSLSocketFactory socketFactory = sslContext.getSocketFactory();
        Socket socket = socketFactory.createSocket("localhost", CatServer.SERVER_PORT);

        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

        send("hello", out);
        send("exit", out);
        receive(in);
        socket.close();
    }

    public static void send(String s, PrintWriter out) throws IOException {
        System.out.println("Sending: " + s);
        out.println(s);
    }

    public static void receive(BufferedReader in) throws IOException {
        String s;
        while ((s = in.readLine()) != null) {
            System.out.println("Reveived: " + s);
        }
    }
}

参考文章:

更多内容关注微信公众号 ”前后端技术精选“,或者语雀,里面有更多知识:https://www.yuque.com/riverzmm/uu60c9?# 《安全》

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Java 语言中,https双向认证即为客户端和服务器双方都需要进行身份验证。这种方式能够提供更高级别的安全保障,因为双方都需要证明自己的身份。 在 Java 中,可以使用 java.net 包中的 HttpsURLConnection 类实现双向认证。要使用双向认证,需要客户端和服务器都持有一个数字证书,并且客户端需要在与服务器通信时提供证书,服务器也需要验证客户端的证书。 代码实现细节较复杂,这里不再赘述。有兴趣的读者可以查找相关的资料进行学习。 ### 回答2: Java语言中的HTTPS双向认证是建立在传输层安全协议(TLS/SSL)之上的一种加密通信机制。在HTTPS连接中,服务器和客户端之间进行双向认证,以确保通信的安全性。 双向认证主要包括以下步骤: 首先,服务器需要具有一张数字证书,该证书由可信任的第三方机构(如CA)颁发。证书中包含了服务器的公钥,以及其他相关信息。服务器会将此证书发送给客户端。 然后,客户端会收到服务器发送的数字证书。客户端会验证证书的合法性,并检查证书中的相关信息,如颁发机构、有效期等。如果证书合法,客户端会直接使用证书中的公钥进行加密。 接下来,客户端会生成一个随机数(密钥),并使用服务器的公钥对其进行加密,然后将加密后的密钥发送给服务器服务器收到客户端发送的加密后的密钥后,会使用自己的私钥进行解密操作,得到客户端生成的随机数。 此后,服务器和客户端都拥有了相同的随机数(密钥),它们可以使用该密钥进行对称加密和解密操作,保证通信的机密性和完整性。 通过以上双向认证的流程,HTTPS连接实现了身份验证和数据加密,确保了通信的安全性。在Java语言中,可以使用相关的API和工具来实现双向认证,如Java KeyStore用于存储和管理数字证书,以及使用SSLContext和SSLParameters等类来配置和控制SSL/TLS连接的安全参数。 ### 回答3: Java语言中的HTTPS双向认证是一种网络通信模式,在该模式中,服务器和客户端之间进行双向验证以确保安全通信。 首先,客户端向服务器发起HTTPS连接请求。服务器会将自己的数字证书发送给客户端,该数字证书包含服务器的公钥,并由数字证书颁发机构签名验证。客户端会验证服务器的证书是否合法和有效。 接下来,客户端生成自己的密钥对,并将公钥发送给服务器。客户端的公钥也被包含在数字证书中,并由客户端的数字证书颁发机构签名验证。服务器会验证客户端的证书是否合法和有效。 在双向认证过程中,客户端和服务器都进行了证书的验证和身份的验证。只有在双方都验证通过的情况下,它们才能建立安全的连接,继续进行通信。 双向认证可以提供更高的安全性,因为不仅服务器验证客户端身份,客户端也可以验证服务器身份。这有效防止了中间人攻击和伪造服务器的问题。 在Java语言中,可以使用Java标准库中的SSLSocket和SSLServerSocket类来实现HTTPS双向认证服务器可以配置使用自己的私钥和证书,用于验证客户端的请求,而客户端可以使用其私钥和证书来验证服务器双向认证HTTPS协议中的一种重要机制,可以在保证通信安全的同时提供双方身份的验证。它广泛应用于银行、电商等对安全性要求较高的应用场景中。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值