证书的生成过程这里就不介绍了,请参照:Java网络编程二:Java Secure(SSL/TLS) Socket实现 中的证书部分
代码结构如下:
Maven配置文件:
- <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/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>JERSEY</groupId>
- <artifactId>JERSEY</artifactId>
- <version>1.0</version>
- <dependencies>
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-client</artifactId>
- <version>1.18</version>
- </dependency>
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-grizzly2</artifactId>
- <version>1.18</version>
- </dependency>
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-json</artifactId>
- <version>1.18</version>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.media</groupId>
- <artifactId>jersey-media-json-jackson</artifactId>
- <version>2.15</version>
- </dependency>
- </dependencies>
- </project>
Person类是基本的JAXB:
- package com.sean;
- import java.util.List;
- import javax.xml.bind.annotation.XmlRootElement;
- @XmlRootElement
- public class Person {
- private String name;
- private List<String> addresses;
- public Person(){}
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public List<String> getAddresses() {
- return addresses;
- }
- public void setAddresses(List<String> addresses) {
- this.addresses = addresses;
- }
- }
客户端代码:
- package com.sean;
- import java.net.URI;
- import javax.net.ssl.SSLContext;
- import javax.ws.rs.core.MediaType;
- import javax.ws.rs.core.UriBuilder;
- import org.glassfish.jersey.SslConfigurator;
- import com.sun.jersey.api.client.Client;
- import com.sun.jersey.api.client.ClientResponse;
- import com.sun.jersey.api.client.WebResource;
- import com.sun.jersey.api.client.config.ClientConfig;
- import com.sun.jersey.api.client.config.DefaultClientConfig;
- import com.sun.jersey.client.urlconnection.HTTPSProperties;
- public class SSLClient {
- public static void main(String[] args) {
- int authType =
- Integer.valueOf(Config.getConfig().getProperty("authority")).intValue();
- SslConfigurator sslConfig = SslConfigurator.newInstance();
- if(authType == 1){
- sslConfig.trustStoreFile(Config.getConfig().getProperty("clientTrustCer"))
- .trustStorePassword(Config.getConfig().getProperty("clientTrustCerPwd"));
- }else if(authType == 2){
- sslConfig.keyStoreFile(Config.getConfig().getProperty("clientCer"))
- .keyStorePassword(Config.getConfig().getProperty("clientCerPwd"))
- .keyPassword(Config.getConfig().getProperty("clientKeyPwd"))
- .trustStoreFile(Config.getConfig().getProperty("clientTrustCer"))
- .trustStorePassword(Config.getConfig().getProperty("clientTrustCerPwd"));
- }
- sslConfig.securityProtocol(Config.getConfig().getProperty("protocol"));
- SSLContext sslContext = sslConfig.createSSLContext();
- ClientConfig cc = new DefaultClientConfig();
- cc.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
- new HTTPSProperties(new MyHostnameVerifier(), sslContext));
- Client client = Client.create(cc);
- URI uri = UriBuilder.fromUri("https://127.0.0.1/queryAddress").port(10000).build();
- WebResource resource = client.resource(uri);
- Person person = new Person();
- person.setName("sean");
- ClientResponse response = resource
- .accept(MediaType.APPLICATION_XML)
- .type(MediaType.APPLICATION_XML)
- .post(ClientResponse.class, person);
- String addresses = response.getEntity(String.class);
- System.out.println(addresses);
- }
- }
SSL握手过程中,会对请求IP或请求域名进行校验,如果在证书信息中无法找到相关请求IP或请求域名则会报错(javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present)
这里实现自己的校验逻辑(如果请求的IP为127.0.0.1或请求的域名为localhost,则直接通过校验)以覆盖默认逻辑
- package com.sean;
- import javax.net.ssl.HostnameVerifier;
- import javax.net.ssl.SSLSession;
- public class MyHostnameVerifier implements HostnameVerifier {
- @Override
- public boolean verify(String hostname, SSLSession session) {
- if("127.0.0.1".equals(hostname) || "localhost".equals(hostname) )
- return true;
- else
- return false;
- }
- }
服务端代码:
- package com.sean;
- import java.io.IOException;
- import java.net.URI;
- import java.util.ArrayList;
- import java.util.List;
- import javax.net.ssl.SSLContext;
- import javax.ws.rs.Consumes;
- import javax.ws.rs.POST;
- import javax.ws.rs.Path;
- import javax.ws.rs.Produces;
- import javax.ws.rs.core.MediaType;
- import javax.ws.rs.core.UriBuilder;
- import org.glassfish.grizzly.http.server.HttpHandler;
- import org.glassfish.grizzly.http.server.HttpServer;
- import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
- import org.glassfish.jersey.SslConfigurator;
- import com.sun.jersey.api.container.ContainerFactory;
- import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory;
- import com.sun.jersey.api.core.PackagesResourceConfig;
- import com.sun.jersey.api.core.ResourceConfig;
- @Path("queryAddress")
- public class SSLServer {
- @POST
- @Consumes(MediaType.APPLICATION_XML)
- @Produces(MediaType.APPLICATION_XML)
- public Person queryAddress(String name) {
- System.out.println(name);
- Person person = new Person();
- List<String> addresses = new ArrayList<String>();
- addresses.add("address1");
- addresses.add("address2");
- person.setAddresses(addresses);
- return person;
- }
- public static void main(String[] args) {
- Integer authType =
- Integer.valueOf(Config.getConfig().getProperty("authority")).intValue();
- SslConfigurator sslConfig = SslConfigurator.newInstance();
- if(authType == 1){
- sslConfig.keyStoreFile(Config.getConfig().getProperty("serverCer"))
- .keyStorePassword(Config.getConfig().getProperty("serverCerPwd"))
- .keyPassword(Config.getConfig().getProperty("serverKeyPwd"));
- }else if(authType == 2){
- sslConfig.keyStoreFile(Config.getConfig().getProperty("serverCer"))
- .keyStorePassword(Config.getConfig().getProperty("serverCerPwd"))
- .keyPassword(Config.getConfig().getProperty("serverKeyPwd"))
- .trustStoreFile(Config.getConfig().getProperty("serverTrustCer"))
- .trustStorePassword(Config.getConfig().getProperty("serverTrustCerPwd"));
- }
- sslConfig.securityProtocol(Config.getConfig().getProperty("protocol"));
- SSLContext sslContext = sslConfig.createSSLContext();
- SSLEngineConfigurator sslEngineConfig = new SSLEngineConfigurator(sslContext);
- //默认情况下是客户端模式,如果忘记修改模式
- //会抛出异常
- //javax.net.ssl.SSLProtocolException: Handshake message sequence violation, 1]
- sslEngineConfig.setClientMode(false);
- if(authType == 1)
- sslEngineConfig.setWantClientAuth(true);
- else if(authType == 2)
- sslEngineConfig.setNeedClientAuth(true);
- ResourceConfig rc = new PackagesResourceConfig("com.sean");
- HttpHandler handler = ContainerFactory.createContainer(
- HttpHandler.class, rc);
- URI uri = UriBuilder.fromUri("https://127.0.0.1/").port(10000).build();
- try {
- HttpServer server = GrizzlyServerFactory.createHttpServer(uri, handler, true,
- sslEngineConfig);
- server.start();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (NullPointerException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- Thread.sleep(1000*1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
配置文件类:
- package com.sean;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- import java.util.Properties;
- public class Config{
- private static Properties config;
- public static Properties getConfig(){
- try{
- if(null == config){
- File configFile =
- new File("src/main/resources/config/config.properties");
- if(configFile.exists() && configFile.isFile()
- && configFile.canRead()){
- InputStream input = new FileInputStream(configFile);
- config = new Properties();
- config.load(input);
- }
- }
- }catch(Exception e){
- //default set
- config = new Properties();
- config.setProperty("authority", String.valueOf(1));
- config.setProperty("protocol", "SSL");
- config.setProperty("serverCer", "src/main/resources/certificate/server.jks");
- config.setProperty("serverCerPwd", "1234sp");
- config.setProperty("serverKeyPwd", "1234kp");
- config.setProperty("serverTrustCer", "src/main/resources/certificate/serverTrust.jks");
- config.setProperty("serverTrustCerPwd", "1234sp");
- config.setProperty("clientCer", "src/main/resources/certificate/client.jks");
- config.setProperty("clientCerPwd", "1234sp");
- config.setProperty("clientKeyPwd", "1234kp");
- config.setProperty("clientTrustCer", "src/main/resources/certificate/clientTrust.jks");
- config.setProperty("clientTrustCerPwd", "1234sp");
- }
- return config;
- }
- }
配置文件config.properties:
- #1:单向认证,只有服务器端需证明其身份
- #2:双向认证,服务器端和客户端都需证明其身份
- authority=2
- #通信协议
- protocol=SSL
- #服务端证书信息
- serverCer=src/main/resources/certificate/server.jks
- #keystore的storepass
- serverCerPwd=1234sp
- #keystore的keypass
- serverKeyPwd=1234kp
- #服务端证书信息
- serverTrustCer=src/main/resources/certificate/serverTrust.jks
- serverTrustCerPwd=1234sp
- #客户端证书信息
- clientCer=src/main/resources/certificate/client.jks
- clientCerPwd=1234sp
- clientKeyPwd=1234kp
- clientTrustCer=src/main/resources/certificate/clientTrust.jks
- clientTrustCerPwd=1234sp
服务端运行结果:
- 三月 03, 2015 3:30:54 下午 com.sun.jersey.api.core.PackagesResourceConfig init
- INFO: Scanning for root resource and provider classes in the packages:
- com.sean
- 三月 03, 2015 3:30:54 下午 com.sun.jersey.api.core.ScanningResourceConfig logClasses
- INFO: Root resource classes found:
- class com.sean.SSLServer
- 三月 03, 2015 3:30:54 下午 com.sun.jersey.api.core.ScanningResourceConfig init
- INFO: No provider classes found.
- 三月 03, 2015 3:30:54 下午 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
- INFO: Initiating Jersey application, version 'Jersey: 1.18 11/22/2013 01:21 AM'
- 三月 03, 2015 3:30:55 下午 org.glassfish.grizzly.http.server.NetworkListener start
- INFO: Started listener bound to [127.0.0.1:10000]
- 三月 03, 2015 3:30:55 下午 org.glassfish.grizzly.http.server.HttpServer start
- INFO: [HttpServer] Started.
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?><person><name>sean</name></person>
客户端运行结果
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?><person><addresses>address1</addresses><addresses>address2</addresses></person>
-
顶
- 1
-
踩