支持 HTTPS 的 Httplistener

Posted

技术标签:

【中文标题】支持 HTTPS 的 Httplistener【英文标题】:Httplistener with HTTPS support 【发布时间】:2012-07-09 08:10:06 【问题描述】:

关于使 .NET HTTPListener 支持 HTTPS 的信息似乎很多令人困惑,有时甚至相互冲突。我的理解如下:

一个人的 C# 代码需要一个 https 前缀(例如,https://*:8443),以便侦听器了解它需要在此端口处理 SSL 请求。

实际的 SSL 握手发生在幕后,由 http.sys 处理(埋在 Windows 机器的某个地方)。 C# 代码不必显式管理 SSL 握手,因为它发生在幕后。

需要在httpListener 机器上拥有“X.509 可信证书”,并且该证书需要以某种方式绑定到端口 8443(在本例中)。

我上面的理解正确吗?如果没有,请教育我。

关于 X.509 证书,我的理解是:

使用makecert 创建 X.509 证书。此证书存储在个人存储中,需要转移到受信任的存储(这是 HTTP 侦听器将查看的位置)。看来我可以使用certMgr 来执行移动,或者我可以使用mmc 来实现移动。似乎有不止一种 X.509 证书格式(DERBase64pks、pswd 保护、pks 私有等)...有我应该使用的首选格式吗?李>

将证书放入受信任的存储区后,我需要将其绑定到 TCP 端口。我在 Windows 7 上:我应该使用 httpcfg 还是 netsh

【问题讨论】:

【参考方案1】:

以下命令为 localhost 生成 10 年的自签名证书,将其导入本地计算机存储并在输出中显示 Thumbprint (certhash):

powershell -Command "New-SelfSignedCertificate -DnsName localhost -CertStoreLocation cert:\LocalMachine\My -NotAfter (Get-Date).AddYears(10)"

然后您可以从输出中复制指纹并使用 netsh.exe 将证书附加到 localhost:443,例如:

netsh http add sslcert ipport=localhost:443 certhash=110000000000003ed9cd0c315bbb6dc1c08da5e6 appid=00112233-4455-6677-8899-AABBCCDDEEFF

适用于 Windows 8 或更高版本。需要管理员权限。

【讨论】:

这听起来非常高效和简单。问题 - 什么是 appid? - 我从哪里得到它? @GoguCelMare 这个答案可以帮助你***.com/questions/537173/…【参考方案2】:

我做了很多功课并得到了这个工作。为 .NET HttpListener 添加 SSL 支持的步骤如下:

    更新 C# 应用程序代码以包含 https 前缀。示例:

    String[] prefixes =  "http://*:8089/","https://*:8443/" ;
    

    从代码方面来说就是这样。

    对于证书方面,使用Windows SDK 命令控制台或 Visual Studio Professional 命令控制台

    使用makecert.exe 创建证书颁发机构。示例:

    makecert -n "CN=vMargeCA" -r -sv vMargeCA.pvk vMargeCA.cer
    

    使用makecert.exe 创建 SSL 证书

    makecert -sk vMargeSignedByCA -iv vMargeCA.pvk -n "CN=vMargeSignedByCA" -ic vMargeCA.cer vMargeSignedByCA.cer -sr localmachine -ss My
    

    使用 MMC GUI 在 Trusted Authority 存储中安装 CA

    使用 MMC GUI 在个人存储中安装 SSL 证书

    将证书绑定到IP address:port 和应用程序。示例:

    netsh http add sslcert ipport=0.0.0.0:8443 certhash=585947f104b5bce53239f02d1c6fed06832f47dc appid=df8c8073-5a4b-4810-b469-5975a9c95230
    

    certhash 是您的 SSL 证书的指纹。您可以使用 mmc 找到它。 appid 在 Visual Studio 中找到...通常在 assembly.cs 中,查找 GUID 值。

可能还有其他方法可以完成上述操作,但这对我有用。

