SSL安全协议理论及双向认证的简单实现

安全套接层(Secure Sockets Layer.SSL)是基于Internet基础的一种保证私秘性的安全协议。它能使客户服务应用间的通信不被窃听,并能始终对服务器和客户端进行认证。SSL协议要求建立在可靠的传输层协议之上。SSL协议是与应用层协议独立无关的,高层的应用协议能透明的建立于SSL协议上。SSL协议在应用层协议通信之前就已完成加密算法、通信密钥的协商及服务器认证工作。在此后应用层协议所传送的数据都会被加密,从而保证通信的私密性。

  • 发展状况

SSL协议于1994年由Netscape公司提出的一个关注网络信息安全的信息加密传输协议。它的目的就是为了客户端浏览器到服务器端之间的信息传输构建一个加密的安全的通道。目前最新的版本是V3.1版本,不过大多数使用的是V3.0版本。

随着网络安全意识的普遍提升,越来越多的网络应用逐步采用了SSL加密传输。由于SSL技术采用了加密、认证、密钥协商等机制来保障通信双方数据传输的保密性、完整性和通信端点的认证,因此SSL协议目前在网银交易、邮箱登陆、数据加密传输等方面得到了广泛的应用。

SSL协议由于运行在TCP/IP层之上,应用层之下,为应用程序提供加密数据通道,并采用RC4、MD5以及RSA等加密算法,统一适用于商业信息的加密。但SSL协议在实现过程中为了满足兼容性和易用性的要求,自身仍然存在一定的脆弱性的问题,攻击者可以利用SSL协议的弱点对其进行攻击以获取敏感信息。

  • 基本理论

    安全套接字(Secure Socket Layer,SSL)协议是Web浏览器与Web服务器之间安全交换信息的协议,提供两个基本的安全服务:鉴别与保密。

    总结起来,SSL协议有三个特性:

    保密:在握手协议中定义了会话密钥后,所有的消息都被加密。

    鉴别:可选的客户端认证,和强制的服务器端认证。

    完整性:传送的消息包括消息完整性检查(使用MAC)。

    SSL介于应用层和TCP层之间。应用层数据不再直接传递给传输层,而是传递给SSL层,SSL层对从应用层收到的数据进行加密,并增加自己的SSL头。

    图1 在互联网模型中SSL层所在的位置

SSL协议分为2层:底层为建立在可靠的传输协议(TCP)之上的SSL记录协议;上层为建立在SSL记录协议之上的SSL握手协议、SSL修改密文规约协议和SSL告警协议。SSL记录协议涉及应用程序提供的信息分段、压缩、数据认证和加密,用于封装不同的上层协议;SSL握手协议用来在服务器和客户机在传输应用数据之前交换版本号、协商加密算法和压缩算法、(相互)身份认证并交换密钥;SSL修改密文规约协议用来表示密码策略的变化;SSL告警协议用于当握手过程或数据加密等操作出错或发生异常情况时,向对方发出警告或终止当前连接。SSL协议提供的服务主要有:认证用户和服务器,确保数据发送到正确的客户机和服务器;加密数据以防止数据中途被窃取;维护数据的完整性,确保数据在传输过程中不被改变。

  • SSL应用理论

    (1) 匿名SSL连接:又称单向认证。这是SSL安全连接的基本模式,主要的浏览器都支持这种方式,适合单向数据安全传输应用。在这种模式下Client没有数字证书,只是Server具有证书。典型的应用就是用户进行网站ID和口令的匿名认证。

    (2) 对等的安全认证:双方认证。在此种模式下通信双方都可发起、接收SSL连接请求。双方可以利用安全应用程序或安全代理软件。

    (3) 电子商务中的应用。电子商务与网上银行交易不同,因为有商户参加,顾客到商家指定的网站去填写订单,向商家报出信用卡号,商家向银行查询信用卡号,有效了才能继续交易。其中顾客可以没有数字证书,商家和银行必须有数字证书。在顾客与商家通信中采用匿名SSL连接。而商家与银行之间传送的是顾客数据,必须互相验证对方的数字证书,采用SSL连接保证传输信息的安全。

  • SSL应用实例

    这里通过一个小的Java程序演示基于SSL安全协议的信息通信。主要利用Java中SSLSocket和SSLServerSocket分别作为客户端和服务器端的对象进行信息的交流。

    首先,通过Java中自带的keytool工具生成keystore。采用keytool –genkeypair命令在默认的密钥库下面生成一个key。

