从远程主机自动下载X509证书链

我正在构建一些.net代码,它将在我们的一台服务器上按计划无人值守运行。 其执行的一部分要求它执行Java可执行文件,该可执行文件通过SSL与某些Web资源和服务进行通信。

如果它通过SSL做某事并且它没有远程证书链中的每一个证书,那么Java绝对会适合。 所以在.net中,我需要能够具体的https:// [无论] .com资源,并需要它在本地下载整个远程链。

为什么我要自动执行此操作? 因为我希望简化部署并且不必执行此操作4次,然后每次Oracle证书之一到期时再次执行此操作。

我这样做:

X509Certificate2 lowestCert = SecurityHelper.DownloadSslCertificate(keyDownloadLocation); X509Chain chain = new X509Chain(); chain.Build(lowestCert); int counter = 0; foreach (X509ChainElement el in chain.ChainElements) { //Extract certificate X509Certificate2 chainCert = el.Certificate; //Come up with an alias and save location string alias = certBaseName + counter; string localLocation = Path.GetDirectoryName( System.Reflection.Assembly.GetEntryAssembly().Location ) + @"\" +alias + ".cer"; //Save it locally SecurityHelper.SaveCertificateToFile(chainCert, localLocation); //Remove (if possible) and add to java's keystore deleteKeyFromKeystore( javaFolder, alias, certKeyStore, SecurityHelper.GetEncryptedAppSetting("JavaKeystorePassword") ); addKeytoKeyStore( localLocation, javaFolder, alias, certKeyStore, SecurityHelper.GetEncryptedAppSetting("JavaKeystorePassword") ); //Then delete if (File.Exists(localLocation)) File.Delete(localLocation); counter++; } 

SecurityHelper.DownloadSslCertificate是一种自定义方法,可以从网站URL创建X509Certificate2。

“cert”是最低级别的证书,我可以把它拿回来,但我不能做的就是把所有东西都拿到链子上。 chain.ChainElements仍然只有1级深度,如果机器上尚未安装链的其余部分。 我的问题是,这是一个全新的URL,完全未知(对机器)证书,我想上升堆栈并递归下载每一个。

有没有办法找到机器未知的URL并以编程方式下载链中的每个证书?

编辑:这是我如何下载eh SSL证书:

 public static X509Certificate2 DownloadSslCertificate(string strDNSEntry) { X509Certificate2 cert = null; using (TcpClient client = new TcpClient()) { client.Connect(strDNSEntry, 443); SslStream ssl = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback( (sender, certificate, chain, sslPolicyErrors) => { return true; } ), null); try { ssl.AuthenticateAsClient(strDNSEntry); } catch (AuthenticationException e) { ssl.Close(); client.Close(); return cert; } catch (Exception e) { ssl.Close(); client.Close(); return cert; } cert = new X509Certificate2(ssl.RemoteCertificate); ssl.Close(); client.Close(); return cert; } } 

编辑#2获取对我有用的远程ssl证书的解决方案是捕获在SslStream.AuthenticateAsClient(url)期间发生的validation回调中的链;

  private static X509ChainElementCollection callbackChainElements = null; private static bool CertificateValidationCallback( Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { callbackChainElements = chain.ChainElements; return true; } 

然后用这个回调启动validation,我有类似的东西:

 public static X509Certificate2 DownloadSslCertificate(string url) { X509Certificate2 cert = null; using (TcpClient client = new TcpClient()) { client.Connect(url, 443); SslStream ssl = new SslStream(client.GetStream(), false, CertificateValidationCallback, null); try { ssl.AuthenticateAsClient(url); //will call CertificateValidationCallback } 

……等

然后,当AuthenticateAsClient完成时,本地“callbackChainElements”变量被设置为X509ChainElement的集合。

实际上我使用Powershell做了一些非常相似的事情。

一件是改变ServicePointManager忽略SSLvalidation(因为你可能没有本地的根证书,听起来)。 它是要设置为true的ServerCertificateValidationCallback,它告诉您正在使用自定义validation(或者在您的情况下,可能没有validation)。

从那里开始,你的大部分时间都与你的联盟有关。 您可以使用Export方法将证书导出为字节数组:

 foreach (X509ChainElement el in chain.ChainElements) { var certToCreate = el.Certificate.Export(X509ContentType.Cert); ... } 

从那里开始,只需要用字节数组做你想做的事情。 您可以将其传递给另一个证书对象的构造函数,然后可以将其导入Java密钥库。

希望有所帮助,如果您有其他问题,请告诉我。

编辑 :实际上,我刚刚注意到这篇关于在.NET中使用X.509证书的提示 。 作者建议不要直接从字节数组中写入,因为它会将临时文件写入不会被清除的目录中。 所以他的建议是将字节数组写入磁盘,然后在完成后手动清理它。