摘要:背景微信退款接口需要使用到證書,我參考微信的官方進(jìn)行,部分代碼如下上面的代碼,在本地調(diào)試的時(shí)候正常跑過,沒有出現(xiàn)任何異常,但是放到測(cè)試環(huán)境之后便會(huì)出現(xiàn)下面的異常,這三種異常都是從這里拋出來的。
背景
微信退款接口需要使用到證書,我參考微信的官方Demo進(jìn)行,部分代碼如下:
char[] password = config.getMchID().toCharArray(); InputStream certStream = config.getCertStream(); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(certStream, password);
上面的代碼,在本地調(diào)試的時(shí)候正常跑過,沒有出現(xiàn)任何異常,但是放到測(cè)試環(huán)境之后便會(huì)出現(xiàn)下面的異常,這三種異常都是從ks.load(certStream, password)這里拋出來的。定位這個(gè)問題花費(fèi)了一些時(shí)間,且讓我小小總結(jié)一下,供大家遇到相同問題時(shí)有個(gè)參考。
異常類型1java.io.IOException: Short read of DER length at sun.security.util.DerInputStream.getLength(DerInputStream.java:582) at sun.security.util.DerValue.init(DerValue.java:391) at sun.security.util.DerValue.異常類型2(DerValue.java:332) at sun.security.util.DerValue. (DerValue.java:345) at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1914) at java.security.KeyStore.load(KeyStore.java:1445) at com.lingyejun.authenticator.ReadPKCS12File$LoadCertInputStream.run(ReadPKCS12File.java:53) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
java.io.IOException: DerInputStream.getLength(): lengthTag=7, too big. at sun.security.util.DerInputStream.getLength(DerInputStream.java:599) at sun.security.util.DerValue.init(DerValue.java:391) at sun.security.util.DerValue.異常類型3(DerValue.java:332) at sun.security.util.DerValue. (DerValue.java:345) at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1914) at java.security.KeyStore.load(KeyStore.java:1445) at com.lingyejun.authenticator.ReadPKCS12File$LoadCertInputStream.run(ReadPKCS12File.java:53) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
java.io.IOException: toDerInputStream rejects tag type 54 at sun.security.util.DerValue.toDerInputStream(DerValue.java:874) at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1915) at java.security.KeyStore.load(KeyStore.java:1445) at com.lingyejun.authenticator.ReadPKCS12File$LoadCertInputStream.run(ReadPKCS12File.java:53) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
結(jié)論:keyStore.load(InputStream stream, char[] password)中的InputStream在嘗試加載的過程中,如果有其他的線程正在使用或者進(jìn)行同樣的讀加載,那么就會(huì)拋出上面的異常。
模擬復(fù)現(xiàn)package com.lingyejun.authenticator; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import java.io.IOException; import java.io.InputStream; import java.security.*; import java.security.cert.CertificateException; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 模擬加載certStream問題 * * @Author: lingyejun * @Date: 2019/6/24 * @Describe: * @Modified By: */ public class ReadPKCS12File { // 線程個(gè)數(shù) private static final int THREAD_POOL_SIZE = 10; // 初始化線程池 private ExecutorService executorService = new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); // HTTPS證書的本地路徑 private static final String CERT_LOCAL_PATH = "apiclient_cert.p12"; // HTTPS證書密碼,默認(rèn)密碼等于商戶號(hào)MCHID private static final String CERT_PASSWORD = "1509107311"; private static InputStream certStream = ReadPKCS12File.class.getClassLoader().getResourceAsStream(CERT_LOCAL_PATH); public static void main(String[] args) { ReadPKCS12File readPKCS12File = new ReadPKCS12File(); for (int threadNo = 0; threadNo < THREAD_POOL_SIZE; threadNo++) { readPKCS12File.executorService.execute(readPKCS12File.new LoadCertInputStream()); } readPKCS12File.executorService.shutdown(); } public class LoadCertInputStream implements Runnable { @Override public void run() { // 證書 char[] password = CERT_PASSWORD.toCharArray(); InputStream certStream = ReadPKCS12File.certStream; try { KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(certStream, password); // 實(shí)例化密鑰庫(kù) & 初始化密鑰工廠 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, password); // 創(chuàng)建 SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), null, new SecureRandom()); // 余下代碼就不寫了,,, System.out.println("初始化SSL成功!"); } catch (IOException e) { e.printStackTrace(); } catch (CertificateException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnrecoverableKeyException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } } } }
知道問題之后,我們只需要將certStream由全局唯一更改為方法的局部變量即可
InputStream certStream = ReadPKCS12File.certStream
改為
InputStream certStream = ReadPKCS12File.class.getClassLoader().getResourceAsStream(CERT_LOCAL_PATH)究其原因
微信的官方Demo中的,InputStream certStream = config.getCertStream(),這行代碼把我給"誤導(dǎo)"了,我是在外部讀取的pkcs12文件輸入流且config對(duì)象是單例的,導(dǎo)致多個(gè)線程共同訪問這行代碼時(shí),certStream不能被正常加載,故出現(xiàn)了上面的問題。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74978.html
摘要:添加信任所有服務(wù)端證書也可在方法中控制信任所有證書使用發(fā)送請(qǐng)求默認(rèn)端口測(cè)試客戶端證書路徑證書密碼發(fā)送請(qǐng)求導(dǎo)入客戶端證書添加信任證書為信任所有證書創(chuàng)建上下文初始化參數(shù)為,則不上傳客戶端證書通常情況都是如此驗(yàn)證系統(tǒng)默認(rèn)證書導(dǎo)出服務(wù)端證書, 添加信任所有服務(wù)端證書也可在方法中控制 package something; import java.security.cert.Certificat...
捕獲和處理異常 本節(jié)描述如何使用三個(gè)異常處理程序組件 — try、catch和finally塊 — 來編寫異常處理程序,然后,解釋了Java SE 7中引入的try-with-resources語(yǔ)句,try-with-resources語(yǔ)句特別適用于使用Closeable資源的情況,例如流。 本節(jié)的最后一部分將介紹一個(gè)示例,并分析各種場(chǎng)景中發(fā)生的情況。 以下示例定義并實(shí)現(xiàn)名為L(zhǎng)istOfNumbe...
摘要:流按操作類型分為兩種字節(jié)流字節(jié)流可以操作任何數(shù)據(jù)因?yàn)樵谟?jì)算機(jī)中任何數(shù)據(jù)都是以字節(jié)的形式存儲(chǔ)的字符流字符流只能操作純字符數(shù)據(jù),比較方便。 1_IO流概述及其分類 1.概念 IO流用來處理設(shè)備之間的數(shù)據(jù)傳輸 Java對(duì)數(shù)據(jù)的操作是通過流的方式 Java用于操作流的類都在IO包中 流按流向分為兩種:輸入流,輸出流。 流按操作類型分為兩種: 字節(jié)流 : 字節(jié)流可以操作任何數(shù)據(jù),因?yàn)樵?..
摘要:字符流字符流是什么字符流是可以直接讀寫字符的流字符流讀取字符就要先讀取到字節(jié)數(shù)據(jù)然后轉(zhuǎn)為字符如果要寫出字符需要把字符轉(zhuǎn)為字節(jié)再寫出類的方法可以按照字符大小讀取通過項(xiàng)目默認(rèn)的碼表一次讀取一個(gè)字符賦值給將讀到的字符強(qiáng)轉(zhuǎn)后打印字符流類的方法可以 1_字符流FileReader 1.字符流是什么 字符流是可以直接讀寫字符的IO流 字符流讀取字符, 就要先讀取到字節(jié)數(shù)據(jù), 然后轉(zhuǎn)為字符. ...
閱讀 2736·2021-11-11 17:21
閱讀 627·2021-09-23 11:22
閱讀 3591·2019-08-30 15:55
閱讀 1651·2019-08-29 17:15
閱讀 583·2019-08-29 16:38
閱讀 921·2019-08-26 11:54
閱讀 2517·2019-08-26 11:53
閱讀 2764·2019-08-26 10:31