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

Tor*_*Tor 5 c# certificate x509certificate2 x509certificate

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

如果 Java 通过 SSL 执行某些操作并且它没有远程证书链中的每个证书,那么它绝对会大发雷霆。因此,在 .NET 中,我需要能够指定一个https://资源,并需要它在本地下载整个远程链。

为什么我要自动执行此操作?因为我想让部署变得简单,并且不必执行此操作 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++;
        }
Run Code Online (Sandbox Code Playgroud)

SecurityHelper.DownloadSslCertificate是一种从网站 url 创建 X509Certificate2 的自定义方法。

cert是最低级别的证书,我可以拿回来,但我不能做的是获取链中的所有内容。chain.ChainElements如果机器上尚未安装链的其余部分,则仍然只有 1 层深。我的问题是,这是一个全新的 URL,其证书(对于机器而言)完全未知,我想进入堆栈并递归下载每个证书。

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

编辑:这是我下载 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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

然后为了用这个回调开始验证,我有类似的东西:

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
        }
Run Code Online (Sandbox Code Playgroud)

... ETC

然后到时间AuthenticateAsClient结束时,局部callbackChainElements变量被设置为 的集合X509ChainElement

小智 4

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

其中之一是更改 ServicePointManager 以忽略 SSL 验证(听起来,因为您本地可能没有根证书)。您想要将 ServerCertificateValidationCallback 设置为 true,这告诉它您正在使用自定义验证(或者在您的情况下,可能没有验证)。

从那里开始,你就可以带着链元素完成大部分任务了。您可以使用 Export 方法将证书导出到字节数组中:

foreach (X509ChainElement el in chain.ChainElements) {
 var certToCreate = el.Certificate.Export(X509ContentType.Cert);
  ...
}
Run Code Online (Sandbox Code Playgroud)

从那里开始,只需对字节数组执行您想要的操作即可。您可以将其传递给另一个证书对象的构造函数,然后可以将其导入到 Java 密钥库中。

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

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