前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >坑爹微信之读取PKCS12流时出现的java.io.IOException: DerInputStream.getLength

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

作者头像
翎野君
发布2023-05-12 20:10:27
2930
发布2023-05-12 20:10:27
举报
文章被收录于专栏:翎野君

背景

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

代码语言:javascript
复制
char[] password = config.getMchID().toCharArray();
InputStream certStream = config.getCertStream();
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(certStream, password);

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

异常类型1

代码语言:javascript
复制
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

代码语言:javascript
复制
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

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

模拟复现

代码语言:javascript
复制
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由全局唯一更改为方法的局部变量即可

代码语言:javascript
复制
InputStream certStream = ReadPKCS12File.certStream

改为

代码语言:javascript
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
    • 异常类型1
      • 异常类型2
        • 异常类型3
        • 模拟复现
        • 究其原因
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档