# JAVA Security

# 概述

Java 安全体系主要有四部分组成

  • JCA (Java Cryptography Architecture)
  • JCE(Java Cryptography Extension),
  • JSSE(Java Secure Sockets Extension)
  • JAAS(Java Authentication and Authorization Service)

JCA和JCE是java平台提供的安全和加密服务的两种API。实现接口打包成Provider成为安全提供者

  • Bouncy Castle
  • Commons Codec

术语

  • 明文(Plaintext)
  • 密文(Ciphertext)
  • 发送者(Sender)
  • 接收者(Receiver)
  • 加密(Encryption)
  • 加密算法(Encryption Algorithm)
  • 加密密钥(Encryption key)
  • 解密(Decryption)
  • 解密算法(Decryption Algorithm)
  • 解密密钥(Decryption key)
  • 密码分析(Cryptanalysis)
  • 密码分析者(Cryptanalyst)
  • 被动攻击(Passive Attack)
  • 主动攻击(Active Attack)
  • 密码体制(Cipher System) : 明文空间,密文空间,密钥空间,加密算法,解密算法
  • 密码协议(Cryptographic Protocal)
  • 密码系统(Cryptography)

# 一、JCA (Java加密体系结构)

JCA主要提供加密框架

主要包含消息摘要(MessageDigest), 带密码的消息摘要(HMAC),加密(Cipher),数字签名(Signature),证书(Certificate)

# MessageDigest

  • MD5
  • SHA

# HMAC

# Cipher

  • 对称加密
    • DES
    • AES
    • Blowfish
  • 非对称加密
    • RSA
    • DSA
    • DH

# Signature

数字签名和签名验证

# Certificate

证书

# 二、JCE

  • Bouncy Castle
  • Commons Codec

# 三、JSSE

Java安全套接字扩展——JSSE,它包含了实现Internet安全通信的一系列包的集合,是SSL和TLS的纯Java实现,同时它是一个开放的标准,每个公司都可以自己实现JSSE,通过它可以透明地提供数据加密、服务器认证、信息完整性等功能,就像使用普通的套接字一样使用安全套接字,大大减轻了开发者的负担,使开发者可以很轻松将SSL协议整合到程序中,并且JSSE能将安全隐患降到了最低点。

# 1. JSSE主要的接口类图:

7tlxw6.jpg (opens new window)

  1. 核心类——SSLSocket和SSLServerSocket。以子类的形式实现SSL协议的Socket,ServerSocket。主要负责:
  • 设置加密套件
  • 管理SSL会话
  • 处理握手结束时间
  • 设置客户端模式或服务器模式
  1. 工厂类——SSLSocketFactory和SSLServerSocketFactory,工厂模式用来创建Socker类。
  2. 会话——SSLSession。安全通信握手过程需要一个会话,为了提高通信的效率,SSL协议允许多个SSLSocket共享同一个SSL会话,在同一个会话中,只有第一个打开的SSLSocket需要进行SSL握手,负责生成密钥及交换密钥,其余SSLSocket都共享密钥信息。
  3. SL上下文——SSLContext。对整个SSL/TLS协议的封装,表示了安全套接字协议的实现。主要负责设置安全通信过程中的各种信息。并且负责构建SSLSocketFactory、SSLServerSocketFactory和SSLEngine等工厂类。
  4. SSL非阻塞引擎——SSLEngine。使用这个类让通信过程支持非阻塞的安全通信。
  5. 密钥管理器——KeyManager。此接口负责选择用于证实自己身份的安全证书,发给通信另一方。KeyManager对象由KeyManagerFactory工厂类生成。
  6. 信任管理器——TrustManager。此接口负责判断决定是否信任对方的安全证书,TrustManager对象由TrustManagerFactory工厂类生成。
  7. 密钥证书存储设施——KeyStore。这个对象用于存放安全证书,安全证书一般以文件形式存放,KeyStore负责将证书加载到内存。

在利用SSL/TLS进行安全通信时,客户端和服务器端都可能要设置用于证实自己身份的安全证书,并且还要设置信任对方的哪些安全证书。一般情况客户端要对服务器端的身份进行验证,但是无需向服务器证实自己的身份,这样不用向对方证实自己身份的通信端我们就说它处于客户模式,否则成它处于服务器模式。SSLSocket的setUseClientMode(Boolean mode)方法可以设置客户端模式或服务器模式。

# 2.证书管理

一个证书是一个实体的数字签名,主要包含实体信息,数字签名,公共钥匙。数字签名是实体信息用实体的私钥加密后的数据,可以用实体的公共钥匙解密。公共钥匙是实体的数字关联,让所有想同这个实体发生信任关系的其他实体用来检验签名。

