Java可执行Jar运行出现PKIX证书路径验证失败问题求助
PKIX路径构建失败问题排查与解决
问题描述
我用Java 8开发了一个需通过Jar包执行的应用,启动类为DescargaMensajesEntidadesInit,代码如下:
public class DescargaMensajesEntidadesInit { private static Logger log = LoggerFactory.getLogger(DescargaMensajesEntidadesInit.class); public static void main(String[] args) { // TODO Apéndice de método generado automáticamente //Verificamos que se pasen tres argumentos if (args.length < 4) { log.warn("Uso: java Main <idAplicacion> <ruta_certificado> <clave_certificado> <entidad>"); return; } //Primer argumento: La identificación de la aplicación String idAplicacion = args[0]; //Segundo argumento: ruta al archivo de certificado String rutaCertificado = args[1]; File certificado = new File(rutaCertificado); //Tercer argumento: clave del certificado String claveCertificado = args[2]; //Cuarto argumento: entidad (debe cumplir con la expresión regular [0-9]{8}) String entidad = args[3]; String patronEntidad = "[0-9]{10}"; //Validamos que el argumento "entidad" cumple con la expresión regular if (!entidad.matches(patronEntidad)) { log.warn("Error: La entidad debe ser un string de 10 dígitos (ejemplo: 1234567890)."); return; } //Verificamos si el archivo de certificado existe y es un archivo if (certificado.exists() && certificado.isFile()) { log.info("Identificacion de la aplicacion: " + idAplicacion); log.info("Ruta del certificado: " + rutaCertificado); log.info("Clave del certificado: " + claveCertificado); log.info("Entidad: " + entidad); //Llamamos al Servicio descarga de mensajes DescargaMensajesEntrada entrada = new DescargaMensajesEntrada(); entrada.setIdAplicacion(idAplicacion); entrada.setFicheroP12(rutaCertificado); entrada.setPasswordP12(claveCertificado.toCharArray()); entrada.setIdEntidad(entidad); String respuesta = descargaMensajesEntidades(entrada); log.info(respuesta); } else { log.warn("El archivo de certificado no existe o no es un archivo válido."); } } private static String descargaMensajesEntidades(DescargaMensajesEntrada entrada) { String respuesta = ""; try { DescargaMensajesEntidadesReturn response = new DescargaMensajesEntidadesReturn(); DescargarMensajesEntidadesService descargarMensajesEntidadesService = new DescargarMensajesEntidadesServiceImpl(); response = descargarMensajesEntidadesService.ejecutar(entrada); respuesta = response.getRespuestaCorrecta().getOut(); if("".equals(respuesta) || respuesta == null) { respuesta = response.getError().getTexto(); } } catch (DescargaMensajesEntidadesFault_Exception e) { log.error("Problemas en el servicio de Descarga de mensajes, motivo: " + e.getMessage()); } return respuesta; } }
直接运行并传入参数时程序正常工作,但打包为可执行Jar后,执行以下命令出现PKIX路径构建失败的SSL握手错误:
java -Djavax.net.ssl.trustStore=CERTLEXNET.p12 -Djavax.net.ssl.trustStorePassword=8ba73c7240 -jar palex.jar "PALEXWS" "/certificado/CERTLEXNET.p12" "8ba73c7240" "2807906202"
编译Jar使用jdk1.8.0_231,运行环境为jre1.8.0_431,不确定是否与版本差异有关,求解决。
解决步骤
1. 指定信任库类型参数
P12格式证书需明确指定信任库类型,JVM默认使用JKS格式,修改执行命令添加-Djavax.net.ssl.trustStoreType=PKCS12:
java -Djavax.net.ssl.trustStore=CERTLEXNET.p12 -Djavax.net.ssl.trustStorePassword=8ba73c7240 -Djavax.net.ssl.trustStoreType=PKCS12 -jar palex.jar "PALEXWS" "/certificado/CERTLEXNET.p12" "8ba73c7240" "2807906202"
2. 校验证书路径与权限
- 命令中
-Djavax.net.ssl.trustStore=CERTLEXNET.p12用的是相对路径,确认执行命令的工作目录下存在该文件,或改用绝对路径(如-Djavax.net.ssl.trustStore=/certificado/CERTLEXNET.p12) - 确保运行程序的用户对证书文件有读取权限
3. 验证证书完整性
用keytool工具检查证书是否有效:
keytool -list -keystore CERTLEXNET.p12 -storetype PKCS12 -storepass 8ba73c7240
若命令报错,说明证书损坏或密码错误,需重新获取正确证书。
4. 处理JDK/JRE版本差异
jdk1.8.0_231到jre1.8.0_431的更新可能调整了SSL信任策略,可尝试:
- 切换编译环境为jre1.8.0_431,重新打包Jar
- 将证书导入JRE默认信任库
cacerts:
keytool -importkeystore -srckeystore CERTLEXNET.p12 -srcstoretype PKCS12 -srcstorepass 8ba73c7240 -destkeystore $JAVA_HOME/jre/lib/security/cacerts -deststoretype JKS -deststorepass changeit
(注:changeit是cacerts默认密码,若已修改请替换)
5. 程序内自定义SSL上下文
若上述方法无效,可在代码中手动加载证书,不依赖JVM参数:
// 在调用descargaMensajesEntidades方法前添加以下代码 try { KeyStore keyStore = KeyStore.getInstance("PKCS12"); FileInputStream fis = new FileInputStream(rutaCertificado); keyStore.load(fis, claveCertificado.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), new SecureRandom()); SSLContext.setDefault(sslContext); } catch (Exception e) { log.error("Error al configurar SSL context: " + e.getMessage()); }
内容的提问来源于stack exchange,提问作者Jesus
相关产品推荐
相关产品推荐

