最近公司移动端API由http升级到https,Nginx作为前端负载均衡,Tomcat作为后端应用服务器。Tomcat不需要开启https,Nginx 需要开启https支持,在 Nginx 中的配置如下:
upstream mobile_https_proxy { server 192.168.37.118:8081 weight=4 max_fails=2 fail_timeout=30s; #192.168.37.118:8081 是后端 Tomcat 的地址和端口}server { listen 443; server_name www.test.51offer.com; root html; index index.html index.htm; ssl on; ssl_certificate /usr/local/openssl/server-cert.cer; #服务器端证书 ssl_certificate_key /usr/local/openssl/server_nopwd.key; #服务器无密码私钥 ssl_client_certificate /usr/local/openssl/root-cert.cer;#CA证书 ssl_session_timeout 5m; ssl_verify_client on; #开户客户端证书验证 ssl_protocols SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; ssl_prefer_server_ciphers on; location ~* ^/mobile/ { proxy_pass http://mobile_https_proxy; }}
配置好后,执行如下命令使配置生效
/usr/local/nginx/sbin/nginx -s reload
在上面的配置中有三个证书
/usr/local/openssl/server-cert.cer; #服务器端证书/usr/local/openssl/server_nopwd.key; #服务器无密码私钥/usr/local/openssl/root-cert.cer;#CA证书
可以使用下面这个Shell脚本生成
#!/bin/shcerts=/usr/local/apps/certskeytool=/usr/java/jdk1.7.0_45/binshell=/usr/local/apps/shellsecho 准备工作...cd /usr/local/apps/echo 清理...rm -rf certsmkdir certscd certsecho 创建辅助目录...mkdir camkdir servermkdir clientecho 创建辅助文件...echo 0 > index.txtecho 01 > serialopenssl rand -out .rand 1000echo 创建根证私钥openssl genrsa -des3 -passout pass:123456 -out $certs/ca/root-key.key 2048echo 创建根证书请求文件openssl req -passin pass:123456 -new -key $certs/ca/root-key.key -out $certs/ca/root-req.csr -subj "/C=CN/ST=SH/L=SH/O=51offer/OU=51offer/CN=*.51offer.com"echo 自签根证书openssl x509 -passin pass:123456 -req -days 100000 -sha1 -signkey $certs/ca/root-key.key -in $certs/ca/root-req.csr -out $certs/ca/root-cert.cerecho 导出p12格式根证书openssl pkcs12 -export -clcerts -in $certs/ca/root-cert.cer -passin pass:123456 -inkey $certs/ca/root-key.key -passout pass:123456 -out $certs/ca/root.p12echo 生成root.jks文件cd $keytoolecho "y" | ./keytool -import -trustcacerts -storepass 123456 -alias root -file $certs/ca/root-cert.cer -keystore $certs/ca/root.jksecho 生成客户端keycd $certsopenssl genrsa -des3 -passout pass:123456 -out $certs/client/client-key.key 2048echo 生成客户端请求文件openssl req -passin pass:123456 -new -key $certs/client/client-key.key -out $certs/client/client-req.csr -subj "/C=CN/ST=SH/L=SH/O=51offer/OU=51offer/CN=www.test.51offer.com"echo 生成客户端证书(root证书,rootkey,客户端key,客户端请求文件这4个生成客户端证书)openssl x509 -passin pass:123456 -req -days 100000 -sha1 -CA $certs/ca/root-cert.cer -CAkey $certs/ca/root-key.key -CAserial ca.srl -CAcreateserial -in $certs/client/client-req.csr -out $certs/client/client-cert.cerecho 生成客户端p12格式根证书openssl pkcs12 -export -clcerts -in $certs/client/client-cert.cer -passin pass:123456 -inkey $certs/client/client-key.key -passout pass:123456 -out $certs/client/client.p12echo 生成客户端JKScd $keytoolecho "y" | ./keytool -import -v -trustcacerts -storepass 123456 -alias client -file $certs/client/client-cert.cer -keystore $certs/client/client.jksecho 生成客户端BKSecho "y" | ./keytool -import -v -trustcacerts -storepass 123456 -alias client -file $certs/client/client-cert.cer -keystore $certs/client/client.bks -deststoretype bks -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath $shell/bcprov-jdk15on-154.jarecho 生成服务端keycd $certsopenssl genrsa -des3 -passout pass:123456 -out $certs/server/server-key.key 2048echo 生成服务端请求文件openssl req -passin pass:123456 -new -key $certs/server/server-key.key -out $certs/server/server-req.csr -subj "/C=CN/ST=SH/L=SH/O=51offer/OU=51offer/CN=www.test.51offer.com"echo 生成服务端证书(root证书,rootkey,客户端key,客户端请求文件这4个生成客户端证书)openssl x509 -passin pass:123456 -req -days 100000 -sha1 -CA $certs/ca/root-cert.cer -CAkey $certs/ca/root-key.key -CAserial ca.srl -CAcreateserial -in $certs/server/server-req.csr -out $certs/server/server-cert.cerecho 生成服务端p12格式根证书openssl pkcs12 -export -clcerts -in $certs/server/server-cert.cer -passin pass:123456 -inkey $certs/server/server-key.key -passout pass:123456 -out $certs/server/server.p12echo 生成服务端无密码keyopenssl rsa -passin pass:123456 -in $certs/server/server-key.key -out $certs/server/server_nopwd.keyecho 生成服务端JKS,在服务器端开启双向验证后,客户端需要这个证书才能访问服务器端资源cd $keytoolecho "y" | ./keytool -import -v -trustcacerts -storepass 123456 -alias server -file $certs/server/server-cert.cer -keystore $certs/server/server.jksecho 生成服务端BKS,在服务器端开启双向验证后,Android客户端需要这个证书才能访问服务器端资源echo "y" | ./keytool -import -v -trustcacerts -storepass 123456 -alias server -file $certs/server/server-cert.cer -keystore $certs/server/server.bks -deststoretype bks -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath $shell/bcprov-jdk15on-154.jarecho success
其中 bcprov-jdk15on-154.jar
用于生成Android端使用的BKS证书,该文件可以下载。
使用HttpClient访问Nginx启用了https双向验证的代码如下:
package com.toulezu.httpsclient;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.security.KeyStore;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.conn.scheme.Scheme;import org.apache.http.conn.ssl.SSLSocketFactory;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;/** * 通过 HttpClient 访问 https 网站例子 * * @author toulezu * */public class HttpsClientJKS { private static final String KEY_STORE_TYPE_JKS = "jks"; private static final String KEY_STORE_TYPE_P12 = "PKCS12"; private static final String SCHEME_HTTPS = "https"; private static final int HTTPS_PORT = 443; private static final String HTTPS_URL = "https://www.test.51offer.com/mobile/school/countries"; private static final String KEY_STORE_CLIENT_PATH = "test-client.p12"; private static final String KEY_STORE_TRUST_PATH = "test-server.jks"; private static final String KEY_STORE_PASSWORD = "123456"; private static final String KEY_STORE_TRUST_PASSWORD = "123456"; public static void main(String[] args) throws Exception { ssl(); } private static void ssl() throws Exception { HttpClient httpClient = new DefaultHttpClient(); try { KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12); KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_JKS); InputStream ksIn = HttpsClientTrustStore.class.getClassLoader().getResourceAsStream(KEY_STORE_CLIENT_PATH); InputStream tsIn = HttpsClientTrustStore.class.getClassLoader().getResourceAsStream(KEY_STORE_TRUST_PATH); try { keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray()); trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray()); } finally { try { ksIn.close(); } catch (Exception ignore) { ignore.printStackTrace(); } try { tsIn.close(); } catch (Exception ignore) { ignore.printStackTrace(); } } SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, KEY_STORE_PASSWORD, trustStore); Scheme sch = new Scheme(SCHEME_HTTPS, HTTPS_PORT, socketFactory); httpClient.getConnectionManager().getSchemeRegistry().register(sch); HttpGet httpget = new HttpGet(HTTPS_URL); System.out.println("executing request" + httpget.getRequestLine()); HttpResponse response = httpClient.execute(httpget); HttpEntity entity = response.getEntity(); System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); if (entity != null) { System.out.println("Response content length: " + entity.getContentLength()); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent())); String text; while ((text = bufferedReader.readLine()) != null) { System.out.println(text); } bufferedReader.close(); } EntityUtils.consume(entity); } finally { httpClient.getConnectionManager().shutdown(); } }}
相关参考如下: