java 请求双向认证的API
服务端配置
- 生成证书
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
openssl genrsa -out server.pem 1024
openssl rsa -in server.pem -out server.key
openssl req -new -key server.pem -out server.csr
openssl x509 -req -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out server.crt
openssl genrsa -out client.pem 1024
openssl rsa -in client.pem -out client.key
openssl req -new -key client.pem -out client.csr
openssl x509 -req -sha256 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out client.crt
- 生成java需要的jks格式证书
openssl pkcs12 -export -clcerts -in client.crt -inkey client.pem -out client.p12
openssl pkcs12 -export -clcerts -in server.crt -inkey server.pem -out server.p12
- nginx配置
server {
server_name blog.yubangweb.com;
listen 443 ssl;
index index.html index.htm;
# 服务端证书
ssl on;
ssl_certificate /var/ssl/server.crt;
ssl_certificate_key /var/ssl/server.key;
ssl_client_certificate /var/ssl/client.crt;
ssl_verify_client on;
root /var/web/blog;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8080;
}
}
客户端代码(复制的)
package demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpHost;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
/**
* #1
* HTTPS 双向认证 - direct into cacerts
*
* @Author Ye_Wenda
* @Date 7/11/2017
*/
public class DemoHttpClient {
public static CloseableHttpClient httpclient;
public static final String KEY_STORE_TRUST_PATH = "C:\\project\\server.p12"; // truststore的路径
public static final String KEY_STORE_TYPE_JKS = "PKCS12"; // truststore的类型
private static final String KEY_STORE_TRUST_PASSWORD = "123456"; // truststore的密码
public static final String KEY_STORE_CLIENT_PATH = "C:\\project\\client.p12";
public static final String KEY_STORE_TYPE_P12 = "PKCS12";
private static final String KEY_STORE_PASSWORD = "123456";
// 获得池化得HttpClient
static {
SSLContext sslcontext = null;
try {
// 设置truststore
KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_JKS);
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
InputStream ksIn = new FileInputStream(KEY_STORE_CLIENT_PATH);
InputStream tsIn = new FileInputStream(new File(KEY_STORE_TRUST_PATH));
try {
keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());
trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());
} finally {
try {
ksIn.close();
tsIn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).loadKeyMaterial(keyStore, KEY_STORE_PASSWORD.toCharArray()).build();
} catch (Exception e) {
e.printStackTrace();
}
// 客户端支持TLSV1,TLSV2,TLSV3这三个版本
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,
new String[]{"TLSv1", "TLSv2", "TLSv3"}, null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());// 客户端验证服务器身份的策略
// Create a registry of custom connection socket factories for supported
// protocol schemes.
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(sslcontext)).build();
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
// Configure total max or per route limits for persistent connections
// that can be kept in the pool or leased by the connection manager.
connManager.setMaxTotal(100);
connManager.setDefaultMaxPerRoute(10);
httpclient = HttpClients.custom().setConnectionManager(connManager).build();
}
/**
* 单向验证且服务端的证书可信
*
* @throws IOException
* @throws ClientProtocolException
*/
public void get(String url) throws ClientProtocolException, IOException {
// Execution context can be customized locally.
HttpClientContext context = HttpClientContext.create();
HttpGet httpget = new HttpGet(url);
// 设置请求的配置
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000)
.setConnectionRequestTimeout(5000).build();
httpget.setConfig(requestConfig);
System.out.println("executing request " + httpget.getURI());
CloseableHttpResponse response = httpclient.execute(httpget, context);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
System.out.println("----------------------------------------");
// Once the request has been executed the local context can
// be used to examine updated state and various objects affected
// by the request execution.
// Last executed request
context.getRequest();
// Execution route
context.getHttpRoute();
// Target auth state
context.getTargetAuthState();
// Proxy auth state
context.getTargetAuthState();
// Cookie origin
context.getCookieOrigin();
// Cookie spec used
context.getCookieSpec();
// User security token
context.getUserToken();
} catch (Exception e) {
e.printStackTrace();
}
}
}