Java网络编程——第十章 安全Sokcet

对称加密,加密解密使用相同的秘钥,收发双发都必须知道这个秘钥,秘钥不可公开;加密解密速度很快
非对称加密,加密解密使用不同的秘钥,公钥用于加密,可以公开,私钥用于解密,不可公开;保证机密性。、完整性、真实性;CPU密集型,速度较慢
一般用法:用非对称加密算法加密对称加密秘钥,最后用对称加密算法加密实际数据

Java安全Socket扩展(JSSE)
javax.net.ssl,定义Java安全网络通信API的抽象类
javax.net 替代构造函数创建安全Socket的抽象Socket工厂类
java.security.cert 处理ssl所需公开秘钥证书的类
com.sun.net.ssl Sun的JSSE参考实现中实现加密算法和协议的具体类,注意该类不是JSSE标准,可以用其他实现替换该包

创建安全Socket

1、使用javax.net.ssl中的SSLSocketFctory,获取SocketFactory实例factory
2、使用factory创建Socket对象,返回的都是javax.net.ssl.SSLSocket
     创建并返回一个连接到指定主机和端口的Socket
     1、public abstract Socket Socket creatSoket(String host, int port) throws IOException, UnknownHostException
     2、public abstract Socket Socket creatSoket(InetAddress host, int port) throws IOException
     从指定本地网络接口和端口连接到指定主机和端口
     3、public abstract Socket Socket creatSoket(String host, int port, InetAddress interface, int localPort) throws IOException, UnknownHostException
     4、public abstract Socket Socket creatSoket(InetAddress host, int port, InetAddress interfacr, int localPort) throws IOException, UnknownHostException
     以代理服务器现有的Socket为起点,经该代理服务器连接到指定的主机和端口,一种autoClose指明该Socket关闭后,底层的proxy Socket是否关闭
     5、public abstract Socket Socket creatSoket(Socket proxy, String host, int port, boolean autoClose) throws IOException
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class HTTPSClient {
    public static void main(String[] args) {
        // https 默认端口
        int port = 443;
        String host = "www.baidu.com";

        SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        SSLSocket socket = null;
        try {
            socket = (SSLSocket) factory.createSocket(host, port);

            // 启用所有密码组
            String[] supported = socket.getSupportedCipherSuites();
            socket.setEnabledCipherSuites(supported);

            Writer out = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
            // https requires the full URL in the GET line
            out.write("GET http://" + host + "/ HTTP/1.1\r\n");
            out.write("Host: " + host + "\r\n");
            out.write("\r\n");
            out.flush();

            // read response
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            // 读取响应头部
            String s;
            while (!(s = in.readLine()).equals("")) {
                System.out.println(s);
            }
            System.out.println();

            // read the length
            String contentLength = in.readLine();
            int length = Integer.MAX_VALUE;
            try {
                length = Integer.parseInt(contentLength.trim(), 16);
            } catch (NumberFormatException ex) {
                // 服务器发送响应的第一行没有发送content-length
            }
            System.out.println(contentLength);


            int c;
            int i = 0;
            while ((c = in.read()) != -1 && i++ < length) {
                System.out.write(c);
            }

            System.out.println();
        } catch (IOException ex) {
            System.err.println(ex);
        } finally {
            try {
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
            }
        }
    }
}

选择密码组
JSSE支持不同认证和加密算法组合,使用SSLSocketFactory 的getSupportedCipherSuites获取支持的组合,处于某些组合强度太弱而被禁用的缘故,可以使用SSLSocket的getEnabledCipherSuites指明可以使用的组合;需要注意的是,实际使用的组合需要客户端和服务端的协商,有一方不同意或者没有相关证书则抛出IOException的子类SSLException,可以通过SSLSocket的setEnabledCipherSuites修改需要使用的密码组;一般使用TLE_ECDHE开头并以SHA256或者SHA384结尾的组合最强;
DES,块加密方式,固定64位一块,不足使用额外位填充
AES,块加密方式,128、192、256位的块,不足位需要额外位填充;
RC4,流加密方式,一次加密一个字节,适用于可能需要一次发送一个字节的协议

事件处理器
由于认证网络通信速度很慢,因此一般需要异步处理连接,JSSE采用标准Java事件模型通知程序,即

得到握手结束事件通知,实现
public interface HandshakeCompletedListener extends java.util.EventListener
     public void handshakeCompleted(HandshakeCompletedEvent event)
其中参数HandshakeCompletedEvent
public class HandshakeCompletedEvent extends java.util.EventObject,该类提供以下方法
     public SSLSession getSession()
     public String getCipherSuite()
     public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifedException
     public SSLSocket getSocket()