这里生成的文件.keystore文件默认放在当前路径下,这里的密钥文件就是双方进行通信的凭证,下面我们利用keytool -list –v命令可以显示默认keystore的key详细信息。

  • 服务器端代码的实现

通过以上步骤生成了keystore,下面就可以通过代码使用该密钥进行基于SSL的信息通信了。服务器端通过SSLServerSocket进行与客户端的通信。

public class KeystoreTest {
    public static void main(String[] args) throws Exception{
        String key="C:\\Users\\Administrator\\Desktop\\.keystore";
        KeyStore keystore=KeyStore.getInstance("JKS");
        keystore.load(new FileInputStream(key),"123456".toCharArray());
        //创建jkd密钥访问库    123456是keystore密码。
        KeyManagerFactory kmf=KeyManagerFactory.getInstance("SunX509");
        kmf.init(keystore,"123456".toCharArray());
        //创建管理jks密钥库的x509密钥管理器,用来管理密钥,需要key的密码
        SSLContext sslc=SSLContext.getInstance("SSLv3");
        // 构造SSL环境,指定SSL版本为3.0,也可以使用TLSv1,但是SSLv3更加常用。
        sslc.init(kmf.getKeyManagers(),null,null);
        SSLServerSocketFactory sslfactory=sslc.getServerSocketFactory();
         SSLServerSocket serversocket=(SSLServerSocket) sslfactory.createServerSocket(9999);
         while(true)
         {
             Socket socket=serversocket.accept();
             try{
                 OutputStream os=socket.getOutputStream();
                 InputStream is=socket.getInputStream();
                 byte[] buf=new byte[1024];
                 int len=is.read(buf);
                 System.out.println("服务器接收客户端信息:"+new String(buf));
                 os.write("ssl test from server".getBytes());
                 os.close();
                 is.close();
             }catch(Exception e)
             { }
         }
    }
}
  • 客户端代码实现

    客户端实现同样需要keystore,这里使用的是同一分keystore,当然可以不使用同一份,可以通过把keystore信息导出到另外一份文件中给客户端使用。这里为了方便,使用同一份keystore进行通信。客户端主要代码如下:

public class KeystoreTestClient {
    public static void main(String[] args) throws Exception{
        String key="C:\\Users\\Administrator\\Desktop\\.keystore";
        KeyStore keystore=KeyStore.getInstance("JKS");  //创建一个keystore来管理密钥库
        keystore.load(new FileInputStream(key),"123456".toCharArray());
        TrustManagerFactory tmf=TrustManagerFactory.getInstance("SunX509");
        tmf.init(keystore);
        SSLContext sslc=SSLContext.getInstance("SSLv3");
        // 构造SSL环境,指定SSL版本为3.0,也可以使用TLSv1,但是SSLv3更加常用。
        sslc.init(null,tmf.getTrustManagers(),null);
        SSLSocketFactory sslfactory=sslc.getSocketFactory();
         SSLSocket socket=(SSLSocket) sslfactory.createSocket("127.0.0.1",9999);
        //创建serversocket通过传输数据来验证授权
         InputStream is=socket.getInputStream();
         OutputStream os=socket.getOutputStream();
         os.write("info from client".getBytes());
         byte[] buf=new byte[1024];
         int len=is.read(buf);
         System.out.println("客户端接收服务器信息:"+new String(buf));
         os.close();
         is.close();
    }
}
  • 测试结果

完成了代码部分之后,进行测试,需要首先启动服务器端程序,服务器端运行之后进行等待,主要利用accept方法的阻塞,让服务器端等待来自客户端的信息,测试结果如下:

服务器端代码控制台打印输出为

图 服务器端输出

