zoukankan      html  css  js  c++  java
  • 安全Socket

    保护通信

    经过开放通道(如公共Internet)的秘密通信绝对需要对数据加密.适合计算机实现的大多数加密机制都是基于密钥思想的.密钥是一种更加一般化的口令,并不限于文本.明文消息根据一种数学算法与密钥的各个位组合,生成加密的密文.使用的密钥位数越多,通过暴力破解的方法解密消息时就会越困难.

    在传统的秘密密匙(对称加密)加密中,加密和解密使用相同的密钥.发送方和接收方都需要知道这个密钥.假设A希望给B发送一个秘密消息.A首先向B发送一个交换秘密的密钥.A和B以后的通信都是用这个密钥进行加密解密.很容易发现问题,如果C监听了A和B之间的连接,那么这个密钥C也会得知.A和B之间的通信则没有任何的秘密.

    解决对称加密被监听的方法是加密和解密使用不同的密钥(非对称加密).一个密钥成为公开密钥,用于加密数据.这个密钥可以提供给任何人.另外一个密钥成为私有秘钥用来解密,这个私有秘钥必须秘密保存.假设A希望给B发送秘密消息,A需要先向B要一个公开密钥,然后使用B提供的公开密钥对消息进行加密,因为这个消息是通过B的公开密钥加密的,只有B的私有秘钥才可以解密.这种情况下C也可以获取公共密钥,但是C只能使用这个密钥进行加密而不能解密.A可以秘密的和B发送消息.

    为什么是A可以秘密的和B发送消息呢?因为B要回复消息也需要使用相同的方法获取A的公共密钥,然后使用A的公共密钥加密回复给A.也就是说A向B发送消息使用B的公钥加密,B接收A的消息使用自己的私钥解密.B回复A使用A的公钥加密,A接收B的消息使用自己的私钥解密.这种情况下非对称加密的复杂性就体现出来了.实际的做法中,我们可能是这么做:

    A向B获取公钥,将对称加密密码使用公钥加密发送给B,B使用自己的私钥解密.之后A,B通信使用这个对称加密手段通信.

    非对称加密看似很安全但实则有一个致命的漏洞.如果A向B获取公钥时,C监听了通信,并且伪造了B的公钥,将一个假的公钥发送给A,A并不知道这个是假的,则使用这个假的公钥进行加密对称密码,C自己解密后获取到了对称加密的密码,然后又使用B的公钥把这个密码加密发送给B.B也不知道这个密码已经被看过了.A,B两人甚至都不知道C这个中间人的存在,这称为中间人攻击.

    实际中解决方案是B把公钥交给可信任的第三方认证机构,A从第三方认证机构获取B的公钥,虽然C依然可以潜伏在第三方机构,但是对于A和B直接交换公钥来说,C要想进行攻击就困难的多了.

    演示代码

    要使用安全的Socket需要有以下几个步骤。

    1 使用keytool生成公开的密钥和证书(keytool是JDK自带的工具)生成的证书在cmd目录下。

    2 花钱请可信任的第三方认证你的证书(本地演示则不用).

    3 为你使用的算法创建一个SSLContext.

    4 为你要使用的证书源创建一个TrustManagerFactory.

    5 为你要使用的密钥类型创建一个KeyManagerFactory。

    6 为密钥和证书数据库创建一个KeyStore对象

    7 用密钥和证书填充KeyStore对象。例如从文件系统添加。

    8 使用KeyStore及其口令初始化KeyManagerFactory。

    9 用KeyManagerFactory中的密钥管理器,TrustManagerFactory中的信任管理器和一个随机源来创建初始化上下文。

    注:keytool工具使用方法参考 http://www.mybatis.cn/archives/1054.html

    注:以下代码片段参考 https://blog.csdn.net/mn960mn/article/details/49746085

    package com.datang.bingxiang.demo;
    
    import java.io.BufferedReader;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.security.KeyStore;
    
    import javax.net.SocketFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.TrustManagerFactory;
    
    public class SSLClient {
        static SSLSocket createSocket(String host, int port) throws Exception {
            KeyStore ks = KeyStore.getInstance("jks");
            InputStream input = new FileInputStream("C:/Users/86152/trust.jks");
            ks.load(input, "123456".toCharArray());
    
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(ks);
    
            SSLContext context = SSLContext.getInstance("TLSv1.2");
    
            /**
             * KeyManager[] 第一个参数是授权的密钥管理器,用来授权验证。TrustManager[]第二个是被授权的证书管理器,
             * 用来验证服务器端的证书。第三个参数是一个随机数值,可以填写null。如果只是服务器传输数据给客户端来验证,就传入第一个参数就可以,
             * 客户端构建环境就传入第二个参数。双向认证的话,就同时使用两个管理器。
             */
            context.init(null, tmf.getTrustManagers(), null);
    
            input.close();
    
            SocketFactory sf = context.getSocketFactory();
            return (SSLSocket) sf.createSocket(host, port);
        }
    
        public static void main(String[] args) throws Exception {
            Socket s = createSocket("127.0.0.1", SSLServer.port);
            InputStream input = s.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(input));
            String str = null;
            while((str=br.readLine())!=null) {
                System.out.println(str);
            }
        }
    }
    package com.datang.bingxiang.demo;
    
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.security.KeyStore;
    
    import javax.net.ssl.KeyManagerFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLServerSocket;
    import javax.net.ssl.SSLServerSocketFactory;
    
    public class SSLServer {
        public static final int port = 4488;
    
        static SSLServerSocket createServerSocket(int port) throws Exception {
            KeyStore ks = KeyStore.getInstance("jks");
            InputStream input = new FileInputStream("C:/Users/86152/myserver.jks");
            ks.load(input, "123456".toCharArray());
    
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, "123456".toCharArray());
    
            SSLContext context = SSLContext.getInstance("TLSv1.2");
    
            /**
             * KeyManager[] 第一个参数是授权的密钥管理器,用来授权验证。TrustManager[]第二个是被授权的证书管理器,
             * 用来验证服务器端的证书。第三个参数是一个随机数值,可以填写null。如果只是服务器传输数据给客户端来验证,就传入第一个参数就可以,
             * 客户端构建环境就传入第二个参数。双向认证的话,就同时使用两个管理器。
             */
            context.init(kmf.getKeyManagers(), null, null);
    
            input.close();
    
            SSLServerSocketFactory ssf = context.getServerSocketFactory();
            return (SSLServerSocket) ssf.createServerSocket(port);
        }
    
        public static void main(String[] args) throws Exception {
            final ServerSocket ss = createServerSocket(port);
            System.out.println("ssl server startup at port " + port);
            while (true) {
                Socket s = ss.accept();
                OutputStream output = null;
                output = s.getOutputStream();
                output.write("hello word".getBytes());
                output.flush();
                s.close();
                System.out.println("3333333333");
            }
        }
    }
  • 相关阅读:
    java Object 类 与 Wrapper包装类
    java == 和equals()
    CPPU OJ | 开发日志
    第十八次CSP认证游记 | 2019.12.15
    CTF入门 |“男神”背后的隐写术
    Luogu2422 | 良好的感觉 (单调栈)
    Luogu4316 | 绿豆蛙的归宿 (期望DP)
    简单电路中的逻辑学(一)
    UVA12124 | Assemble (二分)
    这里是一些常用的工具网站
  • 原文地址:https://www.cnblogs.com/zumengjie/p/15112303.html
Copyright © 2011-2022 走看看