关于使.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证书格式(DER
、Base64
、pks
、pswd protected、pks
private等)……有我应该使用的首选格式吗
一旦我将证书放入受信任的存储,我需要将其绑定到TCP端口。我在Windows上;7:我应该使用httpcfg
还是netsh
?
我做了一堆家庭作业,并完成了这项工作。为.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在受信任的授权存储中安装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找到它。在Visual Studio中找到appid。。。通常在assembly.cs中,查找GUID值。
-
可能还有其他方法可以实现上述目标,但这对我来说很有效。
以下是我在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
,它将在命令行上提示您输入每个证书字段的值。对于通用名称,请键入域名(例如localhost
) -
openssl pkcs12 -inkey bob_key.pem -in bob_cert.cert -export -out bob_pfx.pfx
,以便可以将其密钥导入到目标机器上。
*关于使用makecert
的替代方案,请参阅Walter自己的答案。
-
打开本地计算机的证书管理器。当您运行
certmgr.msc
时,它会打开当前用户的证书管理器,这不是我们想要的。相反,运行certlm.msc
[谢谢,@Arkane],如果这不起作用,那么:- 在目标计算机上的管理命令提示符下,运行
mmc
- 按Ctrl+1,或单击2文件加/删除管理单元
- 选择
Certificates
,然后单击添加> - 在出现的对话框中,选择
Computer Account
,然后单击下一步 - 选择
Local Computer
。单击"完成",然后单击"确定"
- 在目标计算机上的管理命令提示符下,运行
-
将证书(
pfx
)导入目标计算机上的Windows证书存储 -
在先前打开的
mmc
窗口中,深入到证书(本地计算机)>个人 -
右键单击
Personal
,然后单击所有任务->导入 -
在出现的对话框的第二个屏幕中,查找并导入您的证书。您必须将文件类型筛选器更改为
Personal Information Exchange
或All Files
才能找到 -
在下一个屏幕上,输入您在步骤2.1中选择的密码,并密切注意第一个复选框。这决定了证书存储的安全性,以及使用的方便性
-
在最后一个屏幕上,选择
Place all certificates in the following store
。验证它是否显示Personal
,然后单击完成 -
对
Trusted Root Certification Authorities
证书部分重复上面的导入过程。 -
为应用程序创建端口关联。在WindowsVista及更高版本上,和我一样使用
netsh
。(对于Windows XP及更早版本,请使用httpcfg
)
-
在管理命令行中,键入以下内容以设置到应用程序的SSL绑定*以及相应的端口。注意:此命令很容易出错,因为(在PowerShell中)需要转义大括号。以下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接口)
*Microsoft已将SSL绑定链接从这里重定向到那里。
- 启动你的网络服务器,你就可以开始了
我们可以使用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或更高版本。
以下命令为localhost生成10年的自签名证书,将其导入本地计算机存储,并在输出中显示指纹(certhash):
powershell -Command "New-SelfSignedCertificate -DnsName localhost -CertStoreLocation cert:LocalMachineMy -NotAfter (Get-Date).AddYears(10)"
然后,您可以从输出中复制Thumbprint,并使用netsh.exe将证书附加到localhost:443,例如:
netsh http add sslcert ipport=localhost:443 certhash=110000000000003ed9cd0c315bbb6dc1c08da5e6 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
适用于Windows 8或更高版本。需要管理员权限。
由于在答案中制作自己的自签名证书对我不起作用,而且这个问题特别要求制作支持HTTPS的.NET HTTPListener,并询问任何提示/建议,我想分享我的方法。
您需要一个主机名,比如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简单EXE文件(我使用的是1.9.1版本)。回答问题让它生成证书
- 之后,您可以停止反IIS服务器
我相信您必须注意生成的刷新任务,因为我不确定它在几个月后是否会成功(您可能必须重新启动IIS才能续订证书)。