码迷,mamicode.com
首页 > 编程语言 > 详细

Java-JSSE-SSL/TLS编程代码实例-双向认证

时间:2014-11-11 19:16:12      阅读:248      评论:0      收藏:0      [点我收藏+]

标签:des   blog   http   io   ar   os   使用   java   for   

关于SSL/TLS/JSSE的介绍:
1)SSL/TLS协议运行机制
2)图解SSL/TLS协议
3)使用wireshark观察SSL/TLS握手过程
4)SSL/TLS的Java实现--JSSE

(一)使用keytool创建密钥库

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

首先使用JDK自带的keytool工具来生成keystore和truststore。这里使用的Java版本是1.7。
1)创建server的keystore文件,生成server的公钥/私钥密钥对。需要指定keystore的密码(storepass)和密钥对的密码(keypass)。
访问keystore需要storepass。访问密钥对需要keypass。

C:\Program Files\Java\jre7\bin>keytool -genkey -alias catserver -keyalg rsa -keysize 1024 -sigalg sha256withrsa -keypass catserver -keystore c:\_tmp\catserver.keystore -storepass catserverks
What is your first and last name?
[Unknown]: cat
What is the name of your organizational unit?
[Unknown]: cat
What is the name of your organization?
[Unknown]: cat
What is the name of your City or Locality?
[Unknown]: cat
What is the name of your State or Province?
[Unknown]: cat
What is the two-letter country code for this unit?
[Unknown]: ct
Is CN=cat, OU=cat, O=cat, L=cat, ST=cat, C=ct correct?
[no]: y


2)创建client的keystore文件。同样需要指定keystore的密码和密钥对的密码。

C:\Program Files\Java\jre7\bin>keytool -genkey -alias foxclient -keyalg dsa -keysize 512 -sigalg sha1withdsa -keypass foxclient -keystore c:\_tmp\foxclient.keystore -storepass foxclientks
What is your first and last name?
[Unknown]: fox
What is the name of your organizational unit?
[Unknown]: fox
What is the name of your organization?
[Unknown]: fox
What is the name of your City or Locality?
[Unknown]: fox
What is the name of your State or Province?
[Unknown]: fox
What is the two-letter country code for this unit?
[Unknown]: fx
Is CN=fox, OU=fox, O=fox, L=fox, ST=fox, C=fx correct?
[no]: y


3)从server的keystore中导出server的证书(其中包括server的公钥)。

C:\Program Files\Java\jre7\bin>keytool -export -alias catserver -keystore c:\_tmp\catserver.keystore -storepass catserverks -file c:\_tmp\catserver.cer
Certificate stored in file <c:\_tmp\catserver.cer>

4)从client的keystore中导出client的证书(其中包括client的公钥)。

C:\Program Files\Java\jre7\bin>keytool -export -alias foxclient -keystore c:\_tmp\foxclient.keystore -storepass foxclientks -file c:\_tmp\foxclient.cer
Certificate stored in file <c:\_tmp\foxclient.cer>


5)创建server的truststore文件并导入client的证书(其中包括client的公钥)。

C:\Program Files\Java\jre7\bin>keytool -import -alias foxclient -keystore c:\_tmp\catservertrust.keystore -storepass catservertrustks -file c:\_tmp\foxclient.cer
Owner: CN=fox, OU=fox, O=fox, L=fox, ST=fox, C=fx
Issuer: CN=fox, OU=fox, O=fox, L=fox, ST=fox, C=fx
Serial number: 6eaf996f
Valid from: Wed Nov 05 16:15:41 CST 2014 until: Tue Feb 03 16:15:41 CST 2015
Certificate fingerprints:
MD5: B5:B6:92:66:84:92:A0:C2:F5:40:39:25:F8:66:2A:17
SHA1: 07:42:A3:1A:49:7B:C9:34:4B:6B:FA:37:6C:20:98:D4:20:13:7C:91
SHA256: 37:A5:00:A3:13:00:DE:99:3B:08:47:F6:1E:8A:05:F1:4A:B2:C6:22:20:
E1:AF:0E:05:B2:CE:E0:2F:94:B6:94
Signature algorithm name: SHA1withDSA
Version: 3

Extensions:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 69 3E 6A D0 B5 B1 1F BD 48 46 E1 A4 6C 1F 71 90 i>j.....HF..l.q.
0010: 29 06 3B 32 ).;2
]
]

Trust this certificate? [no]: y
Certificate was added to keystore


6)创建client的truststore文件并导入server的证书(其中包括server的公钥)。

C:\Program Files\Java\jre7\bin>keytool -import -alias catserver -keystore c:\_tmp\foxclienttrust.keystore -storepass foxclienttrustks -file c:\_tmp\catserver.cer
Owner: CN=cat, OU=cat, O=cat, L=cat, ST=cat, C=ct
Issuer: CN=cat, OU=cat, O=cat, L=cat, ST=cat, C=ct
Serial number: 3e421457
Valid from: Wed Nov 05 16:13:52 CST 2014 until: Tue Feb 03 16:13:52 CST 2015
Certificate fingerprints:
MD5: 20:44:7C:E5:30:E6:7A:21:C2:49:64:77:E1:3A:A0:77
SHA1: 8B:02:D2:BE:98:2F:99:94:08:47:E2:96:EC:05:1B:5D:B1:8F:30:2F
SHA256: A6:66:85:F4:C2:B2:06:4E:2E:40:D8:52:84:6E:85:2B:5B:BB:C3:B0:9C:
31:92:99:F5:91:5D:83:67:C8:4D:D8
Signature algorithm name: SHA256withRSA
Version: 3

