首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >无法让C# SslStream使用我的X509证书

无法让C# SslStream使用我的X509证书
EN

Stack Overflow用户
提问于 2020-04-05 06:30:59
回答 1查看 1.2K关注 0票数 0

我对安全和加密有点陌生,所以如果我只是犯了一个非常愚蠢的错误,提前道歉。我需要一个服务器和一个客户端通过使用SslStream的安全连接进行通信。但我的证书不起作用。我得到了以下错误:System.NotSupportedException: 'The server mode SSL must use a certificate with the associated private key.',我的代码是在文档中给出的微软示例:https://learn.microsoft.com/en-us/dotnet/api/system.net.security.sslstream?view=netframework-4.8ss

我试过:

使用makecert (如本文所示:SSLStream example - how do I get certificates that work?)

  • Certificates with OpenSSL

除了最后一个例外,所有这些都给了System.NotSupportedException: 'The server mode SSL must use a certificate with the associated private key.'异常。这是否意味着自签证书不起作用?我需要买证书吗?

编辑:这是我使用的代码。它是修改过的示例(抱歉,如果我的示例编码得很糟糕),并且是可执行的,模拟服务器和客户端,并抛出异常:

代码语言:javascript
运行
复制
class Program
{

    static void Main(string[] args)
    {
        //Temporarily added the arguments here for you to see
        args = new string[2] { @"C:\Users\jacke\Documents\CA\TempCert.cer", "FakeServerName" };
        Console.WriteLine("Starting server in seperate thread...");
        Task t = Task.Run(() => { Server.Initialize(args[0]); });
        Task.Delay(500).Wait();
        Client.RunClient(args[1]);
    }
}

public static class Server
{
    private static X509Certificate cert;
    private static TcpListener server;

    public static void Initialize(string certificate)
    {
        cert = X509Certificate.CreateFromCertFile(certificate);
        server = new TcpListener(IPAddress.Any, 12321);
        server.Start();
        while (true)
        {
            Console.WriteLine("Waiting for a client to connect...");

            TcpClient client = server.AcceptTcpClient();
            ProcessClient(client);
        }
    }


    private static void ProcessClient(TcpClient client)
    {

        SslStream sslStream = new SslStream(client.GetStream(), false);
        try
        {
            sslStream.AuthenticateAsServer(cert, clientCertificateRequired: false, checkCertificateRevocation: true);
            sslStream.ReadTimeout = 5000;
            sslStream.WriteTimeout = 5000;   
            Console.WriteLine("Waiting for client message...");
            string messageData = Helpers.ReadMessage(sslStream);
            byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");
            Console.WriteLine("Sending hello message.");
            sslStream.Write(message);
        }
        catch (AuthenticationException e)
        {
            Console.WriteLine("Exception: {0}", e.Message);
            if (e.InnerException != null)
            {
                Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
            }
            Console.WriteLine("Authentication failed - closing the connection.");
            sslStream.Close();
            client.Close();
            return;
        }
        finally
        {                
            sslStream.Close();
            client.Close();
        }
    }



}

public static class Client
{
    private static Hashtable certificateErrors = new Hashtable();
    public static bool ValidateServerCertificate(
          object sender,
          X509Certificate certificate,
          X509Chain chain,
          SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;

        Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
        return false;
    }


    public static void RunClient(string serverName)
    {
        TcpClient client = new TcpClient("localhost", 12321);
        Console.WriteLine("Client connected.");
        SslStream sslStream = new SslStream(
            client.GetStream(),
            false,
            new RemoteCertificateValidationCallback(ValidateServerCertificate),
            null
            );
        try
        {
            sslStream.AuthenticateAsClient(serverName);
        }
        catch (AuthenticationException e)
        {
            Console.WriteLine("Exception: {0}", e.Message);
            if (e.InnerException != null)
            {
                Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
            }
            Console.WriteLine("Authentication failed - closing the connection.");
            client.Close();
            return;
        }
        byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
        sslStream.Write(messsage);
        string serverMessage = Helpers.ReadMessage(sslStream);
        Console.WriteLine("Server says: {0}", serverMessage);

        client.Close();
        Console.WriteLine("Client closed.");
    }


}

public static class Helpers
{
    public static string ReadMessage(SslStream sslStream)
    {
        // Read the  message sent by the server.
        // The end of the message is signaled using the
        // "<EOF>" marker.
        byte[] buffer = new byte[2048];
        StringBuilder messageData = new StringBuilder();
        int bytes = -1;
        do
        {
            bytes = sslStream.Read(buffer, 0, buffer.Length);
            Decoder decoder = Encoding.UTF8.GetDecoder();
            char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
            decoder.GetChars(buffer, 0, bytes, chars, 0);
            messageData.Append(chars);
            // Check for EOF.
            if (messageData.ToString().IndexOf("<EOF>") != -1)
            {
                break;
            }
        } while (bytes != 0);

        return messageData.ToString();
    }
}

下面是我如何创建证书的方法(如我在上面链接的帖子中所解释的):

代码语言:javascript
运行
复制
makecert -sv RootCATest.pvk -r -n "CN=FakeServerName" RootCATest.cer
makecert -ic RootCATest.cer -iv RootCATest.pvk -n "CN=FakeServerName" -sv
TempCert.pvk -pe -sky exchange TempCert.cer
cert2spc TempCert.cer TempCert.spc
pvkimprt -pfx TempCert.spc TempCert.pvk

我在上面的命令中输入的其他信息:

  • 被问到前2条命令密码时,我留下了空白的
  • ,我检查了导出私钥,并将'A‘设置为最后一个命令

的密码。

然后,我在本地证书存储中导入了.pfx文件(之前我也尝试过计算机范围),让程序选择正确的存储。它警告我,CA的所有证书都是可信的,我应该联系CA检查这确实是他们的证书,但我继续。然后我运行了代码(使用我刚刚创建的'TempCert.cer‘文件)并得到了错误。任何建议都是非常感谢的!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-04-05 11:33:07

如果我没有记错,.cer文件不包含私钥,并且我假设通过从文件中加载证书而不会检查证书存储。您可以导出一个.pfx并在导出中包含私钥。

我不知道为什么.pvk尝试失败(我自己也从未尝试过这种格式)-- SslStream绝对可以处理自签名证书;客户端只需忽略验证失败。

但是,如果您已经将其导入商店,则应该能够直接加载它:

代码语言:javascript
运行
复制
using System.Security.Cryptography.X509Certificates;

// ...

using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
    store.Open(OpenFlags.ReadOnly);
    var certs = store.Certificates.Find(X509FindType.FindBySubjectName, "FakeServerName", false);
    return new X509Certificate2(certs[0]);
}

回复您的评论中的错误:

提供给包的凭据未被识别。

我不知道我是否收到了确切的信息,但我确实记得,我必须授予应用程序所遇到的用户访问权限。您必须打开正确的证书工具:

https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-view-certificates-with-the-mmc-snap-in (运行mmc,选择文件/添加插件,选择证书)

然后授予用户对私钥的访问权限:

https://docs.secureauth.com/display/KBA/Grant+Permission+to+Use+Signing+Certificate+Private+Key (右键单击证书,选择所有任务/管理私钥)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61038857

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档