如何为WCF应用添加X509证书和安全验证
一、首先用Visual Studio 命令提示创建证书
makecert -sr LocalMachine -ss My -a sha1 -n CN=Webabcd -sky exchange -pe
certmgr -add -r LocalMachine -s My -c -n Webabcd -s TrustedPeople
在运行处 输入CertMgr.msc 打开证书管理界面就能找到我们刚才创建的证书
证书参数详情请参考 :http://msdn.microsoft.com/zh-cn/library/bfsktky3.aspx
二、为WCF服务添加自定义验证 (添加一个继承UserNamePasswordValidator的类并重写Validate)
/// 自定义的用户名/密码验证类
/// </summary>
public class CustomNamePasswordValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
{
/// <summary>
/// 验证指定的用户名和密码
/// </summary>
/// <param name="userName">要验证的用户名</param>
/// <param name="password">要验证的密码</param>
public override void Validate(string userName, string password)
{
if (!(userName == "webabcd" && password == "webabcd"))
{
throw new FaultException("用户名或密码不正确");
}
}
}
/// </summary>
public class CustomNamePasswordValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
{
/// <summary>
/// 验证指定的用户名和密码
/// </summary>
/// <param name="userName">要验证的用户名</param>
/// <param name="password">要验证的密码</param>
public override void Validate(string userName, string password)
{
if (!(userName == "webabcd" && password == "webabcd"))
{
throw new FaultException("用户名或密码不正确");
}
}
}
三、为WCF宿主添加如下Config配置
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<!--name - 提供服务的类名-->
<!--behaviorConfiguration - 指定相关的行为配置-->
<service name="WCF.ServiceLib.Security.Hello" behaviorConfiguration="SecurityBehavior">
<!--address - 服务地址-->
<!--binding - 通信方式-->
<!--contract - 服务契约-->
<endpoint address="" binding="wsHttpBinding" contract="WCF.ServiceLib.Security.IHello" bindingConfiguration="SecurityBindingConfiguration" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SecurityBehavior">
<!--httpGetEnabled - 指示是否发布服务元数据以便使用 HTTP/GET 请求进行检索,如果发布 WSDL,则为 true,否则为 false,默认值为 false-->
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<!--userNamePasswordValidationMode - 以用户名/密码模式来进行验证的方法-->
<!--UserNamePasswordValidationMode.Windows - 用户名映射到 Windows 用户-->
<!--UserNamePasswordValidationMode.MembershipProvider - 提供基于已配置的 MembershipProvider 的密码验证-->
<!--UserNamePasswordValidationMode.Custom - 基于已配置的自定义 UsernamePasswordValidator 的自定义身份验证-->
<!--customUserNamePasswordValidatorType - 所使用的自定义用户名密码验证程序的类型-->
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCF.ServiceLib.Security.CustomNamePasswordValidator, WCF.ServiceLib" />
<!--findValue - 指定要在 X.509 证书存储区中搜索的值-->
<!--storeLocation - 指定客户端可用于验证服务器证书的证书存储区位置(LocalMachine - 分配给本地计算机的 X.509 证书存储区;CurrentUser - 当前用户使用的 X.509 证书存储区)-->
<!--storeName - 要打开的 X.509 证书存储区的名称(参看:StoreName枚举。AddressBook, AuthRoot, CertificateAuthority, Disallowed, My, Root, TrustedPeople, TrustedPublisher)-->
<!--x509FindType - 要执行的 X.509 搜索的类型(参看:X509FindType枚举)-->
<serviceCertificate findValue="Webabcd" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="SecurityBindingConfiguration">
<security>
<!--clientCredentialType - 客户端用以进行身份验证的凭据的类型,默认值 UserName -->
<!--BasicHttpMessageCredentialType.UserName - 使用用户名凭据对客户端进行身份验证-->
<!--BasicHttpMessageCredentialType.Certificate - 使用证书对客户端进行身份验证-->
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
<configuration>
<system.serviceModel>
<services>
<!--name - 提供服务的类名-->
<!--behaviorConfiguration - 指定相关的行为配置-->
<service name="WCF.ServiceLib.Security.Hello" behaviorConfiguration="SecurityBehavior">
<!--address - 服务地址-->
<!--binding - 通信方式-->
<!--contract - 服务契约-->
<endpoint address="" binding="wsHttpBinding" contract="WCF.ServiceLib.Security.IHello" bindingConfiguration="SecurityBindingConfiguration" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SecurityBehavior">
<!--httpGetEnabled - 指示是否发布服务元数据以便使用 HTTP/GET 请求进行检索,如果发布 WSDL,则为 true,否则为 false,默认值为 false-->
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<!--userNamePasswordValidationMode - 以用户名/密码模式来进行验证的方法-->
<!--UserNamePasswordValidationMode.Windows - 用户名映射到 Windows 用户-->
<!--UserNamePasswordValidationMode.MembershipProvider - 提供基于已配置的 MembershipProvider 的密码验证-->
<!--UserNamePasswordValidationMode.Custom - 基于已配置的自定义 UsernamePasswordValidator 的自定义身份验证-->
<!--customUserNamePasswordValidatorType - 所使用的自定义用户名密码验证程序的类型-->
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCF.ServiceLib.Security.CustomNamePasswordValidator, WCF.ServiceLib" />
<!--findValue - 指定要在 X.509 证书存储区中搜索的值-->
<!--storeLocation - 指定客户端可用于验证服务器证书的证书存储区位置(LocalMachine - 分配给本地计算机的 X.509 证书存储区;CurrentUser - 当前用户使用的 X.509 证书存储区)-->
<!--storeName - 要打开的 X.509 证书存储区的名称(参看:StoreName枚举。AddressBook, AuthRoot, CertificateAuthority, Disallowed, My, Root, TrustedPeople, TrustedPublisher)-->
<!--x509FindType - 要执行的 X.509 搜索的类型(参看:X509FindType枚举)-->
<serviceCertificate findValue="Webabcd" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="SecurityBindingConfiguration">
<security>
<!--clientCredentialType - 客户端用以进行身份验证的凭据的类型,默认值 UserName -->
<!--BasicHttpMessageCredentialType.UserName - 使用用户名凭据对客户端进行身份验证-->
<!--BasicHttpMessageCredentialType.Certificate - 使用证书对客户端进行身份验证-->
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
四、为WCF客户端添加如下Config配置
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<client>
<!--address - 服务地址-->
<!--binding - 通信方式-->
<!--contract - 服务契约-->
<!--bindingConfiguration - 指定相关的绑定配置-->
<!--behaviorConfiguration - 指定相关的行为配置-->
<endpoint address="http://localhost:3502/ServiceHost/Security/Hello.svc"
binding="wsHttpBinding"
contract="SecuritySvc.IHello"
bindingConfiguration="HelloBindingConfiguration"
behaviorConfiguration="HelloBehaviorConfiguration">
<identity>
<!--encodedValue - 此证书编码的值。公钥,用于加密用户名和密码。测试时,请根据实际情况修改此值-->
<certificate encodedValue="MIIBrzCCAV2gAwIBAgIQbKQyXm9CcqFDX4Miyg8BIzAJBgUrDgMCHQUAMBYxFDAS
BgNVBAMTC1Jvb3QgQWdlbmN5MB4XDTExMDUxNzAzMTk1NFoXDTM5MTIzMTIzNTk1
OVowEjEQMA4GA1UEAxMHV2ViYWJjZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEAswtbR9PHHNr+KdqzcmlknoR8cscwcVhyMFT2pL6qPL+Lt8h6RQQqrvkybQVM
JMDO1YEK06VTBQI64v5Fbe+J/eXBD8KMIuV5Jym3QOybppvohOcFBVoEUvly3qce
WskUDTGZokrQ7QHaLZZagKBq7ZtxFHcvf4UeZW63hV+d2P0CAwEAAaNLMEkwRwYD
VR0BBEAwPoAQEuQJLQYdHU8AjWEh3BZkY6EYMBYxFDASBgNVBAMTC1Jvb3QgQWdl
bmN5ghAGN2wAqgBkihHPuNSqXDX0MAkGBSsOAwIdBQADQQBIZUMTjZSFTQYXYDA+
FdISovnpugA8ltEzkDQm7FpJee6WX/nlZzAu6reIdRZaaGnPCky23C7eix3DxIuu
+FqU" />
</identity>
</endpoint>
</client>
<bindings>
<wsHttpBinding>
<binding name="HelloBindingConfiguration">
<security>
<!--clientCredentialType - 客户端用以进行身份验证的凭据的类型,默认值 UserName -->
<!--BasicHttpMessageCredentialType.UserName - 使用用户名凭据对客户端进行身份验证-->
<!--BasicHttpMessageCredentialType.Certificate - 使用证书对客户端进行身份验证-->
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="HelloBehaviorConfiguration">
<clientCredentials>
<serviceCertificate>
<!--authentication - 证书验证模式 -->
<!--X509CertificateValidationMode.None - 不使用证书验证-->
<!--X509CertificateValidationMode.PeerTrust - 如果证书位于被信任的人的存储区中,则有效-->
<!--X509CertificateValidationMode.ChainTrust - 如果该链在受信任的根存储区生成证书颁发机构,则证书有效-->
<!--X509CertificateValidationMode.PeerOrChainTrust -如果证书位于被信任的人的存储区或该链在受信任的根存储区生成证书颁发机构,则证书有效 -->
<!--X509CertificateValidationMode.Custom -用户必须插入自定义 X509CertificateValidator 以验证证书 -->
<authentication certificateValidationMode="PeerTrust" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<configuration>
<system.serviceModel>
<client>
<!--address - 服务地址-->
<!--binding - 通信方式-->
<!--contract - 服务契约-->
<!--bindingConfiguration - 指定相关的绑定配置-->
<!--behaviorConfiguration - 指定相关的行为配置-->
<endpoint address="http://localhost:3502/ServiceHost/Security/Hello.svc"
binding="wsHttpBinding"
contract="SecuritySvc.IHello"
bindingConfiguration="HelloBindingConfiguration"
behaviorConfiguration="HelloBehaviorConfiguration">
<identity>
<!--encodedValue - 此证书编码的值。公钥,用于加密用户名和密码。测试时,请根据实际情况修改此值-->
<certificate encodedValue="MIIBrzCCAV2gAwIBAgIQbKQyXm9CcqFDX4Miyg8BIzAJBgUrDgMCHQUAMBYxFDAS
BgNVBAMTC1Jvb3QgQWdlbmN5MB4XDTExMDUxNzAzMTk1NFoXDTM5MTIzMTIzNTk1
OVowEjEQMA4GA1UEAxMHV2ViYWJjZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEAswtbR9PHHNr+KdqzcmlknoR8cscwcVhyMFT2pL6qPL+Lt8h6RQQqrvkybQVM
JMDO1YEK06VTBQI64v5Fbe+J/eXBD8KMIuV5Jym3QOybppvohOcFBVoEUvly3qce
WskUDTGZokrQ7QHaLZZagKBq7ZtxFHcvf4UeZW63hV+d2P0CAwEAAaNLMEkwRwYD
VR0BBEAwPoAQEuQJLQYdHU8AjWEh3BZkY6EYMBYxFDASBgNVBAMTC1Jvb3QgQWdl
bmN5ghAGN2wAqgBkihHPuNSqXDX0MAkGBSsOAwIdBQADQQBIZUMTjZSFTQYXYDA+
FdISovnpugA8ltEzkDQm7FpJee6WX/nlZzAu6reIdRZaaGnPCky23C7eix3DxIuu
+FqU" />
</identity>
</endpoint>
</client>
<bindings>
<wsHttpBinding>
<binding name="HelloBindingConfiguration">
<security>
<!--clientCredentialType - 客户端用以进行身份验证的凭据的类型,默认值 UserName -->
<!--BasicHttpMessageCredentialType.UserName - 使用用户名凭据对客户端进行身份验证-->
<!--BasicHttpMessageCredentialType.Certificate - 使用证书对客户端进行身份验证-->
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="HelloBehaviorConfiguration">
<clientCredentials>
<serviceCertificate>
<!--authentication - 证书验证模式 -->
<!--X509CertificateValidationMode.None - 不使用证书验证-->
<!--X509CertificateValidationMode.PeerTrust - 如果证书位于被信任的人的存储区中,则有效-->
<!--X509CertificateValidationMode.ChainTrust - 如果该链在受信任的根存储区生成证书颁发机构,则证书有效-->
<!--X509CertificateValidationMode.PeerOrChainTrust -如果证书位于被信任的人的存储区或该链在受信任的根存储区生成证书颁发机构,则证书有效 -->
<!--X509CertificateValidationMode.Custom -用户必须插入自定义 X509CertificateValidator 以验证证书 -->
<authentication certificateValidationMode="PeerTrust" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
QA: 如何得到encodedValue的值?
1.把cer文件(证书)导出为base64格式
a,使用mmc找到需要的证书 -- (Add Snap-In)
b,右击证书 导出
c,base64 encoded x509(.cer)
2.用文本工具打开可得到encodedValue值
最后、客户端调用很简单,直接实例化WCF服务调用,只是在调用前要对步骤二的方法进行传参。
using (var proxy = new SecuritySvc.HelloClient())
{
//此出进行验证
proxy.ClientCredentials.UserName.UserName = txtUserName.Text;
proxy.ClientCredentials.UserName.Password = txtPassword.Text;
//在下面调用你的WCF方法
proxy.SayHello("Vis");
{
//此出进行验证
proxy.ClientCredentials.UserName.UserName = txtUserName.Text;
proxy.ClientCredentials.UserName.Password = txtPassword.Text;
//在下面调用你的WCF方法
proxy.SayHello("Vis");
}