利用mina框架进行ssl双向认证链接
程序员文章站
2022-05-01 09:45:55
...
再上一篇中我已将创建好了证书,现在开始进行测试创建的证书有没有用,先创建一个maven项目目录结构如下
我们这里是进行的双向测试,如果想进行单项测试,也就是服务器端不验证客户端的身份(关于ssl的原理可以自行百度了解)等会再说,改一个配置就行.
先引入pom文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>minaDemo</groupId>
<artifactId>minaDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 设定除*仓库(repo1.maven.org/maven2/)外的其他仓库,按设定顺序进行查找. -->
<repositories>
<repository>
<id>offical</id>
<name>Maven Repository Switchboard</name>
<url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jboss</id>
<name>jboss repository</name>
<url>http://repository.jboss.com/maven2/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>openqa-releases</id>
<name>Openqa Release Repository</name>
<url>http://nexus.openqa.org/content/repositories/releases</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.0.0-RC1</version>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-integration-beans</artifactId>
<version>2.0.0-RC1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
</project>
1 开始搞服务端的配置
server端通过spring配置进行启动 所以先看配置吧 serverContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.net.SocketAddress">
<bean class="org.apache.mina.integration.beans.InetSocketAddressEditor" />
</entry>
</map>
</property>
</bean>
<!-- The IoHandler implementation -->
<bean id="serverHandler" class="com.sundoctor.mina.example3.ssl.server.TLSServerHandler" />
<!-- The SSL configuration -->
<bean id="keystoreFactory" class="org.apache.mina.filter.ssl.KeyStoreFactory">
<property name="password" value="123456"/>
<property name="dataUrl" value="classpath:com/sundoctor/mina/example3/ssl/server.jks"/>
</bean>
<bean id="keyStore" factory-bean="keystoreFactory" factory-method="newInstance"/>
<bean id="truststoreFactory" class="org.apache.mina.filter.ssl.KeyStoreFactory">
<property name="password" value="123456"/>
<property name="dataUrl" value="classpath:com/sundoctor/mina/example3/ssl/server.jks"/>
</bean>
<bean id="trustStore" factory-bean="truststoreFactory" factory-method="newInstance"/>
<!-- SSLContext to be used -->
<bean id="sslContextFactory" class="org.apache.mina.filter.ssl.SslContextFactory">
<property name="protocol" value="TLS"/>
<property name="keyManagerFactoryAlgorithm" value="SunX509"/>
<property name="keyManagerFactoryKeyStore"><ref local="keyStore"/></property>
<property name="keyManagerFactoryKeyStorePassword" value="123456"/>
<property name="trustManagerFactoryAlgorithm" value="SunX509"/>
<property name="trustManagerFactoryKeyStore"><ref local="trustStore"/></property>
</bean>
<bean id="sslContext" factory-bean="sslContextFactory" factory-method="newInstance"/>
<!-- the IoFilters -->
<bean id="executorFilter" class="org.apache.mina.filter.executor.ExecutorFilter"/>
<bean id="mdcInjectionFilter" class="org.apache.mina.filter.logging.MdcInjectionFilter">
<constructor-arg value="remoteAddress"/>
</bean>
<bean id="codecFilter" class="org.apache.mina.filter.codec.ProtocolCodecFilter">
<constructor-arg>
<bean class="org.apache.mina.filter.codec.textline.TextLineCodecFactory" />
</constructor-arg>
</bean>
<bean id="loggingFilter" class="org.apache.mina.filter.logging.LoggingFilter" />
<bean id="sslFilter" class="org.apache.mina.filter.ssl.SslFilter">
<constructor-arg ref="sslContext"/>
<!--默认是client mode,必须在握手开始之前调用 设置为false代表是服务端 true代表是客户端 -->
<property name="useClientMode" value="false"/>
<!--如果客户端不提供其证书,通信将会结束双向 此时设置为true则代表双向认证 -->
<property name="needClientAuth" value="true"/>
<!-- 此项设置为true 代表 此项代表认证 -->
<property name="wantClientAuth" value="false"/>
</bean>
<!-- The SSL filter chain. -->
<bean id="sslFilterChainBuilder" class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder">
<property name="filters">
<map>
<entry key="executor" value-ref="executorFilter"/>
<entry key="mdcInjectionFilter" value-ref="mdcInjectionFilter"/>
<entry key="sslFilter" value-ref="sslFilter"/>
<entry key="codecFilter" value-ref="codecFilter"/>
<entry key="loggingFilter" value-ref="loggingFilter"/>
</map>
</property>
</bean>
<!-- The SSL enabled IoAcceptor which binds to port 1235 -->
<bean id="ioAcceptorWithSSL" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" init-method="bind" destroy-method="unbind">
<property name="defaultLocalAddress" value="127.0.0.1:50003" />
<property name="handler" ref="serverHandler" />
<property name="reuseAddress" value="true" />
<property name="filterChainBuilder" ref="sslFilterChainBuilder" />
</bean>
</beans>
服务端启动代码:TLSServerSpring.java:
package com.sundoctor.mina.example3.ssl.server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
public class TLSServerSpring {
private static final Logger logger = LoggerFactory.getLogger(TLSServerSpring.class);
public static void main(String[] args) throws Exception {
getApplicationContext();
logger.debug("Listening ...");
}
public static ConfigurableApplicationContext getApplicationContext() {
return new ClassPathXmlApplicationContext("com/sundoctor/mina/example3/ssl/server/serverContext.xml");
}
}
然后就是服务端的具体业务处理类:TLSServerHandler.java 此类继承了IoHandlerAdapter 具体的业务逻辑写在这里面:
package com.sundoctor.mina.example3.ssl.server;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TLSServerHandler extends IoHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(TLSServerHandler.class);
public void sessionCreated(IoSession session) throws Exception {
logger.debug("[NIO Server]>> sessionCreated");
}
public void sessionOpened(IoSession session) throws Exception {
logger.debug("[NIO Server]>> sessionOpened");
System.out.println("incoming client:"+session.getRemoteAddress());
}
public void sessionClosed(IoSession session) throws Exception {
logger.debug("[NIO Server]>> sessionClosed");
}
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
logger.debug("[NIO Server]>> sessionIdle");
}
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
logger.debug("[NIO Server]>> exceptionCaught :");
cause.printStackTrace();
}
public void messageReceived(IoSession session, Object message) throws Exception {
logger.debug("[NIO Server]>> messageReceived");
logger.debug("[NIO Server Received]>> : {}", (String) message);
session.write("安全链接已建立!证书验证通过,已经收到消息,over!");
}
public void messageSent(IoSession session, Object message) throws Exception {
logger.debug("[NIO Server]>> messageSent");
logger.debug("[NIO Server messageSent]>> : {}", (String) message);
}
}
2 开始配置客户端:
clientContext.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="clientHandler" class="com.sundoctor.mina.example3.ssl.client.TLSClientHandler" />
<!-- The SSL configuration -->
<bean id="keystoreFactory" class="org.apache.mina.filter.ssl.KeyStoreFactory">
<property name="password" value="123456"/>
<property name="dataUrl" value="classpath:com/sundoctor/mina/example3/ssl/client.jks"/>
</bean>
<bean id="keyStore" factory-bean="keystoreFactory" factory-method="newInstance"/>
<bean id="truststoreFactory" class="org.apache.mina.filter.ssl.KeyStoreFactory">
<property name="password" value="123456"/>
<property name="dataUrl" value="classpath:com/sundoctor/mina/example3/ssl/client.jks"/>
</bean>
<bean id="trustStore" factory-bean="truststoreFactory" factory-method="newInstance"/>
<!-- SSLContext to be used -->
<bean id="sslContextFactory" class="org.apache.mina.filter.ssl.SslContextFactory">
<property name="protocol" value="TLS"/>
<property name="keyManagerFactoryAlgorithm" value="SunX509"/>
<property name="keyManagerFactoryKeyStore"><ref local="keyStore"/></property>
<property name="keyManagerFactoryKeyStorePassword" value="123456"/>
<property name="trustManagerFactoryAlgorithm" value="SunX509"/>
<property name="trustManagerFactoryKeyStore"><ref local="trustStore"/></property>
</bean>
<bean id="sslContext" factory-bean="sslContextFactory" factory-method="newInstance"/>
<!-- the IoFilters -->
<bean id="executorFilter" class="org.apache.mina.filter.executor.ExecutorFilter">
<constructor-arg>
<bean class="java.util.concurrent.Executors" factory-method="newCachedThreadPool"/>
</constructor-arg>
</bean>
<bean id="mdcInjectionFilter" class="org.apache.mina.filter.logging.MdcInjectionFilter">
<constructor-arg value="remoteAddress"/>
</bean>
<bean id="codecFilter" class="org.apache.mina.filter.codec.ProtocolCodecFilter">
<constructor-arg>
<bean class="org.apache.mina.filter.codec.textline.TextLineCodecFactory" />
</constructor-arg>
</bean>
<bean id="loggingFilter" class="org.apache.mina.filter.logging.LoggingFilter" />
<bean id="sslFilter" class="org.apache.mina.filter.ssl.SslFilter">
<constructor-arg ref="sslContext"/>
<-- 此项必须设置为true代表的客户端 -->
<property name="useClientMode" value="true"/>
</bean>
<!-- The SSL filter chain. -->
<bean id="sslFilterChainBuilder" class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder">
<property name="filters">
<map>
<entry key="executor" value-ref="executorFilter"/>
<entry key="mdcInjectionFilter" value-ref="mdcInjectionFilter"/>
<entry key="sslFilter" value-ref="sslFilter"/>
<entry key="codecFilter" value-ref="codecFilter"/>
<entry key="loggingFilter" value-ref="loggingFilter"/>
</map>
</property>
</bean>
<bean id="ioConnectorWithSSL" class="org.apache.mina.transport.socket.nio.NioSocketConnector" scope="prototype" >
<property name="handler" ref="clientHandler" />
<property name="filterChainBuilder" ref="sslFilterChainBuilder" />
</bean>
</beans>
客户端业务处理类TLSClientHandler.java:
package com.sundoctor.mina.example3.ssl.client;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TLSClientHandler extends IoHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(TLSClientHandler.class);
public void sessionCreated(IoSession session) throws Exception {
logger.debug("[NIO Client]>> sessionCreated");
}
public void sessionOpened(IoSession session) throws Exception {
logger.debug("[NIO Client]>> sessionOpened");
}
public void sessionClosed(IoSession session) throws Exception {
logger.debug("[NIO Client]>> sessionClosed");
}
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
logger.debug("[NIO Client]>> sessionIdle");
}
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
logger.debug("[NIO Client]>> exceptionCaught :");
cause.printStackTrace();
}
public void messageReceived(IoSession session, Object message) throws Exception {
logger.debug("[NIO Client]>> messageReceived");
logger.debug("[NIO Client Received]>>{}", (String) message);
}
public void messageSent(IoSession session, Object message) throws Exception {
logger.debug("[NIO Client]>> messageSent");
logger.debug("[NIO Client messageSent]>> : {}", (String) message);
}
}
然后就是客户端的启动代码TSLClientSpring.java:
package com.sundoctor.mina.example3.ssl.client;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TSLClientSpring {
public static void main(String[] args) throws Exception {
ApplicationContext context = getApplicationContext();
NioSocketConnector ioConnectorWithSSL = (NioSocketConnector) context.getBean("ioConnectorWithSSL");
// 创建连接
ConnectFuture future = ioConnectorWithSSL.connect(new InetSocketAddress("192.168.2.125", 6008));
// 等待连接创建完成
future.awaitUninterruptibly();
// 获取连接会话
IoSession session = future.getSession();
/*ioConnectorWithSSL.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("GBK"))));*/
// 发送信息
session.write("qqqq我是安全的吗?");
// 等待连接断开
session.getCloseFuture().awaitUninterruptibly();
ioConnectorWithSSL.dispose();
}
public static ConfigurableApplicationContext getApplicationContext() {
return new ClassPathXmlApplicationContext("com/sundoctor/mina/example3/ssl/client/clientContext.xml");
}
}
注意看第一张我的截图,我的server.jks与client.jks放置的位置,可以看配置里面,配的有路径,这时候启动服务端,会出现这样的画面:
然后启动客户端去链接:
此时看到客户端发出去了一段话,这段话是我在客户端的启动类中写上去的,然后收到了一段话,这段话是服务端发来的.
我们可以看服务端是不是发了,点击切换按钮,就是右上那个我画圈的地方:
对吧,服务端发的,客户端收到了,客户端发的 服务端也受到了,那就是认证成功了呗,如果你把这两份jks换成不同的ca认证,那么就会报错握手失败也就是认证失败了
推荐阅读