嗨,
我在使用Clojure工具尝试在MS-Windows后严格防火墙下下载项目依赖项时遇到了PKIX证书异常。
无法从中央仓库传输工件
(https://repo1.maven.org/maven2/)
sun.security.validator.ValidatorException: PKIX路径构建失败
sun.security.provider.certpath.SunCertPathBuilderException: 无法
找到请求目标的合法证书路径
我认为这是因为防火墙使用自签名的根证书来控制并监视流量。
以下是分析,并对长的阅读感到抱歉。
在大组织中,工作站通常位于控制并监视所有与互联网往来的防火墙后面,这已成为一种标准做法。公司通常还会创建自己的自签名根证书,并用它替换https流量中发现的证书,以便解密和分析数据流,实际上利用中间人攻击来达到其目的。
为此,从中发起连接的用户工作站需要在其信任的证书存储中安装自签名证书。在Windows上,这将是受信任的根授权证书密钥库。
Java自带自己的证书存储,称为java KeyStore,它与工作站上可用的通用密钥库是分开的。因此,任何通过防火墙通过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)。
相反,似乎更容易使用java.next.ssl.*
属性(请参见https://stackoverflow.com/questions/5871279/ssl-and-cert-keystore)来指示Java查看通用或备用密钥库。
在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
但它并没有。结果是 $JvmOpts
变量并未通过这里的Powershell脚本传递给 prep 命令 https://github.com/clojure/brew-install/blob/b91fb78e321b5e39bedda594f5d578579d448d19/src/main/resources/clojure/install/ClojureTools.psm1#L388
& $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
。缺点是用户/工具必须在每次调用时都传递此标志。
- 如果代码的其他部分之前已建立任何SSL连接,则使用
System/setProperty
设置 javax.net.ssl.tustStoreType
属性可能不会有任何效果。
- 与上一条相同,但引入了新的脚本选项(例如 -Sssl EDN),其中的 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
的 make-classpath2
函数解析,该函数使用 System/setProperty
在运行时设置属性。
- e.g. 示例 clj 用户配置
{:clojure.tools.deps.alpha/ssl {:trustStore "Windows-ROOT"}}
- cons
- 如果代码的其他部分之前已建立任何SSL连接,则使用
System/setProperty
设置 javax.net.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
命令行工具时才会生效。