利用mina框架進行ssl雙向認證連結
阿新 • • 發佈:2018-11-30
再上一篇中我已將建立好了證書,現在開始進行測試建立的證書有沒有用,先建立一個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認證,那麼就會報錯握手失敗也就是認證失敗了