zoukankan      html  css  js  c++  java
  • SSL编程(3).NET实现SSL服务端

     

    准备开发用数字证书

    一般学习和开发调试场合,不会随便使用正式的SSL服务器证书的私钥。由于服务器验证对于SSL来说是必须的,SSL服务器端必须有拥有一个服务器 证书,即能够访问到证书的私钥。对于要求客户端验证的SSL,对客户端有着同样的要求,客户端需要拥有与自己声称的身份对应的数字证书。

    Windows SDK中有一个制作测试开发用的临时数字证书的命令行工具:makecert.exe。这一工具也被包含在Visual Studio中。打开SDK或者Visual Studio的命令行提示窗口,输入如下的命令:

    makecert –ss “MY”

    会在当前用户的个人证书存储中创建一个新的数字证书,证书用途有“所有”(All),这种证书既能够用于服务器验证,又能够用于用户验证。下图中名 为Joe’s-Software-Emporium的证书就是makecert命令所生成的,我们随后把它用于服务端的身份验证。我们再创建一个名为 Test2的证书,这个证书将会在后面用于客户端验证。生成名称为Test2的证书命令如下:

    Makecert -n “CN=Test2” -ss “MY”

    myMakeCerts

    到这里,SSL服务器端和客户端两边的证书就都准备好了。需要注意的是,这两个证书目前都没有得到系统的信任,下面的编程调试过程中,我们将会讨论对证书信任的处理。

    SSL服务端实现

    Ssl服务端示例1的功能是在端口443等待客户端的SSL握手请求,SSL握手成功后,接收客户端的数据,然后给客户端发送一段应答数据。

    首先是实现TCP服务端,使用一个TcpListener对象启动侦听,等待连接,接受连接获得一个与客户端对等的TcpClient对象。这个比TCP客户端稍微要复杂一点儿。这里不详述,不清楚的读者可以阅读Tcp服务端编程相关的参考资料。代码片段如下:

    TcpListener listener = new TcpListener(IPAddress.Any, 443);

    listener.Start();

    while (true)

                {

    Console.WriteLine("Waiting for a client to connect...");

    // 应用程序会阻塞在这里,直到有一个客户端发起连接.

    TcpClient client = listener.AcceptTcpClient();

               ProcessClient(client);

                }

    代码运行到ProcessClient时,服务端已经有了由一个TcpClient对象代表的Tcp连接。到这里,在网络通信层面上,服务端与客户 端成为对等的。我们使用从服务器端的TcpClient对象的IO流构造一个SslStream对象,处理SSL协议。服务端与客户端的差异在于服务器端 要调用AuthenticateAsServer函数,把自己设定为SSL服务端模式,并进入等待对端作为客户端发起SSL握手。 AuthenticateAsServer函数必须有一个服务器证书作为输入,加载名为Joe's-Software-Emporium的服务器证书代码 片段如下:

    X509Store store = new X509Store("MY", StoreLocation.CurrentUser );
    store.Open(OpenFlags.ReadOnly);
    X509Certificate2Collection storecollection = (X509Certificate2Collection)store.Certificates.Find(X509FindType.FindBySubjectName, @"Joe's-Software-Emporium",false);
    serverCertificate = storecollection[0];


    把获得的serverCertificate对象作为服务器证书输入,启动SSL服务端:

    SslStream sslStream = new SslStream(

               client.GetStream(), false,

    new RemoteCertificateValidationCallback(ValidateClientCertificate));

    // Authenticate the server but don't require the client to authenticate.

    try

                {

                     sslStream.AuthenticateAsServer(serverCertificate,

                                                                     false, // 这个参数决定是否需要客户端出示数字证书对客户端身份进行验证.

                                                                     SslProtocols.Tls, false);

                 // Display the properties and settings for the authenticated stream.

    建立连接后,服务器调用SslStream的Read, Write函数,进行数据的安全收发处理。

    SSL连接测试

    我们仍然使用前面的简单SSL客户端示例1,修改目标地址和端口连接SSL服务端示例1。客户端立即报告服务端出示的证书无效,结束了SSL握手。

    代码执行情况如下:

    image

    程序输出是:

    image

    如果我们强行让客户端负责证书检验的函数ValidateServerCertificate返回true的话,SSL握手能够完成,后面的加密数据收发也能进行。但是这样做意味着客户端会接受任何服务器证书,这样的ssl客户端程序对ssl中间人攻击处于不设防状态。 我们不打算在这里提供这种糟糕的示例,性急的读者可以自己改,把上图中断点处代码直接改成return true,就完事了。需要切记,那样的ValidateServerCertificate代码只能用于SSL编程学习玩玩,决不能用于任何正式产品之 中!或者,开发者强行让计算机信任签发测试证书的CA,也能让客户端示例1完成SSL握手;但是这样意味着系统信任了一个测试用CA,这会危及整个计算机 的公钥信任,我们在这里也不这么做。

    尽管最简单的SSL客户端示例1无法连接这个服务端,但是对于不要求客户端验证的SSL服务端,这个服务端代码已经完整了。如果服务器端加载的是一个由Verisign这样的公众信任的CA签发的有效服务器证书,客户端示例1将能够正常连接并完成数据的加密收发。

    由于多数读者不会有这样一个服务器证书,客户端示例1这样的,只信任系统信任的证书的安全SSL客户端,会拒绝与使用测试用证书的SSL服务端建立SSL连接。

  • 相关阅读:
    LeetCode "Median of Two Sorted Arrays"
    LeetCode "Distinct Subsequences"
    LeetCode "Permutation Sequence"

    LeetCode "Linked List Cycle II"
    LeetCode "Best Time to Buy and Sell Stock III"
    LeetCode "4Sum"
    LeetCode "3Sum closest"
    LeetCode "3Sum"
    LeetCode "Container With Most Water"
  • 原文地址:https://www.cnblogs.com/cioliuguilan/p/5593020.html
Copyright © 2011-2022 走看看