通过HandshakeCompletedListener的
     public abstract void addHandshakeCompletedListener(HandshakeCompletedListener listener)
     public abstract void removeHandshakeCompletedListener(HandshakeCompletedListener listener) throws IllegleArguementException
实现对SSLSocket对象的握手结束事件的注册

会话管理,由于安全握手时间较长,会话管理解决Session共享问题,SSL允许扩展到多个Socket的Session,相同Session的不同Sokcet可以共享同一组秘钥,这样只有会话中的第一个Socket需要生成和交换秘钥的开销;对于在一个较短的时间内对一个主机的一个端口打开多个安全Socket,JSSE会自动重用会话秘钥;安全Socket的会话有SSLSession管理
获得Session
public abstract SSLSession getSession()
是否允许创建会话
public abstract void setEnableSessionCreation(boolean allowCreation)
重新开始会话,即放弃之前的秘钥、证书等
public abstract void startHandshake() throws IOException

客户端模式,一般而言不需要验证客户端的合法性
public abstract void setUseClientMod(boolean mode) throws IlleagleArguementException,如果传入true,则使用client的模式,不需要认证;客户端服务端都可以使用该方法;注意该方法对于任何指定Socket只能设置一次,否则抛出IllegleArguementException
public abstract void setNeedClientAuth(boolean needAuthentication),该方法用于SSLServerSocket,指明与他连接的客户端是否需要认证;若Socket不用在服务端,则抛出IlleglArguementException

服务端安全Socket,可客户端SSLSocket类似,public abstract class SSLServerSosocket extends ServerSocket, 其创建方式通过 public abstract class SSLServerSocketFactory获得SSLServerSocketFactory,进而通过该SSLServerSocketFactory创建Socket,creatServerSocket();
注意,SSLServerSocketFactory.getDefault(),返回的factory只支持服务器认证,不支持加密,sun的参考实现需要com.sun.net.ssl.SSLContext对象负责创建已经充分配置的和初始化的SSLServerSocket,一般步骤
1、使用keytool生成公开秘钥和证书
2、申请可信任第三方认证生成的整数
3、创建SSLContext
4、为证书源创建TrustManagerFactory
5、位使用的秘钥类型创建KeyManagerFactory
6、为秘钥和证书数据库创建KeyStore对象,对于Oracle默认值为JKS
7、用秘钥和证书填充KeyStore对象
8、用KeyStore及其短语初始化KeyManagerFactory,
9、用keyManagerFactory的秘钥管理器(必要)、TrustManagerFactory的信任管理器(可选)和一个随机源(可选)初始化上下文;后两个若使用默认值,则为null
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;


public class SecureOrderTaker {
    public final static int PORT = 7000;
    public final static String algorithm = "SSL";

