坑爹微信之读取PKCS12流时出现的java.io.IOException: DerInputStream.getLength

简介: 坑爹微信之读取PKCS12流时出现的java.io.IOException: DerInputStream.getLength

背景

微信退款接口需要使用到证书,我参考微信的官方Demo进行,部分代码如下:

char[] password = config.getMchID().toCharArray();
InputStream certStream = config.getCertStream();
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(certStream, password);

上面的代码,在本地调试的时候正常跑过,没有出现任何异常,但是放到测试环境之后便会出现下面的异常,这三种异常都是从ks.load(certStream, password)这里抛出来的。定位这个问题花费了一些时间,且让我小小总结一下,供大家遇到相同问题时有个参考。

异常类型1

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

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

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在尝试加载的过程中,如果有其他的线程正在使用或者进行同样的读加载,那么就会抛出上面的异常。    

模拟复现

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由全局唯一更改为方法的局部变量即可

InputStream certStream = ReadPKCS12File.class.getClassLoader().getResourceAsStream(CERT_LOCAL_PATH)

改为

InputStream certStream = ReadPKCS12File.class.getClassLoader().getResourceAsStream(CERT_LOCAL_PATH)

究其原因

微信的官方Demo中的,InputStream certStream = config.getCertStream(),这行代码把我给'误导'了,我是在外部读取的pkcs12文件输入流且config对象是单例的,导致多个线程共同访问这行代码时,certStream不能被正常加载,故出现了上面的问题。

 

参考回答:

https://stackoverflow.com/questions/7399154/pkcs12-derinputstream-getlength-exception/7399546#7399546


目录
相关文章
|
小程序 Java 数据安全/隐私保护
基于微信小程序的音乐平台 毕业设计 JAVA+Vue+SpringBoot+MySQL
基于微信小程序的音乐平台 毕业设计 JAVA+Vue+SpringBoot+MySQL
261 1
|
弹性计算 前端开发 小程序
微信小程序上传文件至阿里云OSS直传(java后端签名+前端直传)
当前的通用文件上传方式是通过前端上传到服务器,再由服务器转存至对象存储。这种方式在处理小文件时效率尚可,但大文件上传因受限于服务器带宽,速度较慢。例如,一个100MB的文件在5Mbps带宽的阿里云ECS上上传至服务器需160秒。为解决此问题,可以采用后端签名的方式,使微信小程序直接上传文件到阿里云OSS,绕过服务器中转。具体操作包括在JAVA后端引入相关依赖,生成签名,并在微信小程序前端使用这个签名进行文件上传,注意设置正确的请求头和formData参数。这样能提高大文件上传的速度。
3068 1
|
4月前
|
数据可视化 机器人 Java
聊天软件自动回复脚本,微信抖音快手小红书,消息自动回复工具机器人【java】
包含4个完整模块:主逻辑模块实现核心回复功能,工具模块封装常用函数,UI模块提供可视化控制界面
|
3月前
|
XML Java Android开发
微信虚拟视频插件安卓,微信虚拟相机替换拍照,java源码分享
完整的相机应用项目包含三个主要文件:主活动实现、布局文件和清单文件。代码实现了相机预览、
|
25天前
|
消息中间件 人工智能 Java
抖音微信爆款小游戏大全:免费休闲/竞技/益智/PHP+Java全筏开源开发
本文基于2025年最新行业数据,深入解析抖音/微信爆款小游戏的开发逻辑,重点讲解PHP+Java双引擎架构实战,涵盖技术选型、架构设计、性能优化与开源生态,提供完整开源工具链,助力开发者从理论到落地打造高留存、高并发的小游戏产品。
|
25天前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
3月前
|
Java 计算机视觉
微信虚拟视频聊天插件,QQ抖音快手虚拟摄像头工具,替换相机视频流java
实现包含了虚拟摄像头核心功能,可以捕获真实摄像头视频流,处理后输出到虚拟摄像头设备。
|
4月前
|
XML Java 数据格式
微信不封号无限加人软件,微信一键自动加人软件,java实现批量化加人
本项目包含手机号生成工具与附近人列表展示功能。手机号工具支持批量生成、格式验证及CSV导出,可自定义前缀生成符合中国规则的随机号码。
|
4月前
|
机器人 Java
微信自动回复机器人插件,自动同意回复消息, 微信群管理机器人,JAVA框架
本项目基于开源微信机器人框架开发,提供自动回复私聊消息、自动同意好友请求及微信群管理功能。核心功能包括:自动回复用户消息、处理好友申请(发送欢迎语并备注新好友)、群聊管理(如欢迎新成员、关键词踢人和群内自动回复)。项目依赖 `weixin-java-mp` 和 `weixin-java-cp` 核心库,并结合 Lombok 和 Guava 提升开发效率。代码结构清晰,包含配置类 `WechatBotConfig` 初始化服务实例,以及多个处理器实现具体逻辑。下载地址:https://www.pan38.com/share.php?code=r4HGg,提取码:8888(仅供学习参考)。
|
8月前
|
存储 小程序 前端开发
微信小程序与Java后端实现微信授权登录功能
微信小程序极大地简化了登录注册流程。对于用户而言,仅仅需要点击授权按钮,便能够完成登录操作,无需经历繁琐的注册步骤以及输入账号密码等一系列复杂操作,这种便捷的登录方式极大地提升了用户的使用体验
2476 12

热门文章

最新文章