你好,
我在尝试使用 MS-Windows 上的严格防火墙后面的 clojure 工具下载项目依赖项时遇到了 PKIX 证书异常。
无法从 central 转移工件…
(https://repo1.maven.org/maven2/)
sun.security.validator.ValidatorException: PKIX 路径构建失败
sun.security.provider.certpath.SunCertPathBuilderException: 无能力
找到到请求的目标的有效证书路径
我认为这可能是由于防火墙使用自签名的根证书来控制 & 监控流量所导致的。
以下是分析,并对冗长的阅读致歉。
在大型组织中,将工作站置于防火墙后面,该防火墙控制和监控所有来自和前往互联网的流量是一种标准做法。公司自己创建自签名的根证书并将它们替换到 https 流量中也很常见,以解密和分析数据流,实际上利用了中间人攻击对他们有利。
为了使其工作,用户工作站,其中连接来源发起,需要在他们的可信证书存储中安装自签名证书。在 Windows 上,这将是可信根证书颁发机构证书密钥库。
Java 自带自己的证书存储,即 Java 密钥存储库,它独立于工作站上可用的通用密钥库。因此,任何通过防火墙的 Java https 互联网连接都可能因未知证书错误而失败,因为自签名证书未安装在 Java 密钥库中。
这不利于 clojure/clj 命令行工具下载库,因为它无法在 Java 密钥库中找到自签名证书。例如,当从此类计算机尝试访问互联网时,会抛出异常。
> clojure -P
Error building classpath ...
org.eclipse.aether.resolution.ArtifactDescriptorException: Failed to
read artifact descriptor for .... ... Caused by:
org.eclipse.aether.resolution.ArtifactResolutionException: Could not
transfer artifact ... from/to central
(https://repo1.maven.org/maven2/):
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target ...
...
我想至少有两种方法可以绕过这个问题
1. 在 Java 密钥库中安装缺失的证书,或者
2. 指示 Java 实例检查通用密钥库。
似乎没有直接发现缺失证书的方法,你可能需要一些用户努力才能使用 Java keytool 将它们安装到 Java 密钥库中(请在https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html上的“处理证书和 SSL”部分查看工作)。
相反,似乎更容易使用 java.next.ssl.*
属性来指示 Java 检查通用或备选密钥库(请参见https://stackoverflow.com/questions/5871279/ssl-and-cert-keystore)。
在Windows系统上,假设大多数配置有严格防火墙的用户都在使用,只需设置一个简单的Java属性,指导Java使用通用Windows密钥库(https://stackoverflow.com/questions/41257366/import-windows-certificates-to-java)
javax.net.ssl.trustStoreType=Windows-ROOT
基于此,我原本期待以下操作在Windows上能够工作
clojure -J-D'javax.net.ssl.trustStoreType=Windows-ROOT' -P
但它并没有。结果发现在这里的PowerShell脚本中 https://github.com/clojure/brew-install/blob/b91fb78e321b5e39bedda594f5d578579d448d19/src/main/resources/clojure/install/ClojureTools.psm1#L388,并未将$JvmOpts
变量传递给pre命令
& $JavaCmd -classpath $ToolsCp clojure.main -m clojure.tools.deps.alpha.script.make-classpath2 --config-user $ConfigUser --config-project $ConfigProject --basis-file $BasisFile --libs-file $LibsFile --cp-file $CpFile --jvm-file $JvmFile --main-file $MainFile --manifest-file $ManifestFile @ToolsArgs
我想到的一些选项,可以通过使用Java属性来解决一般问题,从最侵入性/具体到一般情况
- 如果运行在Windows上,负责下载依赖的
clojure.tools.deps.alpha.script.make-classpath2
函数应该设置javax.net.ssl.trustStoreType=Windows-ROOT
- .e.g.
(System/setProperty "javax.net.ssl.trustStoreType" "Windows-ROOT")
。
- cons
- 这仅在Windows上有效。
- 这会强制使用通用密钥库,但这可能不是用户始终想要的。
- 可以通过一个新的脚本选项(例如-W)来缓解这种情况,将其传递给
clojure.tools.deps.alpha
。缺点是用户/工具必须在每次调用时始终传递此标志。
- 使用
System/setProperty
在运行时设置javax.net.ssl.tustStoreType
属性可能没有效果,如果代码的其他部分之前已经进行了任何SSL连接。
- 与前面的相同,但引入一个新的脚本选项(例如-Sssl EDN),其中包含
javax.net.ssl.*
属性,并将作为此类传递给clojure.tools.deps.alpha
,在运行时设置。
- e.g.
clojure -Sssl {:trustStoreType "Windows-ROOT"} -P
- cons
- 与#1中的相同缺点,而且对于用户来说,每次调用clojure时都要输入edn映射更加繁琐。
- 发明一个新的deps.edn键(例如
:clojure.tools.deps.alpha/ssl
),其键值对应为java.net.ssl.*
属性,可以在用户配置的deps.edn中设置,并由clojure.tools.deps.alpha
s make-classpath2
fn在运行时使用System/setProperty
设置属性。
- e.g. 示例clj用户配置
{:clojure.tools.deps.alpha/ssl {:trustStore "Windows-ROOT"}}
- cons
- 使用
System/setProperty
在运行时设置javax.net.ssl.*
属性可能没有效果,如果代码的其他部分之前已经进行了任何SSL连接。
- 更新脚本,以便将-J JVM选项传递给-P命令的Java调用(我已经测试过该功能)。
- e.g.
clojure -J-D'javax.net.ssl.trustStoreType=Windows-ROOT' -P
- cons
- 用户/工具仍然必须在每次调用clojure时提供
-J-Djavax.net.ssl.*
选项。
- 与前面的相同,但引入一个名为
CLJ_JVMOPTS
的环境变量,其值插入到脚本的$JvmOpts
变量中。
- e.g.
set CLJ_JVMOPTS=-D'javax.net.ssl.trustStoreType=Windows-ROOT'
,然后clojure -P
我更倾向于#5,因为它似乎没有任何缺点,并且可以一键设置并应用于所有clojure调用,为用户/工具提供了更好的体验。
告诉我您的想法。我很乐意修改脚本和/或tools.deps.alpha
库。
谢谢
PS:我还尝试了将属性设置为MAVEN_OPTS
变量,正如https://maven.apache.org/guides/mini/guide-repository-ssl.html中所述,但没有效果。我怀疑这只能在直接调用mvn
命令行工具时生效。