    public static void main(String[] args) {
        try {
            SSLContext context = SSLContext.getInstance(algorithm);
            // 参考实现只支持X.509秘钥
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            // Oracle默认秘钥库类型
            KeyStore ks = KeyStore.getInstance("JKS");
            /* 出于安全考考虑,每个秘钥库都要使用短语加密
             * 从从磁盘读取前,都必须提供这个短语,短语以char[]形式存储
             * 这样可以快速送内存擦除,而非等待GC
             */
            //char[] password = System.console().readPassword();
            char[] password = "wyc660904".toCharArray();
            // 证书文件的创建 http://www.cnblogs.com/youxia/p/java002.html
            ks.load(new FileInputStream("C:\\Users\\Administrator\\Desktop\\程序学习\\java网络编程\\JavaNetworkProgramming\\src\\ch10\\jnp4e.keys"), password);
            kmf.init(ks, password);
            context.init(kmf.getKeyManagers(), null, null);

            // 擦除秘钥
            Arrays.fill(password, '0');

            SSLServerSocketFactory factory = context.getServerSocketFactory();
            SSLServerSocket server = (SSLServerSocket) factory.createServerSocket(PORT);

            // 增加匿名(未认证)密码组
            String[] supported = server.getSupportedCipherSuites();
            String[] anonCipherSuitesSupported = new String[supported.length];
            int numAnonCipherSuitesSupported = 0;
            for (int i = 0; i < supported.length; i++) {
                if (supported[i].indexOf("_anon_") > 0) {
                    anonCipherSuitesSupported[numAnonCipherSuitesSupported++] = supported[i];
                }
            }
            String[] oldEnabled = server.getEnabledCipherSuites();
            String[] newEnabled = new String[oldEnabled.length + numAnonCipherSuitesSupported];
            System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length);
            System.arraycopy(anonCipherSuitesSupported, 0, newEnabled, oldEnabled.length, numAnonCipherSuitesSupported);
            server.setEnabledCipherSuites(newEnabled);

            // 设置完成,可以进行实际通信
            while (true) {
                try (Socket theConnection = server.accept()) {
                    InputStream in = theConnection.getInputStream();
                    int c;
                    while ((c = in.read()) != -1) {
                        System.out.write(c);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        } catch (IOException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException
                | CertificateException | UnrecoverableKeyException e) {
            e.printStackTrace();
        }
    }
}

配置SSLServerSocket和SSLSocket类似,同样可以选择密码组、会话管理、客户端模式



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
// C/C++, 利用OpenSSL库对Socket传输进行安全加密(RSA+AES) // 1. 利用RSA安全传输AES生成密钥所需的Seed(32字节) // 2. 利用AES_encrypt/AES_decrypt对Socket上面的业务数据进行AES加密/解密 // --- // * 理论上只需要AES就能保证全部流程,但由于AES加密所需要的AES-KEY是一个结构 // * 这个一个结构,如果通过网络进行传输,就需要对它进行网络编码,OpenSSL里面没有现成的API // * 所以就引入RSA来完成首次安全的传输,保证Seed不会被窃听 // * 同样,只使用RSA也能完成全部流程,但由于RSA的处理效率比AES低, // * 所以在业务数据传输加密上还是使用AES // --- // 下面的代码包含了上述传输加密流程所需的所有步骤(OpenSSL部分) // 在实际的Socket应用开发时,需要将这些步骤插入到Client/Server网络通信的特定阶段 // --- // 为能完成代码的编译和执行,需要先安装OpenSSL执行库及开发库 // 以Debian为例,需要安装openssl 和 libssl-dev // 编译命令: g++ -o rsa-encrypt rsa-encrypt.cpp -lcrypto // --- // 所需的OpenSSL主要的API及功能描述 // 1. RSA_generate_key() 随机生成一个RSA密钥对,供RSA加密/解密使用 // 2. i2d_RSAPublicKey() 将RSA密钥对里面的公钥提出到一个BUF,用于网络传输给对方 // 3. d2i_RSAPublicKey() 将从网络传过来的公钥信息生成一个加密使用的RSA(它里面只有公钥) // 4. RSA_public_encrypt() 使用RSA的公钥对数据进行加密 // 5. RSA_private_decrypt() 使用RSA的私钥对数据进行加密 // 6. AES_set_encrypt_key() 根据Seed生成AES密钥对中的加密密钥 // 7. AES_set_decrypt_key() 根据Seed生成AES密钥对中的解密密钥 // 8. AES_encrypt() 使用AES加密密钥对数据进行加密 // 9. AES_decrypt() 使用AES解密密钥对数据进行解密 // --- // 一个典型的安全Socket的建立流程, 其实就是如何将Server随机Seed安全发给Client // -- C: Client S:Server // C: RSA_generate_key() --> RSAKey --> i2d_RSAPublicKey(RSAKey) --> RSAPublicKey // C: Send(RSAPublicKey) TO Server // S: Recv() --> RSAPublicKey --> d2i_RSAPublicKey(RSAPublicKey) --> RSAKey // S: Rand() --> Seed --> RSA_public_encrypt(RSAKey, Seed) --> EncryptedSeed // S: Send(EncryptedSeed) TO Client // C: Recv() --> EncryptedSeed --> RSA_private_decrypt(RSAKey, EncryptedSeed) --> Seed // --- 到此, Client和Server已经完成完成传输Seed的处理 // --- 后面的流程是它们怎样使用这个Seed来进行业务数据的安全传输 // C: AES_set_encrypt_key(Seed) --> AESEncryptKey // C: AES_set_decrypt_key(Seed) --> AESDecryptKey // S: AES_set_encrypt_key(Seed) --> AESEncryptKey // S: AES_set_decrypt_key(Seed) --> AESDecryptKey // --- Client传输数据给Server // C: AES_encrypt(AESEncryptKey, Data) --> EncryptedData --> Send() --> Server // S: Recv() --> EncryptedData --> AES_decrypt(AESDecryptKey, EncryptedData) --> Data // --- Server传输数据给Client // S: AES_encrypt(AESEncryptKey, Data) --> EncryptedData --> Send() --> Client // C: Recv() --> EncryptedData --> AES_decrypt(AESDecryptKey, EncryptedData) --> Data / ========================================================================================= */

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值