SSL 与自托管 WCF 服务
一篇关于证书和 WCF 的有时晦涩难懂的文章
引言
要在客户端和服务器之间执行任何类型的 SSL 加密,必须有证书。对于初学者来说,证书可能有点晦涩难懂,尤其是当它们与一些奇怪的 WCF 配置设置混在一起时,但别担心,一切尽在这里。
我写这篇文章(我的第一篇)的原因是,我非常努力地才让它奏效,而我需要的信息散落在各个地方。我还在运行时遇到了很多错误,而这些错误实际上是由过程中更早的设置问题引起的。非常非常令人恼火。希望这能为某人省去一些麻烦!
我假设您熟悉 WCF 的基础知识,并且熟悉配置文件等。我没有包含任何源代码,因为服务本身实际上并不重要,SSL 的关键在于设置。
证书
本示例假设您(和我一样)没有权限访问漂亮闪亮的证书颁发机构 (CA),并且需要使用 makecert 工具。(稀疏的信息可以在 这里 找到。)注意: 我最近发现了 这个 很棒的网站,一个免费的 CA,谁能想到呢。
您需要一个证书 (cert) 来充当您的根颁发机构,还有一个证书来充当实际用于 SSL 的证书,该证书需要由您的根颁发机构签名。如果您不设置根颁发机构,您的单个证书将不被信任,您将在事后很长一段时间通过一系列极其令人恼火的 WCF 异常来发现这一点。
以下命令(在 Visual Studio 命令提示符下运行)将创建您的根证书,不,这不是我自己想出来的,您可以在各处找到一些很好的示例,例如 这里)。
makecert -sv SignRoot.pvk -cy authority -r signroot.cer -a
sha1 -n "CN=Dev Certification Authority" -ss my -sr localmachine
请查看上面的链接,了解这些参数的含义,这并不非常重要,但知道一下也很好。
运行此命令并成功后,您需要将此证书设为受信任的颁发机构。您可以通过 MMC 管理单元控制台来完成此操作。转到运行窗口并输入“mmc”,然后按 Enter。然后在打开的窗口(称为“Microsoft 管理控制台”,如果你关心的话)中执行以下操作。
文件 -> 添加/删除管理单元 -> 添加… -> 双击证书 -> 选择计算机帐户并单击下一步 -> 完成 -> 关闭 -> 确定
然后选择“证书(本地计算机)” ->“个人” ->“证书”节点。

您应该会看到一个名为“Dev Certificate Authority”(或您决定的任何其他名称)的证书。将此证书从当前节点移动到“证书(本地计算机)” ->“受信任的根证书颁发机构” ->“证书”节点,拖放即可轻松完成。

现在您拥有的**不是**您需要的证书 :)
不过,您已经能够创建受信任的证书了,这很好。
现在您必须创建另一个证书,也就是您实际上要使用的证书。
再次运行 makecert,但按如下方式运行...
makecert -iv SignRoot.pvk -ic signroot.cer -cy end -pe -n
CN="localhost" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr
localmachine -sky exchange -sp
"Microsoft RSA SChannel Cryptographic Provider" -sy 12
请注意,您正在使用第一个证书作为最新证书的颁发者。这很重要…在我写 localhost
的地方,您需要填写您机器的 DNS 名称。换句话说,如果您部署服务的方式是其终结点读取 http://bob:10010/Service,那么该名称就是 bob。此外,您还需要为需要运行的每个主机执行此操作(是的,一个用于 bob,另一个用于 localhost
)。
通过双击证书(选择“证书(本地计算机)”‘“个人”‘“证书”)来获取证书的签名,打开详细信息选项卡,然后滚动到“指纹”选项。

选择指纹并复制。将其粘贴到记事本或其他文本编辑器中,并删除空格。保留它。(更多关于如何执行此操作的信息可以在 这里 查看,尽管此链接有点不必要。)
您的证书已设置完毕。
配置带 SSL 证书的端口
现在您可以使用另一个有趣的工具 httpcfg。(有关更多信息,请参阅 这里。)首先运行以下命令,以检查您想要的端口上没有任何正在运行的服务。
httpcfg query ssl

请注意,有两个端口,一个将用于 chrise 的请求,一个将用于 localhost,请跟踪哪个是哪个。
如果您是第一次执行此操作,它应该只返回一个换行符。如果确切的 IP 地址已经设置了 SSL(或者以后您需要删除任何错误),您可以使用以下命令,其中 IP 地址和端口显示为上一个查询的结果。
httpcfg delete ssl -i 0.0.0.0:8888
在完成对以上两个命令的修改后,运行以下命令,将给定的证书(从而允许非对称加密)与给定的端口关联起来。
请注意,IP 地址 0.0.0.0
是一个特殊的地址,表示此 PC 上的所有 IP 地址。
httpcfg set ssl -i 0.0.0.0:8012 -h abababababababababababababab
其中最后一个部分 (ab^n) 是您之前保留的指纹(您保留了吗?)。好了,您现在拥有一个启用了 SSL 的端口。
WCF服务
终于!
WCF 服务中用于 SSL 的大部分工作都在配置中。我个人更喜欢配置文件,但这也可以在代码中完成。
服务配置需要如下所示
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="NewBehavior">
<serviceMetadata httpsGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="Binding">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="NewBehavior"
name="TestWCF.ServiceImplementation.TestWCFService">
<endpoint
address="https://chrise:10081/TestWCFService"
binding="basicHttpBinding"
bindingConfiguration="Binding"
name="TestWCFService.Http"
contract="TestWCF.ServiceContracts.ITestWCFService" />
<host>
<baseAddresses>
<add baseAddress="https://chrise:10081/TestWCFService" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
请注意,httpsGetEnabled
设置为 true
。这意味着即使是您的 mex 也是加密的,很酷!另请注意,chrise
(在这种情况下为主机名)**必须**有相应的证书,否则无效。
我使用的是 basic HTTP 绑定。您也可以使用 wsHTTP,但如果整个传输都已加密,那么对消息进行加密有什么意义呢?
请注意,clientCredentialType
设置为 none,这只是为了简化整个解决方案,否则我们将进入另一篇文章。
客户端如下所示
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="TestWCFService.Http">
<security mode="Transport">
<transport clientCredentialType="None"
proxyCredentialType="None"
realm="" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address=https://chrise:10081/TestWCFService
binding="basicHttpBinding"
bindingConfiguration="TestWCFService.Http"
contract="TestWCF.Client.TestWCFService.ITestWCFService"
name="TestWCFService.WSHttp" />
</client>
</system.serviceModel>
这样应该就可以工作了!
我遇到的一些异常
首先,这只能在您的本地计算机上工作。为什么?因为世界上没有其他计算机愚蠢到会信任您的个人证书。为了让您的客户端在另一台 PC 上正常工作,您需要导出您的颁发机构证书和 SSL 证书,然后将它们导入到客户端的 PC 上。这就是为什么 makecert **不适合生产环境**。各位,请使用真正的 CA。
其次,当服务运行时,您应该能够浏览到终结点。如果您这样做时,出现一个提示您有关证书可信度的弹出窗口,那么您的证书不被信任,客户端将无法与服务器通信。