Java實(shí)現(xiàn)Https調(diào)用

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中袋倔,

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宾娜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嚣艇,更是在濱河造成了極大的恐慌华弓,老刑警劉巖该抒,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凑保,死亡現(xiàn)場離奇詭異,居然都是意外死亡频伤,警方通過查閱死者的電腦和手機(jī)芝此,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸵膏,“玉大人怎炊,你說我怎么就攤上這事≌椋” “怎么了瓜挽?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵久橙,是天一觀的道長剥汤。 經(jīng)常有香客問我,道長碰凶,這世上最難降的妖魔是什么鹿驼? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮畜晰,結(jié)果婚禮上砾莱,老公的妹妹穿的比我還像新娘。我一直安慰自己凄鼻,他們只是感情好腊瑟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著块蚌,像睡著了一般闰非。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上峭范,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼辆毡。 笑死菜秦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的舶掖。 我是一名探鬼主播球昨,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼访锻!你這毒婦竟也來了褪尝?” 一聲冷哼從身側(cè)響起闹获,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤期犬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后避诽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體龟虎,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年沙庐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鲤妥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拱雏,死狀恐怖棉安,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铸抑,我是刑警寧澤贡耽,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站鹊汛,受9級(jí)特大地震影響蒲赂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刁憋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一滥嘴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧至耻,春花似錦若皱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泥耀,卻和暖如春饺汹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痰催。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國打工兜辞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迎瞧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓逸吵,卻偏偏與公主長得像凶硅,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扫皱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容