命令行工具

PKIX path building failed(Maven)下载依赖报错怎么修

作者: · 最后核实

报错信息Could not transfer artifact ... from/to central (https://repo.maven.apache.org/maven2): PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Maven 报 PKIX path building failed,意思是运行 Maven 的 Java 环境不信任仓库出示的证书。 实际情况多半是公司代理用自己的根证书重新签发了 HTTPS,或者私有 Nexus、Artifactory 用的是内部 CA,而这张根证书不在 JDK 的 cacerts 里。它和 Java PKIX path building failed 是同一个问题,只是出现在构建工具里。

快速修复

最快的修法是把代理或内部 CA 的根证书导入 Maven 实际使用的那个 JDK。

  1. 找到 Maven 使用的 JDK:
mvn -v    # 看 "Java home:" 一行
  1. 在这台机器上看仓库证书是谁签发的:
openssl s_client -connect maven.aliyun.com:443 -servername maven.aliyun.com -showcerts </dev/null | grep -E 's:|i:'

如果最上层的签发者是公司名或代理设备,而不是公网 CA,就找 IT 要这张根证书。

  1. 导入到那个 JDK:
"$JAVA_HOME/bin/keytool" -importcert -cacerts -alias company-proxy-root \
  -file company-proxy-root.pem -storepass changeit -noprompt

(JDK 8 把 -cacerts 换成 -keystore "$JAVA_HOME/jre/lib/security/cacerts"。)然后重新构建。不需要清整个本地仓库,但要删掉下载失败时留下的 *.lastUpdated 文件,或者加 -U 运行。

原因

这个报错有 4 种原因,办公网络里最常见的是公司代理解密 HTTPS。

1. 公司代理或安全网关解密 HTTPS

浏览器正常,是因为 IT 把代理根证书装进了系统证书库;JDK 有自己的证书库,并不知道它。

  • 确认: 所有 HTTPS 仓库都失败,签发者是代理设备。
  • 修复: 按上面导入代理根证书;Windows 上也可以给 Maven 加 -Djavax.net.ssl.trustStoreType=Windows-ROOT,让 JDK 直接读 Windows 证书库。

2. 私有仓库用了内部 CA

公司自建的 Nexus、Artifactory 或 GitLab 制品库,证书由内部 CA 签发。

  • 修复: 导入内部根证书,或者在项目里放一个专用信任库。先复制公网根证书,再加上自己的:
cp "$JAVA_HOME/lib/security/cacerts" .mvn/truststore.p12
keytool -importcert -keystore .mvn/truststore.p12 -storepass changeit \
  -alias company-root -file company-root.pem -noprompt

然后在 .mvn/jvm.config 里写:

-Djavax.net.ssl.trustStore=.mvn/truststore.p12
-Djavax.net.ssl.trustStorePassword=changeit

3. 仓库服务器证书链不完整

自建 Nexus 前面放了 Nginx,只配置了站点证书,没发中间证书,浏览器能打开,Java 却报错。

  • 确认: openssl s_client -showcerts 只列出一张证书,可以用证书链补全工具查看缺了哪张。
  • 修复: 在服务器上配置完整证书链;宝塔面板里把中间证书一起粘贴到证书栏。

4. 修错了 JDK

导入到了一个 JDK,而 Maven、IDE 或 CI 构建机用的是另一个。逐一检查 mvn -v、IDE 的 Maven Runner 设置和 CI 镜像。

各环境修法

各环境的做法都是让运行 Maven 的 JVM 信任这张根证书:导入 cacerts,或者用参数指定专用信任库。

Maven Wrapper(mvnw)和 CI: 把信任库参数写进 .mvn/jvm.config 或 MAVEN_OPTS,每台构建机都能用上。用 Docker 镜像跑 CI 时,在构建镜像时就把根证书导入镜像里的 JDK。

国内镜像: 在 settings.xml 里配置阿里云等镜像(https://maven.aliyun.com/repository/public)可以加快下载,但它解决的是速度,不是信任问题;镜像地址也必须保持 https://。

IntelliJ IDEA / Eclipse: 确认运行 Maven 的 JDK(IDEA:Settings → Build Tools → Maven → Runner → JRE),导入到那个 JDK,或者改用已经修好的系统 JDK。

Gradle: 解析依赖时会报同样的错,在 gradle.properties 里指定信任库:

systemProp.javax.net.ssl.trustStore=/path/to/truststore.p12
systemProp.javax.net.ssl.trustStorePassword=changeit

Linux 系统自带 JDK: Debian/Ubuntu 和 CentOS/RHEL 系统上,把根证书加入系统信任库(update-ca-certificates 或 update-ca-trust)会同时更新系统 OpenJDK 的 cacerts。

不要这样做

关闭证书校验或改用 HTTP 仓库,都会让依赖下载失去防篡改保护。

  • -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true 之类的参数。它们关掉了依赖下载的证书校验,而依赖下载正是供应链攻击最常发生的地方。
  • 把仓库地址改成 http://,或者从 settings.xml 里删掉拦截 HTTP 的 mirror 配置。
  • 提交包含私钥的信任库,或者从老 JDK 复制一份缺少新根证书的 cacerts。

常见问题

Maven 用的是哪个 JDK?

优先用 JAVA_HOME 指向的 JDK,没设置时用 PATH 上找到的 java。mvn -v 输出里的 Java home: 就是它。IntelliJ IDEA、Eclipse 可能用自己配置的 JDK 运行 Maven,那个 JDK 有自己的 cacerts。

能不能把信任库配置放在项目里,而不是每台电脑都改?

可以。Maven 3.3.1 起支持项目根目录的 .mvn/jvm.config,里面的 JVM 参数对每次构建生效,例如 -Djavax.net.ssl.trustStore=.mvn/truststore.p12。信任库里只能放证书,绝不能放私钥。

换成阿里云 Maven 镜像能解决吗?

只有当出问题的恰好是原来那台仓库服务器时才有用。如果是公司代理在解密 HTTPS,访问任何 HTTPS 地址都会同样报错,换镜像没有用,必须先让 JVM 信任代理根证书。

直接把仓库地址改成 http:// 不行吗?

不行。Maven 3.8.1 起默认拦截 HTTP 仓库,原因是:通过 HTTP 下载的依赖可以在传输途中被替换,而这些代码会进入你的构建和产品。

资料来源

  1. Maven: Guide to Remote repository access through authenticated HTTPS
  2. Maven: Configuring Apache Maven
  3. keytool (Java SE 21 tool reference)
  4. Gradle: Build Environment