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

标签 c# certificate x509certificate x509certificate2

我正在构建一些 .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++;
        }

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

cert 是最低级别的证书,我可以取回它,但我不能做的是获取链中的所有内容。如果链的其余部分尚未安装在机器上,chain.ChainElements 仍然只有一层深。我的问题是这是一个全新的 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;
        }
    }

编辑 #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;
 }

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

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 以忽略 SSL 验证(因为您可能没有本地根证书,听起来)。您要将 ServerCertificateValidationCallback 设置为 true,这告诉它您正在使用自定义验证(或者在您的情况下,可能没有验证)。

从那里开始,您已经完成了链元件的大部分工作。您可以使用 Export 方法将证书导出到字节数组中:

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

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

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

编辑:实际上,我刚刚在 tips working with X.509 certificates in .NET 上注意到这篇帖子.作者建议不要直接从字节数组写入,因为它会将临时文件写入未清理的目录。所以他的建议是先把字节数组写入磁盘,写完了再手动清理。

关于c# - 从远程主机自动下载 X509 证书链,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24618798/

相关文章:

java - 在运行时定期更改 SSL 证书

python - Jupyter Notebook安装Python3.5内核时,确认ssl证书问题

c# - Bouncy CaSTLe X509 Bind to Port Error 指定的登录 session 不存在。它可能已经被终止

创建新的 SqlDataAdapter 时出现 C# InvalidOperationException

java - 在 Java 中读取/加载 jks 证书文件返回空别名

java - 使用 Java 从证书别名到包含私钥的 PEM 文件

java - Java 流 allMatch() 中无法比较的类型捕获和 int

c# - 删除 cookie 名称中的 .AspNet 前缀

c# - TPL 数据流加速?

c# - 如何在已编译的 Linq to SQL 查询中使用返回 IQueryable<T> 的方法而不会出错