【讨论】:

嗯,我尝试按照这些提示做所有事情,但我无法完成最后一步 - 它说某些参数无效...... 我注意到,当我复制并粘贴到命令行时,有时会出现“?”出现在“certhash=”和实际密钥之间。仔细检查您的输入。 有什么方法可以将根 CA 证书链接到中间证书? @WalterKelt 您的回答对我帮助很大,并且几乎填补了现有文档中的所有空白。但是,有一些我必须自己填写,因此我发布了我的分步过程作为答案。干杯! @WalterKelt 只是一个猜测,但可能是可执行项目的属性文件夹中的 AssemblyInfo 文件中的 Guid【参考方案3】:

由于在答案中制作您自己的自签名证书对我不起作用,并且该问题特别要求使 .NET HTTPListener 能够支持 HTTPS 并要求提供任何提示/建议,因此我想分享我的方法。

您需要一个主机名,例如 www.made-up.com,它需要指向您的 WAN IP 地址(例如,向您的主机提供商咨询)并转发其端口,例如443,到您的本地机器。不要忘记在本地计算机的防火墙中打开入站 443 端口。

我使用了https://letsencrypt.org/。在 Windows 上,这不像在 Linux 上那么容易,因为没有任何用于 Windows 的官方 certbot ACME 客户端。但是,您可以使用https://github.com/Lone-Coder/letsencrypt-win-simple,其中也有二进制文件。但是“当前仅支持 IIS”。但是您可以很容易地欺骗它在您的计算机上创建一个证书,以便您可以通过 SSL 方式访问您的 HTTP 侦听器:

    安装 IIS(通过打开/关闭的 Windows 功能),在 IIS 中创建一个网站并分配主机名。还要为它建立一个安全的(443 端口)网站。 运行letsencrypt-win-simple EXE文件(我用的是1.9.1版)。回答问题,让它生成证书。 之后,您可以停止 de IIS 服务器。

我相信您必须注意生成的刷新任务,因为我不确定几个月后它会成功(您可能必须再次启动 IIS 才能更新证书)。

【讨论】:

certbot 可与 cygwin 和 IIS 一起使用,如果您能够添加“。” ->“文本/html”mime 类型。执行“pip3 install certbot”,然后将 Web 根目录挂载到标准 posix 路径,然后运行“certbot certonly”。不是最流畅的,但它确实有效。【参考方案4】:

以下是我在 Windows 上设置独立服务器的详细步骤,使用 OpenSSL 为 C# HTTPListener 应用程序创建自签名证书。它包含大量链接,以供您进一步研究。

    通过HttpListener在.NET中创建一个独立的服务器:

    var prefixes = "http://localhost:8080/app/root", "https://localhost:8443/app/root";
    var listener = new HttpListener();
    foreach (string s in prefixes)
        listener.Prefixes.Add(s);
    listener.Start();
    

    创建自签名证书:*

      openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365,它将提示您在命令行上输入每个证书字段的值。对于通用名称,请输入域名(例如 localhostopenssl pkcs12 -inkey bob_key.pem -in bob_cert.cert -export -out bob_pfx.pfx,以便可以在目标机器上使用其密钥导入。

    *有关使用 makecert 的替代方法,请参阅 Walter 自己的 answer。

    为本地计算机打开证书管理器。当您运行certmgr.msc 时,它会为当前用户 打开证书管理器,这不是我们想要的。而是:

      从目标计算机上的管理命令提示符运行mmcCtrl + M,或单击 文件 > 添加/删除快照-in 选择Certificates,然后点击添加> 在出现的对话框中,选择Computer Account,然后单击下一步 选择Local Computer。点击完成,然后点击Okay

    将证书(pfx)导入目标机器上的Windows Certificate Store

      在之前打开的 mmc 窗口中,向下钻取到 证书(本地计算机) > 个人 右键单击Personal,然后单击所有任务 -> 导入... 在出现的对话框的第二个屏幕中,找到并导入您的证书。您必须将文件类型过滤器更改为 Personal Information ExchangeAll Files 才能找到它 在下一个屏幕上,输入您在步骤 2.1 中选择的密码,并密切注意第一个复选框。这决定了您的证书存储的安全程度以及使用的方便程度 在最后一个屏幕上,选择Place all certificates in the following store。验证它是否显示 Personal,然后单击 FinishTrusted Root Certification Authorities 证书部分重复上述导入过程。

    为您的应用程序创建端口关联。在 Windows Vista 及更高版本上,像我一样使用netsh。 (对于 Windows XP 及更早版本,请使用 httpcfg

    在管理命令行中,键入以下内容以将 SSL binding* 设置到您的应用程序和相应的端口。 注意:这个命令是easy to get wrong,因为(在PowerShell中)大括号必须是escaped。以下 PowerShell 命令将起作用:

    netsh http add sslcert ipport=0.0.0.0:8443 `
        certhash=110000000000003ed9cd0c315bbb6dc1c08da5e6 `
        appid=`00112233-4455-6677-8899-AABBCCDDEEFF`
    

    对于cmd.exe,应使用以下代码:

    netsh http add sslcert ipport=0.0.0.0:8443 certhash=110000000000003ed9cd0c315bbb6dc1c08da5e6 appid=00112233-4455-6677-8899-AABBCCDDEEFF
    
    ipport 参数将导致 SSL 证书绑定到每个网络接口上的端口 8443;要绑定到特定接口(仅),请选择与该网络接口关联的 IP 地址。 certhash 只是证书指纹,去掉了空格 appid 是存储在应用程序的程序集信息中的 GUID。 (旁注:netsh 机制显然是一个 COM 接口,从这个 question 及其答案来看)

    * Microsoft 已将 SSL 绑定 链接从 here 重定向到 there。

    启动您的网络服务器,一切顺利!

【讨论】:

@Jez IIRC,我只使用Trusted Root Certificate Authorities 证书存储没有问题。您的设置是否有什么特别之处需要证书也位于 Personal 存储中? 当我在安装了证书的机器上运行netsh 命令时,出现错误“SSL 证书添加失败,错误 1312 - 指定的登录会话不存在。它可能已经被终止了。”查看这个问题的答案,看起来证书应该在个人商店中通过netsh 安装它(它谈到运行certutilmy 而不是root):***.com/a/19766650/178757 在我的 Windows 上,生成 .pfx 文件的命令使用 Git-(Bash)-for-Windows 挂起。作为解决方案,只需根据openssl-hangs-during-pkcs12-export在命令前添加winpty即可。 感谢您的宝贵时间,但我自己忘记了我什么时候发表评论的,但我赞成您的回答,所以我想您的回答对我有用,谢谢:) 只是为寻找项目 GUID 的未来读者提供的注释,在 Visual Studio 中,某些项目不再具有程序集信息文件。如果是这种情况,您的项目的 GUID 应该在您的解决方案文件中。只需在记事本中打开您的 .sln 文件并查找类似 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 的内容【参考方案5】:

我们可以使用 PowerShell 和 C# 导入证书(无需手动步骤)。

详情见:https://blog.davidchristiansen.com/2016/09/howto-create-self-signed-certificates-with-powershell/

我正在使用此代码:

/// <summary>
/// Create and install a self-signed certificate for HTTPS use
/// </summary>
private static void CreateInstallCert(int expDate, string password, string issuedBy)

    // Create/install certificate
    using (var powerShell = System.Management.Automation.PowerShell.Create())
    
        var notAfter = DateTime.Now.AddYears(expDate).ToLongDateString();
        var assemPath = Assembly.GetCallingAssembly().Location;
        var fileInfo = new FileInfo(assemPath);
        var saveDir = Path.Combine(fileInfo.Directory.FullName, "CertDir");
        if (!Directory.Exists(saveDir))
        
            Directory.CreateDirectory(saveDir);
        

        // This adds certificate to Personal and Intermediate Certification Authority
        var rootAuthorityName = "My-RootAuthority";
        var rootFriendlyName = "My Root Authority";
        var rootAuthorityScript =
            $"$rootAuthority = New-SelfSignedCertificate" +
            $" -DnsName 'rootAuthorityName'" +
            $" -NotAfter 'notAfter'" +
            $" -CertStoreLocation cert:\\LocalMachine\\My" +
            $" -FriendlyName 'rootFriendlyName'" +
            $" -KeyUsage DigitalSignature,CertSign";
        powerShell.AddScript(rootAuthorityScript);

        // Export CRT file
        var rootAuthorityCrtPath = Path.Combine(saveDir, "MyRootAuthority.crt");
        var exportAuthorityCrtScript =
            $"$rootAuthorityPath = 'cert:\\localMachine\\my\\' + $rootAuthority.thumbprint;" +
            $"Export-Certificate" +
            $" -Cert $rootAuthorityPath" +
            $" -FilePath rootAuthorityCrtPath";
        powerShell.AddScript(exportAuthorityCrtScript);

        // Export PFX file
        var rootAuthorityPfxPath = Path.Combine(saveDir, "MyRootAuthority.pfx");
        var exportAuthorityPfxScript =
            $"$pwd = ConvertTo-SecureString -String 'password' -Force -AsPlainText;" +
            $"Export-PfxCertificate" +
            $" -Cert $rootAuthorityPath" +
            $" -FilePath 'rootAuthorityPfxPath'" +
            $" -Password $pwd";
        powerShell.AddScript(exportAuthorityPfxScript);

        // Create the self-signed certificate, signed using the above certificate
        var gatewayAuthorityName = "My-Service";
        var gatewayFriendlyName = "My Service";
        var gatewayAuthorityScript =
            $"$rootcert = ( Get-ChildItem -Path $rootAuthorityPath );" +
            $"$gatewayCert = New-SelfSignedCertificate" +
            $" -DnsName 'gatewayAuthorityName'" +
            $" -NotAfter 'notAfter'" +
            $" -certstorelocation cert:\\localmachine\\my" +
            $" -Signer $rootcert" +
            $" -FriendlyName 'gatewayFriendlyName'" +
            $" -KeyUsage KeyEncipherment,DigitalSignature";
        powerShell.AddScript(gatewayAuthorityScript);

        // Export new certificate public key as a CRT file
        var myGatewayCrtPath = Path.Combine(saveDir, "MyGatewayAuthority.crt");
        var exportCrtScript =
            $"$gatewayCertPath = 'cert:\\localMachine\\my\\' + $gatewayCert.thumbprint;" +
            $"Export-Certificate" +
            $" -Cert $gatewayCertPath" +
            $" -FilePath myGatewayCrtPath";
        powerShell.AddScript(exportCrtScript);

        // Export the new certificate as a PFX file
        var myGatewayPfxPath = Path.Combine(saveDir, "MyGatewayAuthority.pfx");
        var exportPfxScript =
            $"Export-PfxCertificate" +
            $" -Cert $gatewayCertPath" +
            $" -FilePath myGatewayPfxPath" +
            $" -Password $pwd"; // Use the previous password
        powerShell.AddScript(exportPfxScript);

        powerShell.Invoke();
    

需要 PowerShell 4 或更高版本。

【讨论】:

我的 VS 中缺少 Path.Combine 和 System.Management

以上是关于支持 HTTPS 的 Httplistener的主要内容,如果未能解决你的问题,请参考以下文章

支持 HTTPS 的 Httplistener

curl不能支持https问题

iOS支持Https

curl不能支持https问题

为啥 libevent 不包含内置的 https 支持?

使用 ELB 支持双向 TLS/HTTPS