图 客户端输出

通过控制台打印输出的信息可以看出,客户端和服务器端完成了信息的通信,打印出了预期的结果,实现了基于SSL安全协议的信息通信。

通过进一步的了解,知道了上面的代码的实现仅仅实现了单向认证,并进行了相互之间的通信。如果要实现双向认证,如何实现呢?

  • 双向认证

双向认证和单向认证仅仅多了一项,具体过程如下:

为了实现消息认证。

Server需要:

1)KeyStore: 其中保存服务端的私钥

2)Trust KeyStore:其中保存客户端的授权证书

同样,Client需要:

1)KeyStore:其中保存客户端的私钥

2)Trust KeyStore:其中保存服务端的授权证书

在这里我还是推荐使用Java自带的keytool命令,去生成这样信息文件。当然目前非常流行的开源的生成SSL证书的还有OpenSSL。 OpenSSL用C语言编写,跨系统。但是我们可能在以后的过程中用java程序生成证书的方便性考虑,还是用JDK自带的keytool。

1)生成服务端私钥,并且导入到服务端KeyStore文件中

keytool -genkey -alias serverkey -keystore kserver.keystore

过程中,分别需要填写,根据需求自己设置就行

serverkey私钥的密码,不填写和keystore的密码一致。这里千万注意,直接回车就行了,不用修改密码。否则在后面的程序中以及无法直接应用这个私钥,会报错。(这个说法我并没有验证)

就可以生成kserver.keystore文件

server.keystore是给服务端用的,其中保存着自己的私钥

上面并没有指定密钥生成位置,那么kserver.keystore文件在哪里呢?

2)根据私钥,导出服务端证书

keytool -export -alias serverkey -keystore kserver.keystore -file server.crt

server.crt就是服务端的证书

3)将服务端证书,导入到客户端的Trust KeyStore中

keytool -import -alias serverkey -file server.crt -keystore tclient.keystore

tclient.keystore是给客户端用的,其中保存着受信任的证书

采用同样的方法,生成客户端的私钥,客户端的证书,并且导入到服务端的Trust KeyStore中

1)keytool -genkey -alias clientkey -keystore kclient.keystore

2)keytool -export -alias clientkey -keystore kclient.keystore -file client.crt

3)keytool -import -alias clientkey -file client.crt -keystore tserver.keystore

如此一来,生成的文件分成两组

服务端保存:kserver.keystore tserver.keystore

客户端保存:kclient.keystore tclient.kyestore

客户端:

/**
 * SSL Client
 *
 */
public class SSLClient {  

    private static final String DEFAULT_HOST                    = "127.0.0.1";
    private static final int    DEFAULT_PORT                    = 7777;  

    private static final String CLIENT_KEY_STORE_PASSWORD       = "123456";
    private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";  

    private SSLSocket           sslSocket;  

    /**
     * 启动客户端程序
     *
     * @param args
     */
    public static void main(String[] args) {
        SSLClient client = new SSLClient();
        client.init();
        client.process();
    }  

    /**
     * 通过ssl socket与服务端进行连接,并且发送一个消息
     */
    public void process() {
        if (sslSocket == null) {
            System.out.println("ERROR");
            return;
        }
        try {
            InputStream input = sslSocket.getInputStream();
            OutputStream output = sslSocket.getOutputStream();  

            BufferedInputStream bis = new BufferedInputStream(input);
            BufferedOutputStream bos = new BufferedOutputStream(output);  

            bos.write("Client Message".getBytes());
            bos.flush();  

            byte[] buffer = new byte[20];
            bis.read(buffer);
            System.out.println(new String(buffer));  

            sslSocket.close();
        } catch (IOException e) {
            System.out.println(e);
        }
    }  

