嗨,
我在尝试使用 Clojure 工具在严格防火墙后的 MS-Windows 上下载项目依赖时遇到 PKIX 证书异常。
无法从/到 central 转移工件
(https://repo1.maven.org/maven2/)
sun.security.validator.ValidatorException: PKIX 路径构建失败
sun.security.provider.certpath.SunCertPathBuilderException: unable
to find valid certification path to requested target
我认为这是由于防火墙使用的自签名根证书引起的,该证书用于控制与监视流量。
以下是分析及对长时间阅读的歉意。
在大型组织中,工作站通常位于防火墙后面,该防火墙控制和监视所有来自和前往互联网的流量。公司创建自己的自签名根证书并将其替换为 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 密钥存储库中(有关证书和 SSL 的工作,请参阅https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html)。
相反,似乎更容易通过使用 java.next.ssl.*
属性来指示 Java 查看通用或备用密钥存储库(https://stackoverflow.com/questions/5871279/ssl-and-cert-keystore)。
在Windows上,我想大多数有如此限制性防火墙的用户都在使用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脚本传递到准备命令中 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属性来解决一般问题的选项,从最侵入性/最具体到通用情况
- 对于负责下载依赖的函数
clojure.tools.deps.alpha.script.make-classpath2
,如果运行在Windows上,则设置 javax.net.ssl.trustStoreType=Windows-ROOT
在运行时,
- 例如:
(System/setProperty "javax.net.ssl.trustStoreType" "Windows-ROOT")
。
- cons
- 这仅在Windows上有效。
- 这强制要求使用通用keystore,但这可能不是用户总是想要的。
- 可以通过增加一个新脚本的选项(例如 -W)来减轻此影响,该选项作为参数传递给
clojure.tools.deps.alpha
。缺点是用户/工具必须始终在执行时设置此标志。
- 使用
System/setProperty
在运行时设置 javax.net.ssl.tustStoreType
属性可能无效,如果代码的其他部分之前已建立了任何SSL连接。
- 与之前相同,但新增一个脚本选项(例如-Sssl EDN),其EDN是
javax.net.ssl.*
属性,并将作为此类传递给 clojure.tools.deps.alpha
以在运行时设置。
- 例如:
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
fn在运行时设置属性使用 System/setProperty
。
- 例如示例clj用户配置
{:clojure.tools.deps.alpha/ssl {:trustStore "Windows-ROOT"}}
- cons
- 如果代码的其他部分之前已建立了任何SSL连接,则使用
System/setProperty
在运行时设置 javax.net.ssl.*
属性可能无效。
- 更新脚本,以便将 -J JVM选项传递到-P命令的Java调用中(我已经测试过这可以工作)。
- 例如:
clojure -J-D'javax.net.ssl.trustStoreType=Windows-ROOT' -P
- cons
- 用户/工具仍然必须在每次调用clojure时提供
-J-Djavax.net.ssl.*
选项。
- 与之前相同,但引入一个
CLJ_JVMOPTS
环境变量,其值被插入到脚本的 $JvmOpts
变量中。
- 例如:
set CLJ_JVMOPTS=-D'javax.net.ssl.trustStoreType=Windows-ROOT'
,然后 clojure -P
我更倾向于#5,因为没有明显的缺点,可以一次性设置并应用于所有的clojure调用,为用户/工具提供更好的体验。
告诉我您的想法。我很乐意在脚本中或tools.deps.alpha
库中进行更改。
谢谢
PS:我还尝试像在https://maven.apache.org/guides/mini/guide-repository-ssl.html中一样在 MAVEN_OPTS
变量中设置属性,但没有效果。我怀疑这仅在直接调用 mvn
命令行工具时才生效。