坑爹微信之读取PKCS12流时出现的java.io.IOException: DerInputStream.getLength
背景
微信退款接口需要使用到证书,我参考微信的官方Demo进行,部分代码如下:
1 2 3 4 | char [] password = config.getMchID().toCharArray(); InputStream certStream = config.getCertStream(); KeyStore ks = KeyStore.getInstance( "PKCS12" ); ks.load(certStream, password); |
上面的代码,在本地调试的时候正常跑过,没有出现任何异常,但是放到测试环境之后便会出现下面的异常,这三种异常都是从ks.load(certStream, password)这里抛出来的。定位这个问题花费了一些时间,且让我小小总结一下,供大家遇到相同问题时有个参考。
异常类型1
1 2 3 4 5 6 7 8 9 10 11 | java.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.<init>(DerValue.java:332) at sun.security.util.DerValue.<init>(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) |
异常类型2
1 2 3 4 5 6 7 8 9 10 11 | 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.<init>(DerValue.java:332) at sun.security.util.DerValue.<init>(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) |
异常类型3
1 2 3 4 5 6 7 8 | 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) at java.lang.Thread. run (Thread.java:748) |
结论:keyStore.load(InputStream stream, char[] password)中的InputStream在尝试加载的过程中,如果有其他的线程正在使用或者进行同样的读加载,那么就会抛出上面的异常。
模拟复现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 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 { // 线程个数 private static final int THREAD_POOL_SIZE = 10 ; // 初始化线程池 private ExecutorService executorService = new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); // HTTPS证书的本地路径 private static final String CERT_LOCAL_PATH = "apiclient_cert.p12" ; // HTTPS证书密码,默认密码等于商户号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); // 实例化密钥库 & 初始化密钥工厂 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, password); // 创建 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由全局唯一更改为方法的局部变量即可
1 | InputStream certStream = ReadPKCS12File.certStream |
改为
1 | InputStream certStream = ReadPKCS12File. class .getClassLoader().getResourceAsStream(CERT_LOCAL_PATH) |
究其原因
微信的官方Demo中的,InputStream certStream = config.getCertStream(),这行代码把我给'误导'了,我是在外部读取的pkcs12文件输入流且config对象是单例的,导致多个线程共同访问这行代码时,certStream不能被正常加载,故出现了上面的问题。
参考回答:
出处:http://www.cnblogs.com/lingyejun/
若本文如对您有帮助,不妨点击一下右下角的【推荐】。
如果您喜欢或希望看到更多我的文章,可扫描二维码关注我的微信公众号《翎野君》。
转载文章请务必保留出处和署名,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现