Extensions:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: F5 91 E6 14 EE EF 5F 24 4F AC 6F A6 B8 36 A6 11 ......_$O.o..6..
0010: 2B 5C DF 04 +\..
]
]

Trust this certificate? [no]: y
Certificate was added to keystore


keysize
如果加密算法是rsa,key size范围512->16384 bits,并且必须是64的倍数。
如果加密算法是dsa,key size范围512->1024 bits,并且必须是64的倍数。

sigalg
如果加密算法是rsa,签名算法可以是md5withrsa/sha1withrsa/sha256withrsa/sha384withrsa/sha512withrsa
如果加密算法是dsa,签名算法可以是sha1withdsa

如果熟悉Java的socket编程,就会发现使用JSSE进行SSL/TLS编程其实和它很相似。
区别在于ServerSocket对象换成了SSLServerSocket,Socket对象换成了SSLSocket对象。

(二)server端处理流程和代码

处理流程:
1)加载server的keystore文件,需要指定keystore的密码(storepass)。
KeyStore类型有如下三种:
jceks - The proprietary keystore implementation provided by the SunJCE provider.
jks - The proprietary keystore implementation provided by the SUN provider.
pkcs12 - The transfer syntax for personal identity information as defined in PKCS #12.

2)加载server的truststore文件,需要指定truststore的密码(storepass)。

3) 创建KeyManagerFactory对象并用1)中加载的keystore和server密钥对的密码(keypass)来初始化。

4) 创建TrustManagerFactory对象并用2)中加载的truststore来初始化。truststore中存的是client的公钥,不需要keypass也可以访问。

5)创建SSLContext并用3)和4)中创建的KeyManagerFactory和TrustManagerFactory对象来初始化。
http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
创建SSLContext是需要给出SSLContext Algorithms。上面这个链接中给出了合法的SSLContext Algorithms,有如下可用值。
SSL - Supports some version of SSL; may support other versions
SSLv2 - Supports SSL version 2 or later; may support other versions
SSLv3 - Supports SSL version 3; may support other versions
TLS - Supports some version of TLS; may support other versions
TLSv1 - Supports RFC 2246: TLS version 1.0 ; may support other versions
TLSv1.1 - Supports RFC 4346: TLS version 1.1 ; may support other versions
TLSv1.2 - Supports RFC 5246: TLS version 1.2 ; may support other versions

6)创建SSLServerSocketFactory,在指定的端口上创建SSLServerSocket并设定需要客户端证书:setNeedClientAuth(true)

7)在SSLServerSocket对象上调用accept()方法等待客户端的连接。
客户端连上来之后这个函数会返回一个SSLSocket对象,在这个对象的输入输出流上进行读写。
在这个SSLSocket对象上可以添加一个HandshakeCompletedListener的监听器,SSL/TLS握手结束后这个监听器的handshakeCompleted方法就会被调用。
客户端有三种方法会触发握手:
- 显式调用startHandshake方法/calling startHandshake which explicitly begins handshakes, or
- 在socket对象上进行read或write操作/any attempt to read or write application data on this socket causes an implicit handshake, or
- 在socket对象上调用getSession方法/a call to getSession tries to set up a session if there is no currently valid session, and an implicit handshake is done.

package learning.net.ssl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.KeyStore;
import java.security.cert.X509Certificate;

import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;

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 = "c:\\_tmp\\catserver.keystore";
        String serverKeyStorePwd = "catserverks";
        String catServerKeyPwd = "catserver";
        String serverTrustKeyStoreFile = "c:\\_tmp\\catservertrust.keystore";
        String serverTrustKeyStorePwd = "catservertrustks";

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

}


(三)client端处理流程和代码
处理流程:(1~5和server相同)
1)加载client的keystore文件。

2)加载client的truststore文件。

3) 创建KeyManagerFactory对象并初始化。

4) 创建TrustManagerFactory对象并初始化。truststore中存的是server的公钥,不需要keypass也可以访问。

5)创建SSLContext并用3)和4)中创建的KeyManagerFactory和TrustManagerFactory对象来初始化。

6)创建SSLSocketFactory,在指定的网络地址和端口上创建SSLSocket。

7)在这个SSLSocket对象的输入输出流上进行读写。

package learning.net.ssl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;

public class FoxClient {
    public static void main(String[] args) throws Exception {
        String clientKeyStoreFile = "c:\\_tmp\\foxclient.keystore";
        String clientKeyStorePwd = "foxclientks";
        String foxclientKeyPwd = "foxclient";
        String clientTrustKeyStoreFile = "c:\\_tmp\\foxclienttrust.keystore";
        String clientTrustKeyStorePwd = "foxclienttrustks";

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

Java-JSSE-SSL/TLS编程代码实例-双向认证

标签:des   blog   http   io   ar   os   使用   java   for   

原文地址:http://blog.csdn.net/fw0124/article/details/41013333

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!