https的服務(wù)器配置見:http://www.reibang.com/p/860a297e1323
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ctjsoft.ticket.https.example.util.CertificationUtil;
/**
* 證書認(rèn)證管理,實(shí)現(xiàn)X509TrustManager接口
*/
public class MyX509TrustManager implements X509TrustManager{
private static final LoggerLOGGER = LoggerFactory.getLogger(MyX509TrustManager.class);
? private static final X509Certificate[]EMPTY_X509CERTIFICATE_ARRAY =new X509Certificate[]{};
? private X509Certificate[]rootCerts;
? public MyX509TrustManager(X509Certificate rootCertificate) {
if(rootCertificate ==null) {
throw new IllegalArgumentException("root certificate is null ");
? ? ? }
LOGGER.info("init root certificate");
? ? ? this.rootCerts =new X509Certificate[]{rootCertificate};
? }
public MyX509TrustManager(X509Certificate[] rootCerts) {
if(rootCerts ==null || rootCerts.length ==0) {
throw new IllegalArgumentException("root certificate is null ");
? ? ? }
LOGGER.info("init root certificates ,sizei:{}",rootCerts.length);
? ? ? this.rootCerts = rootCerts;
? }
@Override
? public void checkClientTrusted(X509Certificate[] arg0, String arg1)throws CertificateException {
System.out.println("checkClientTrusted:"+arg1);
? }
@Override
? ? public void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException {
if (chain ==null) {
throw new CertificateException("checkServerTrusted: X509Certificate array is null");
? ? ? ? }
if (chain.length <1) {
throw new CertificateException("checkServerTrusted: X509Certificate is empty");
? ? ? ? }
if (!(null != authType && authType.equals("ECDHE_RSA"))) {
throw new CertificateException("checkServerTrusted: AuthType is not ECDHE_RSA");
? ? ? ? }
? ? ? //驗(yàn)證證書
? ?????boolean isTrusted = CertificationUtil.verifyCertChain(chain,rootCerts);
? ? ? if(!isTrusted) {
throw new CertificateException("server's Cert verify fail");
? ? ? }
}
@Override
? public X509Certificate[]getAcceptedIssuers() {
return EMPTY_X509CERTIFICATE_ARRAY;
? }
}
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CertificationUtil {
private static final LoggerLOGGER = LoggerFactory.getLogger(CertificationUtil.class);
? /**
* 加載證書
? ? * @param inputStream
? ? * @return
? ? */
? public static X509CertificateloadCertificate(String certPath) {
try? {
Path path = Paths.get("src/main/resources", certPath);
? ? ? ? ? ? InputStream inputStream =new FileInputStream(path.toFile());
? ? ? ? ? CertificateFactory cf = CertificateFactory.getInstance("X509");
? ? ? ? ? ? ? X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
? ? ? ? ? ? ? cert.checkValidity();
? ? ? ? ? return cert;
? ? ? ? }catch (CertificateExpiredException e) {
throw new RuntimeException("證書已過期", e);
? ? ? }catch (CertificateNotYetValidException e) {
throw new RuntimeException("證書尚未生效", e);
? ? ? }catch (CertificateException e) {
throw new RuntimeException("無效的證書", e);
? ? ? }catch (Exception e) {
String errorMessage = e.getMessage() ==null ?"" : e.getMessage() +"鸟废。";
? ? ? ? ? ? if (certPath.startsWith("/")) {
errorMessage +="ClassPath路徑不可以/開頭盒延,請(qǐng)去除后重試。";
? ? ? ? ? ? }
throw new RuntimeException("讀取[" + certPath +"]失敗胯盯。" + errorMessage, e);
? ? ? ? }
}
/**
* 驗(yàn)證證書鏈?zhǔn)欠袷切湃巫C書庫中證書簽發(fā)的
*
? ? * @param certs? ? 目標(biāo)驗(yàn)證證書列表
? ? * @param rootCerts 可信根證書列表
? ? * @return 驗(yàn)證結(jié)果
*/
? ? public static boolean verifyCertChain(X509Certificate[] certs, X509Certificate[] rootCerts) {
boolean sorted =sortByDn(certs);
? ? ? ? if (!sorted) {
LOGGER.error("證書鏈驗(yàn)證失敳┠浴:不是完整的證書鏈");
return false;
? ? ? ? }
//先驗(yàn)證第一個(gè)證書是不是信任庫中證書簽發(fā)的
? ? ? ? X509Certificate prev = certs[0];
? ? ? ? boolean firstOK =verifyCert(prev, rootCerts);
? ? ? ? if (!firstOK || certs.length ==1) {
return firstOK;
? ? ? ? }
//驗(yàn)證證書鏈
? ? ? ? for (int i =1; i < certs.length; i++) {
try {
X509Certificate cert = certs[i];
? ? ? ? ? ? ? ? try {
cert.checkValidity();
? ? ? ? ? ? ? ? }catch (CertificateExpiredException e) {
LOGGER.error("證書已經(jīng)過期",e);
return false;
? ? ? ? ? ? ? ? }catch (CertificateNotYetValidException e) {
LOGGER.error("證書未激活",e);
return false;
? ? ? ? ? ? ? ? }
verifySignature(prev.getPublicKey(), cert);
? ? ? ? ? ? ? ? prev = cert;
? ? ? ? ? ? }catch (Exception e) {
LOGGER.error("證書鏈驗(yàn)證失敗",e);
return false;
? ? ? ? ? ? }
}
return true;
? ? }
/**
* 驗(yàn)證證書是否是信任證書庫中證書簽發(fā)的
*
? ? * @param cert? ? ? 目標(biāo)驗(yàn)證證書
? ? * @param rootCerts 可信根證書列表
? ? * @return 驗(yàn)證結(jié)果
*/
? ? private static boolean verifyCert(X509Certificate cert, X509Certificate[] rootCerts) {
try {
cert.checkValidity();
? ? ? ? }catch (CertificateExpiredException e) {
LOGGER.error("證書已經(jīng)過期", e);
return false;
? ? ? ? }catch (CertificateNotYetValidException e) {
LOGGER.error("證書未激活", e);
return false;
? ? ? ? }
Map subjectMap =new HashMap();
? ? ? ? for (X509Certificate root : rootCerts) {
subjectMap.put(root.getSubjectDN(), root);
? ? ? ? }
Principal issuerDN = cert.getIssuerDN();
? ? ? ? X509Certificate issuer = subjectMap.get(issuerDN);
? ? ? ? if (issuer ==null) {
LOGGER.error("證書鏈驗(yàn)證失敗");
return false;
? ? ? ? }
try {
PublicKey publicKey = issuer.getPublicKey();
? ? ? ? ? ? verifySignature(publicKey, cert);
? ? ? ? }catch (Exception e) {
LOGGER.error("證書鏈驗(yàn)證失敗", e);
return false;
? ? ? ? }
return true;
? ? }
private static void verifySignature(PublicKey publicKey, X509Certificate cert)
throws NoSuchProviderException, CertificateException, NoSuchAlgorithmException, InvalidKeyException,
? ? ? ? ? ? SignatureException {
cert.verify(publicKey);
? ? }
private static boolean sortByDn(X509Certificate[] certs) {
//主題和證書的映射
? ? ? ? Map subjectMap =new HashMap();
? ? ? ? //簽發(fā)者和證書的映射
? ? ? ? Map issuerMap =new HashMap();
? ? ? ? //是否包含自簽名證書
? ? ? ? boolean hasSelfSignedCert =false;
? ? ? ? for (X509Certificate cert : certs) {
if (isSelfSigned(cert)) {
if (hasSelfSignedCert) {
return false;
? ? ? ? ? ? ? ? }
hasSelfSignedCert =true;
? ? ? ? ? ? }
Principal subjectDN = cert.getSubjectDN();
? ? ? ? ? ? Principal issuerDN = cert.getIssuerDN();
? ? ? ? ? ? subjectMap.put(subjectDN, cert);
? ? ? ? ? ? issuerMap.put(issuerDN, cert);
? ? ? ? }
List certChain =new ArrayList();
? ? ? ? X509Certificate current = certs[0];
? ? ? ? addressingUp(subjectMap, certChain, current);
? ? ? ? addressingDown(issuerMap, certChain, current);
? ? ? ? //說明證書鏈不完整
? ? ? ? if (certs.length != certChain.size()) {
return false;
? ? ? ? }
//將證書鏈復(fù)制到原先的數(shù)據(jù)
? ? ? ? for (int i =0; i < certChain.size(); i++) {
certs[i] = certChain.get(i);
? ? ? ? }
return true;
? ? }
/**
* 向上構(gòu)造證書鏈
*
? ? * @param subjectMap 主題和證書的映射
? ? * @param certChain? 證書鏈
? ? * @param current? ? 當(dāng)前需要插入證書鏈的證書,include
*/
? ? private static void addressingUp(final Map subjectMap, List certChain,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? final X509Certificate current) {
certChain.add(0, current);
? ? ? ? if (isSelfSigned(current)) {
return;
? ? ? ? }
Principal issuerDN = current.getIssuerDN();
? ? ? ? X509Certificate issuer = subjectMap.get(issuerDN);
? ? ? ? if (issuer ==null) {
return;
? ? ? ? }
addressingUp(subjectMap, certChain, issuer);
? ? }
/**
* 向下構(gòu)造證書鏈
*
? ? * @param issuerMap 簽發(fā)者和證書的映射
? ? * @param certChain 證書鏈
? ? * @param current? 當(dāng)前需要插入證書鏈的證書阵谚,exclude
*/
? ? private static void addressingDown(final Map issuerMap, List certChain,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? final X509Certificate current) {
Principal subjectDN = current.getSubjectDN();
? ? ? ? X509Certificate subject = issuerMap.get(subjectDN);
? ? ? ? if (subject ==null) {
return;
? ? ? ? }
if (isSelfSigned(subject)) {
return;
? ? ? ? }
certChain.add(subject);
? ? ? ? addressingDown(issuerMap, certChain, subject);
? ? }
/**
* 驗(yàn)證證書是否是自簽發(fā)的
*
? ? * @param cert 目標(biāo)證書
? ? * @return true梢什;自簽發(fā)绳矩,false玖翅;不是自簽發(fā)
*/
? ? private static boolean isSelfSigned(X509Certificate cert) {
return cert.getSubjectDN().equals(cert.getIssuerDN());
? ? }
}
static SSLSocketFactorygetSSLSocketFactory()throws NoSuchAlgorithmException, KeyManagementException {
SSLContext ctx =null;
? ? ? ctx = SSLContext.getInstance("TLS");
? ? ? X509Certificate rootCertificate = CertificationUtil.loadCertificate("merrick2.crt");
? ? ? ctx.init(null, new TrustManager[] {new MyX509TrustManager(rootCertificate) }, new SecureRandom());
? ? ? return ctx.getSocketFactory();
? }
?一金度、JDK ---單向https
private static String doPost(String httpUrl,String param) throws NoSuchAlgorithmException, KeyManagementException {
????HttpsURLConnection connection =null;
? ? ? InputStream is =null;
? ? ? OutputStream os =null;
? ? ? BufferedReader br =null;
? ? ? ? String result =null;
? ? ? ? try {
? ? ? ? ? ? URL url =new URL(httpUrl);
? ? ? ? ? ? // 通過遠(yuǎn)程url連接對(duì)象打開連接
? ? ? ? ? ? connection = (HttpsURLConnection) url.openConnection();
? ? ? ? ? ? connection.setSSLSocketFactory(getSSLSocketFactory());
? ? ? ? ? ? connection.setHostnameVerifier(new DefaultHostnameVerifier());
? ? ? ? ? ? // 設(shè)置連接請(qǐng)求方式
? ? ? ? ? ? connection.setRequestMethod(ExampleConstants.REQUEST_METHOD);
? ? ? ? ? ? // 設(shè)置連接主機(jī)服務(wù)器超時(shí)時(shí)間:60000毫秒
? ? ? ? ? ? connection.setConnectTimeout(2000);
? ? ? ? ? ? // 設(shè)置讀取主機(jī)服務(wù)器返回?cái)?shù)據(jù)超時(shí)時(shí)間:60000毫秒
? ? ? ? ? ? connection.setReadTimeout(60000);
? ? ? ? ? ? // 默認(rèn)值為:false猜极,當(dāng)向遠(yuǎn)程服務(wù)器傳送數(shù)據(jù)/寫數(shù)據(jù)時(shí),需要設(shè)置為true
? ? ? ? ? ? connection.setDoOutput(true);
? ? ? ? ? ? // 默認(rèn)值為:true丢胚,當(dāng)前向遠(yuǎn)程服務(wù)讀取數(shù)據(jù)時(shí)携龟,設(shè)置為true勘高,該參數(shù)可有可無
? ? ? ? ? ? connection.setDoInput(true);
? ? ? ? ? ? // 設(shè)置傳入?yún)?shù)的格式:請(qǐng)求參數(shù)應(yīng)該是 name1=value1&name2=value2 的形式华望。
? ? ? ? ? ? connection.setRequestProperty("Content-Type", ExampleConstants.Content_Type);
? ? ? ? ? ? // 通過連接對(duì)象獲取一個(gè)輸出流
? ? ? ? ? ? os = connection.getOutputStream();
? ? ? ? ? ? // 通過輸出流對(duì)象將參數(shù)寫出去/傳輸出去,它是通過字節(jié)數(shù)組寫出的
? ? ? ? ? ? os.write(param.getBytes());
? ? ? ? ? ? // 通過連接對(duì)象獲取一個(gè)輸入流,向遠(yuǎn)程讀取
? ? ? ? ? ? if (connection.getResponseCode() ==200) {
????????????????is = connection.getInputStream();
? ? ? ? ? ? ? ? // 對(duì)輸入流對(duì)象進(jìn)行包裝:charset根據(jù)工作項(xiàng)目組的要求來設(shè)置
? ? ? ? ? ? ? ? br =new BufferedReader(new InputStreamReader(is, ExampleConstants.DEFAULT_CHARSET));
? ? ? ? ? ? ? ? StringBuffer sbf =new StringBuffer();
? ? ? ? ? ? ? ? String temp =null;
? ? ? ? ? ? ? ? // 循環(huán)遍歷一行一行讀取數(shù)據(jù)
? ? ? ? ? ? ? ? while ((temp = br.readLine()) !=null) {
sbf.append(temp);
? ? ? ? ? ? ? ? ? ? sbf.append("\r\n");
? ? ? ? ? ? ? ? }
result = sbf.toString();
? ? ? ? ? ? }
}catch (MalformedURLException e) {
e.printStackTrace();
? ? ? ? }catch (IOException e) {
e.printStackTrace();
? ? ? ? }finally {
// 關(guān)閉資源
? ? ? ? ? ? if (null != br) {
try {
br.close();
? ? ? ? ? ? ? ? }catch (IOException e) {
e.printStackTrace();
? ? ? ? ? ? ? ? }
}
if (null != os) {
try {
os.close();
? ? ? ? ? ? ? ? }catch (IOException e) {
e.printStackTrace();
? ? ? ? ? ? ? ? }
}
if (null != is) {
try {
is.close();
? ? ? ? ? ? ? ? }catch (IOException e) {
e.printStackTrace();
? ? ? ? ? ? ? ? }
}
// 斷開與遠(yuǎn)程地址url的連接
? ? ? ? ? ? connection.disconnect();
? ? ? ? }
return result;
? ? }
二、OKhttp---單向https
public static Response httpsPost(String url, String json)throws Exception {
OkHttpClient client =new OkHttpClient.Builder().
sslSocketFactory(getSSLSocketFactory()).
//解決報(bào)錯(cuò)javax.net.ssl.SSLPeerUnverifiedException: Hostname 127.0.0.1 not verified
? ? ? ? ? ? ? ? ? ? hostnameVerifier(new HostnameVerifier() {
@Override
? ? ? ? ? ? ? ? public boolean verify(String s, SSLSession sslSession) {
????????????????System.out.println("主機(jī):" + s);
????????????????return true;
? ? ? ? ? ? ? ? }
}).
connectTimeout(10, TimeUnit.MINUTES).
readTimeout(10,TimeUnit.MINUTES).
build();
? ? RequestBody body = RequestBody.create(JSON, json);
? ? Request request =new Request.Builder()
.url(url)
.post(body)
.build();
? ? Response response = client.newCall(request).execute();
? ? return response;
}
三碌更、雙向認(rèn)證
如果要開啟https雙向認(rèn)證:
(1)nginx中增加配置:由nginx去對(duì)客戶端證書的驗(yàn)證
ssl_verify_client:on? # on-開啟證書校驗(yàn)痛单;off:關(guān)閉劲腿,此配置默認(rèn)為關(guān)閉
ssl_client_certificate:? ? ?#客戶端 根級(jí)證書公鑰所在路徑
(2)客戶端代碼改造:
在單向https的代碼基礎(chǔ)上焦人,增加客戶端證書(pfx格式,包含公鑰忽匈、私鑰)
第一種辦法: MyX509TrustManager中傳入兩個(gè)證書
第二種辦法:MyX509TrustManager使用KeyStore矿辽,將證書放入KeyStore中袋倔,