getlength java_坑爹微信之读取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.(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)

异常类型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.(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)

异常类型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());

// 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.certStream

改为

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

究其原因

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

参考回答:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值