18

我需要在使用 TCP/IP 套接字进行通信的各种进程之间提供安全通信。我想要身份验证和加密。而不是重新发明轮子,我真的很想使用 SSL 和 SslStream 类和自签名证书。我想要做的是根据我本地应用程序中的已知副本验证远程进程的证书。(不需要证书颁发机构,因为我打算手动复制证书)。

为此,我希望应用程序能够在第一次运行时自动生成新证书。除了 makecert.exe,看起来这个链接显示了一种自动生成自签名证书的方法,所以这是一个开始。

我查看了 SslStream 的 AuthenticateAsServer 和 AuthenticateAsClient 方法。您可以提供回调以进行验证,因此看起来是可能的。但现在我了解了它的细节,我真的认为不可能做到这一点。

我是否朝着正确的方向前进?有更好的选择吗?以前有没有人做过这样的事情(基本上是对等 SSL 而不是客户端服务器)?

4

3 回答 3

29

第 1 步:生成自签名证书:

  • 我下载了Doug Cook 发布的Certificate.cs 类
  • 我使用此代码生成 .pfx 证书文件:

    byte[] c = Certificate.CreateSelfSignCertificatePfx(
            "CN=yourhostname.com", //host name
            DateTime.Parse("2000-01-01"), //not valid before
            DateTime.Parse("2010-01-01"), //not valid after
            "mypassword"); //password to encrypt key file
    
        using (BinaryWriter binWriter = new BinaryWriter(
            File.Open(@"testcert.pfx", FileMode.Create)))
        {
            binWriter.Write(c);
        }
    

第 2 步:加载证书

    X509Certificate cert = new X509Certificate2(
                            @"testcert.pfx", 
                            "mypassword");

第3步:把它放在一起

  • 我基于这个非常简单的 SslStream 示例
  • 您将收到有关 SslProtocolType 枚举的编译时错误。只需将其从 SslProtocolType.Default 更改为 SslProtocols.Default
  • 有 3 条关于已弃用功能的警告。我用建议的替代品替换了它们。
  • 我将 Server Program.cs 文件中的这一行替换为步骤 2 中的行:

    X509Certificate 证书 = getServerCert();

  • 在 Client Program.cs 文件中,确保您设置 serverName = yourhostname.com(并且它与证书中的名称匹配)

  • 在 Client Program.cs 中,CertificateValidationCallback 函数失败,因为 sslPolicyErrors 包含 RemoteCertificateChainErrors。如果你再深入一点,这是因为签署证书的颁发机构不是受信任的根。
  • 我不想让用户将证书导入根存储等,所以我为此做了一个特殊情况,我检查了 certificate.GetPublicKeyString() 是否等于我存档的公钥对于该服务器。如果匹配,我从该函数返回 True。这似乎行得通。

第 4 步:客户端身份验证

这是我的客户端进行身份验证的方式(它与服务器有点不同):

TcpClient client = new TcpClient();
client.Connect(hostName, port);

SslStream sslStream = new SslStream(client.GetStream(), false,
    new RemoteCertificateValidationCallback(CertificateValidationCallback),
    new LocalCertificateSelectionCallback(CertificateSelectionCallback));

bool authenticationPassed = true;
try
{
    string serverName = System.Environment.MachineName;

    X509Certificate cert = GetServerCert(SERVER_CERT_FILENAME, SERVER_CERT_PASSWORD);
    X509CertificateCollection certs = new X509CertificateCollection();
    certs.Add(cert);

    sslStream.AuthenticateAsClient(
        serverName,
        certs,
        SslProtocols.Default,
        false); // check cert revokation
}
catch (AuthenticationException)
{
    authenticationPassed = false;
}
if (authenticationPassed)
{
    //do stuff
}

CertificateValidationCallback 与服务器案例中的相同,但请注意 AuthenticateAsClient 如何获取一组证书,而不仅仅是一个证书。所以,你必须像这样添加一个 LocalCertificateSelectionCallback(在这种情况下,我只有一个客户端证书,所以我只返回集合中的第一个):

static X509Certificate CertificateSelectionCallback(object sender,
    string targetHost,
    X509CertificateCollection localCertificates,
    X509Certificate remoteCertificate,
    string[] acceptableIssuers)
{
    return localCertificates[0];
}
于 2009-03-31T03:42:31.553 回答
2

你也可以看看这个示例异步 SslStream 客户端/服务器实现示例 http://blogs.msdn.com/joncole/archive/2007/06/13/sample-asynchronous-sslstream-client-server-implementation.aspx

如果证书未正确生成,您可以获得异常服务器模式 SSL 必须使用具有关联私钥的证书。

基本证书示例

makecert -sr LocalMachine -ss My -n CN=Test -sky exchange -sk 123456

或者

作为外部文件

makecert -sr LocalMachine -ss My -n CN=Test -sky exchange -sk 123456 c:\Test.cer

证书创建工具 (Makecert.exe)
http://msdn.microsoft.com/en-us/library/bfsktky3%28VS.80%29.aspx

于 2010-04-26T12:10:18.767 回答
1

您提出的建议对我来说听起来不错,只是听起来您希望等到调用回调以生成证书。我不认为那会飞。AFAIK,您必须在调用时提供有效证书AuthenticateAsX

但是,这些类是可覆盖的;所以理论上,你可以创建一个派生类,它首先检查是否需要生成证书,如果需要生成它,然后调用父AuthenticateAsX方法。

于 2009-03-30T02:11:47.473 回答