    /**
     * <ul>
     * <li>ssl连接的重点:</li>
     * <li>初始化SSLSocket</li>
     * <li>导入客户端私钥KeyStore,导入客户端受信任的KeyStore(服务端的证书)</li>
     * </ul>
     */
    public void init() {
        try {
            SSLContext ctx = SSLContext.getInstance("SSL");  

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");  

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

            ks.load(new FileInputStream(".../kclient.keystore"), CLIENT_KEY_STORE_PASSWORD.toCharArray());  //改成相应的文件路径
            tks.load(new FileInputStream(".../tclient.keystore"), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray()); //改成相应的文件路径 

            kmf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());
            tmf.init(tks);  

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

            sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket(DEFAULT_HOST, DEFAULT_PORT);
        } catch (Exception e) {
            System.out.println(e);
        }
    }  

}  

服务器:

/***********************************************************************************************************************
 *
 * 1)生成服务端私钥</li>
 * keytool -genkey -alias serverkey -keystore kserver.keystore
 * 2)根据私钥,到处服务端证书
 * keytool -exoport -alias serverkey -keystore kserver.keystore -file server.crt
 * 3)把证书加入到客户端受信任的keystore中
 * keytool -import -alias serverkey -file server.crt -keystore tclient.keystore
 *
 **********************************************************************************************************************/  

/**
 * SSL Server
 *
 */
public class SSLServer {  

    private static final int    DEFAULT_PORT                    = 7777;  

    private static final String SERVER_KEY_STORE_PASSWORD       = "123456";
    private static final String SERVER_TRUST_KEY_STORE_PASSWORD = "123456";  

    private SSLServerSocket     serverSocket;  

    /**
     * 启动程序
     *
     * @param args
     */
    public static void main(String[] args) {
        SSLServer server = new SSLServer();
        server.init();
        server.start();
    }  