SSL协议通信涉及密钥储存的文件格式比较多,例如cer、pfx、jks、keystore、truststore等格式文件

  • cer:俗称证书,这个证书中没有私钥,只包含了公钥;
  • pfx:也称为证书,一般供浏览器使用,不仅包含了公钥,还包含了私钥。私钥是加密的,不输入密码是解不了密的;
  • jks:java密钥存储器(javakey store),它可以同时容纳N个公钥跟私钥,是一个密钥库;
  • keystore:跟.jks基本是一样的,只是叫法不太一样,默认生成的证书存储库格式;
  • truststore:信任证书存储库,它仅仅包含了通信对方的公钥,当然你可以直接把通信对方的jks作为信任库

就算如此你也只能知道通信对方的公钥,私钥都是加密的

有些时候我们需要把pfx或cert转化为jks以便于用java进行ssl通信,例如一个银行只提供了pfx证书,而我们想用java进行ssl通信时就要将pfx转化为jks格式。

7tHzbF.jpg (opens new window)

# 例子

理论上,通信双方分别拥有一个keystore和一个truststore,keystore用于存放自己的密钥和公钥,truststore用于存放所有需要信任方的公钥。为了方便直接使用jks即keystore替代truststore(免去证书导来导去),因为对方的keystore包含了自己需要的信任公钥。

1.生成

keytool -genkey -alias aliasName -keypass keypassword -keyalg RSA -keysize 1024 -validity 365 -keystore e:\aliasName.keystore -storepass 123456
 
# keypass: 访问私钥的密码
# storepass: 访问密钥库的密码

2.导出(证书库导出到crt证书文件)

keytool -export -alias aliasName -keystore e:\aliasName.keystore -file e:\aliasName.crt -storepass 123456

3.导入(从证书文件导入到keystore或jks文件)

keytool -import -alias aliasName -file e:\aliasName.crt -keystore e:\aliasName.keystore -storepass 123456 

keytool -list  -v -keystore e:\aliasName.keystore -storepass 123456
# 服务端
public static void main(String[] args) throws Exception {
 
    //密钥管理器
    KeyStore serverKeyStore = KeyStore.getInstance("JKS");//证书库格式
    serverKeyStore.load(new FileInputStream("e:\\myserver.jks"), "123456".toCharArray());//加载密钥库

    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");//证书格式
    kmf.init(serverKeyStore, "123456".toCharArray());//加载密钥储存器

    //信任管理器
    KeyStore clientKeyStore = KeyStore.getInstance("JKS");
    clientKeyStore.load(new FileInputStream("e:\\myclient.jks"), "123456".toCharArray());

    TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
    tmf.init(clientKeyStore);

    //SSL上下文设置
    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    //SSLServerSocket
    SSLServerSocketFactory serverFactory = sslContext.getServerSocketFactory();
    SSLServerSocket svrSocket = (SSLServerSocket) serverFactory.createServerSocket(34567);
    //svrSocket.setNeedClientAuth(true);//客户端模式,服务端需要验证客户端身份

    String[] supported = svrSocket.getEnabledCipherSuites();//加密套件
    svrSocket.setEnabledCipherSuites(supported);

    //接收消息
    System.out.println("端口已打开,准备接受信息");
    SSLSocket cntSocket = (SSLSocket) svrSocket.accept();//开始接收
    InputStream in=cntSocket.getInputStream();//输入流
    int a=in.read(new byte[102]);//循环检查是否有消息到达
    System.out.println("来自于客户端:" + a);
}

服务器端有行代码svrSocket.setNeedClientAuth(true);用于设置是否验证客户端的身份。假如我们把它注释掉或设置为false,此时客户端将不再需要自己的密钥管理器,即服务器不需要通过client.jks对客户端的身份进行验证,把密钥管理器直接设置为null也可以跟服务器端进行通信。

# 客户端
public static void main(String[] args) throws Exception {
 
    //密钥管理器
    KeyStore clientKeyStore = KeyStore.getInstance("JKS");
    clientKeyStore.load(new FileInputStream("e:\\myclient.jks"), "123456".toCharArray());

    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    kmf.init(clientKeyStore, "123456".toCharArray());


    //信任管理器
    KeyStore serverKeyStore = KeyStore.getInstance("JKS");
    serverKeyStore.load(new FileInputStream("e:\\myserver.jks"), "123456".toCharArray());


    TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
    tmf.init(serverKeyStore);

    //SSL上下文
    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);


    SSLSocketFactory sslcntFactory =(SSLSocketFactory) sslContext.getSocketFactory();
    SSLSocket sslSocket= (SSLSocket) sslcntFactory.createSocket("127.0.0.1", 34567);

    String[] supported = sslSocket.getSupportedCipherSuites();
    sslSocket.setEnabledCipherSuites(supported);

    //发送
    OutputStream out=sslSocket.getOutputStream();
    out.write("hello".getBytes());

}

信任管理器

  1. 如果系统属性javax.net.ssl.truststore指定了truststore文件,那么信任管理器将去jre路径下的lib/security目录寻找这个文件作为信任存储器;
  2. 如果没设置系统属性,则去寻找一个%java_home%/lib/security/jssecacerts文件作为信任存储器;
  3. 如果jssecacerts不存在而cacerts存在,则cacerts作为信任存储器。

# 四、JAAS