    /**
     *
     * 听SSL Server Socket
     * 由于该程序不是演示Socket监听,所以简单采用单线程形式,并且仅仅接受客户端的消息,并且返回客户端指定消息
     *
     */
    public void start() {
        if (serverSocket == null) {
            System.out.println("ERROR");
            return;
        }
        while (true) {
            try {
                Socket s = serverSocket.accept();
                InputStream input = s.getInputStream();
                OutputStream output = s.getOutputStream();  

                BufferedInputStream bis = new BufferedInputStream(input);
                BufferedOutputStream bos = new BufferedOutputStream(output);  

                byte[] buffer = new byte[20];
                bis.read(buffer);
                System.out.println(new String(buffer));  

                bos.write("Server Echo".getBytes());
                bos.flush();  

                s.close();
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }  

    /**
     *
     * ssl连接的重点:
     * 初始化SSLServerSocket
     * 导入服务端私钥KeyStore,导入服务端受信任的KeyStore(客户端的证书)
     *
     */
    public void init() {
        try {
            SSLContext ctx = SSLContext.getInstance("SSL");  

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");  

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

            ks.load(new FileInputStream(".../kserver.keystore"), SERVER_KEY_STORE_PASSWORD.toCharArray());//改成相应的文件路径
            tks.load(new FileInputStream(".../tserver.keystore"), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray()); //改成相应的文件路径

            kmf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());
            tmf.init(tks);  

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

            serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(DEFAULT_PORT);
            serverSocket.setNeedClientAuth(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}  

如此以来,就完成了双向认证,在双向认证的基础上进行通信,两个字:安全!!

参考:http://www.cnblogs.com/yqskj/p/3142006.html

时间: 2024-10-09 23:46:41

SSL安全协议理论及双向认证的简单实现的相关文章

使用 jdk自带ssl包 进行 https通讯双向认证

package com.iraid.test; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.security.Key

https 单向双向认证说明_数字证书, 数字签名, SSL(TLS) , SASL

转自:https 单向双向认证说明_数字证书, 数字签名, SSL(TLS) , SASL 因为项目中要用到TLS + SASL 来做安全认证层. 所以看了一些网上的资料, 这里做一个总结. 1. 首先推荐几个文章: 数字证书: http://www.cnblogs.com/hyddd/archive/2009/01/07/1371292.html 数字证书和SSL: http://www.2cto.com/Article/201203/121534.html 数字签名: http://www.

tomcat配置SSL双向认证

一.SSL简单介绍 SSL(Secure Sockets Layer 安全套接层)就是一种协议(规范),用于保障客户端和服务器端通信的安全,以免通信时传输的信息被窃取或者修改. 怎样保障数据传输安全?  客户端和服务器端在进行握手(客户端和服务器建立连接和交换参数的过程称之为握手)时会产生一个“对话密钥”(session key),用来加密接下来的数据传输,解密时也是用的这个“对话密钥”,而这个“对话密钥”只有客户端和服务器端知道.也就是说只要这个“对话密钥”不被破解,就能保证安全. 2. 客户

nginx配置ssl加密(单双向认证、部分https)

默认nginx是没有安装ssl模块的,需要编译安装nginx时加入--with-http_ssl_module选项. 关于SSL/TLS原理请参考这里,如果你只是想测试或者自签发ssl证书,参考 这里 . 提示:nignx到后端服务器由于一般是内网,所以不加密. 1. 全站SSL 全站做ssl是最常见的一个使用场景,默认端口443,而且一般是单向认证. server { listen 443; server_name example.com; root /apps/www; index inde

【密码学】ssl双向认证和单向认证原理

有朋友在搞一个项目,周末有聊到一些安全性的东西,很自然会想起https,但https究竟如何实施,其原理又是什么? 基于ssl,一般的应用都是单向认证,如果应用场景要求对客户来源做验证也可以实现成双向认证. 网上google一下: 为了便于更好的认识和理解 SSL 协议,这里着重介绍 SSL 协议的握手协议.SSL 协议既用到了公钥加密技术又用到了对称加密技术,对称加密技术虽然比公钥加密技术的速度快,可是公钥加密技术提供了更好的身份认证技术.SSL 的握手协议非常有效的让客户和服务器之间完成相互

java中关于SSL/TSL的介绍和如何实现SSL Socket双向认证

一.        SSL概述 SSL协议采用数字证书及数字签名进行双端实体认证,用非对称加密算法进行密钥协商,用对称加密算法将数据加密后进行传输以保证数据的保密性,并且通过计算数字摘要来验证数据在传输过程中是否被篡改和伪造,从而为敏感数据的传输提供了一种安全保障手段. SSL协议提供的服务主要有: 1)认证用户和服务器,确保数据发送到正确的客户机和服务器 认证用户和服务器的合法性,使它们能够确信数据将被发送到正确的客户机和服务器上.客户机和服务器都有各自的识别号,这些识别号由公开密钥进行编号,

Nginx、SSL双向认证、PHP、SOAP、Webservice、https

本文是1:1模式,N:1模式请参见新的一篇博客<SSL双向认证(高清版)> ----------------------------------------------------- 我是分割线 --------------------------------------------------------- 标题太长了不知道该怎么起,索性就把keyword列出来吧~ WebService的WS-*搞了一天没搞定,看样子PHP应该是彻底抛弃SOAP协议了,google翻烂了也没找到什么靠谱的解

java实现ssl单/双向认证通信[推荐]

有关SSL的原理和介绍在网上已经有不少,对于Java下使用keytool生成证书,配置SSL通信的教程也非常多.但如果我们不能够亲自动手做一个SSL Sever和SSL Client,可能就永远也不能深入地理解Java环境下,SSL的通信是如何实现的.对SSL中的各种概念的认识也可能会仅限于可以使用的程度.本文通过构造一个简单的SSL Server和SSL Client来讲解Java环境下SSL的通信原理. 首先我们先回顾一下常规的Java Socket编程.在Java下写一个Socket服务器

[转帖]nginx配置ssl加密(单/双向认证、部分https)

nginx配置ssl加密(单/双向认证.部分https) https://segmentfault.com/a/1190000002866627 nginx下配置ssl本来是很简单的,无论是去认证中心买SSL安全证书还是自签署证书,但最近公司OA的一个需求,得以有个机会实际折腾一番.一开始采用的是全站加密,所有访问http:80的请求强制转换(rewrite)到https,后来自动化测试结果说响应速度太慢,https比http慢慢30倍,心想怎么可能,鬼知道他们怎么测的.所以就试了一下部分页面h