[英]WCF Transport Security using Certificates is ignoring chain trust
我一直在努力讓WCF安全工作為我的項目工作,並且運氣不佳。 我正在嘗試創建一個使用net.tcp作為綁定的服務,並同時執行消息和傳輸安全性。 使用用戶名和密碼完成消息安全性,並使用證書完成傳輸安全性(據稱!)。
對於我的開發測試,我創建了自己的證書頒發機構,並將此證書放在我的計算機的可信存儲(LocalMachine)中。 然后,我創建了兩個證書,每個證書都由我的證書頒發機構簽名,一個用於要使用的服務,另一個用於客戶端應用程序。 我將這兩個放在LocalMachine中的個人商店(我的)中。 然后,為了進行測試,我創建了一個未由我的證書頒發機構簽名的隨機證書(因此不受信任)並將其放在LocalMachine中的個人存儲中。 我使用makecert來創建這些證書。
然后,我配置連接到服務的客戶端應用程序,以使用無效的不受信任的證書作為其客戶端證書。 設置(假設)服務以使用鏈信任檢查客戶端證書。 但是,此客戶端能夠連接並成功與服務通信! 它應該被拒絕,因為它的證書是不可信的!
我不知道是什么導致了這種行為,所以我把這個問題提交給你們,看看你們是怎么做的。 這是我的WCF配置:
服務配置:
<system.serviceModel>
<services>
<service behaviorConfiguration="DHTestBehaviour" name="DigitallyCreated.DHTest.Business.DHTestBusinessService">
<endpoint address="" binding="netTcpBinding" contract="DigitallyCreated.DHTest.Business.IDHTestBusinessService" bindingConfiguration="DHTestNetTcpBinding" bindingNamespace="http://www.digitallycreated.net/DHTest/v1" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8090/"/>
<add baseAddress="http://localhost:8091/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="DHTestBehaviour">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="DHTestMembershipProvider"/>
<serviceCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" findValue="CN=business.dhtestDHTest.com" />
<clientCertificate>
<authentication certificateValidationMode="ChainTrust" trustedStoreLocation="LocalMachine" revocationMode="NoCheck" />
</clientCertificate>
</serviceCredentials>
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="DHTestRoleProvider" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="DHTestNetTcpBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
<transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign"/>
</security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
客戶會議:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IDHTestBusinessService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288"
maxBufferSize="65536" maxConnections="10" maxReceivedMessageSize="65536">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
<message clientCredentialType="UserName" />
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="DHTestBusinessServiceEndpointConf">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" findValue="CN=invalid"/>
<serviceCertificate>
<authentication revocationMode="NoCheck" trustedStoreLocation="LocalMachine"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="net.tcp://phoenix-iv:8090/" binding="netTcpBinding"
behaviorConfiguration="DHTestBusinessServiceEndpointConf"
bindingConfiguration="NetTcpBinding_IDHTestBusinessService"
contract="DHTest.NetTcp.Business.IDHTestBusinessService"
name="NetTcpBinding_IDHTestBusinessService">
<identity>
<dns value="business.dhtest.com" />
</identity>
</endpoint>
</client>
</system.serviceModel>
客戶端用戶名/密碼驗證碼:
DHTestBusinessServiceClient client = new DHTestBusinessServiceClient();
client.ClientCredentials.UserName.UserName = "ratfink";
client.ClientCredentials.UserName.Password = "testpassword";
提前謝謝你的幫助。
編輯(2009/06/01):
我的一位朋友向我指出了一個博客 ,回答了為什么會發生這種情況的問題。 顯然,當你指定TransportWithMessageCredential 正是意味着:交通運輸, 只有消息憑據。 這就是我的證書在傳輸級別被忽略的原因。
但是,我不認為這個問題是完整和封閉的,因為我仍然想這樣做。 :)我將調查自定義證書驗證器,我認為我可以插入,看看是否有效。 結果我會全部回復你。
編輯(2009/06/08):
不,自定義證書驗證程序也不起作用。 WCF根本不會調用它們。
我找到了解決問題的方法,但事實證明它比我想象的要糟糕得多。
基本上,要實現傳輸和消息憑據檢查,您需要定義自定義綁定。 (我在這里找到了這個效果的信息 )。
我發現最簡單的方法是繼續在XML中進行配置,但在運行時復制並稍微修改XML配置中的netTcp綁定。 您需要啟用一個開關。 這是服務端和客戶端的代碼:
服務方
ServiceHost businessHost = new ServiceHost(typeof(DHTestBusinessService));
ServiceEndpoint endpoint = businessHost.Description.Endpoints[0];
BindingElementCollection bindingElements = endpoint.Binding.CreateBindingElements();
SslStreamSecurityBindingElement sslElement = bindingElements.Find<SslStreamSecurityBindingElement>();
sslElement.RequireClientCertificate = true; //Turn on client certificate validation
CustomBinding newBinding = new CustomBinding(bindingElements);
NetTcpBinding oldBinding = (NetTcpBinding)endpoint.Binding;
newBinding.Namespace = oldBinding.Namespace;
endpoint.Binding = newBinding;
客戶端
DHTestBusinessServiceClient client = new DHTestBusinessServiceClient();
ServiceEndpoint endpoint = client.Endpoint;
BindingElementCollection bindingElements = endpoint.Binding.CreateBindingElements();
SslStreamSecurityBindingElement sslElement = bindingElements.Find<SslStreamSecurityBindingElement>();
sslElement.RequireClientCertificate = true; //Turn on client certificate validation
CustomBinding newBinding = new CustomBinding(bindingElements);
NetTcpBinding oldBinding = (NetTcpBinding)endpoint.Binding;
newBinding.Namespace = oldBinding.Namespace;
endpoint.Binding = newBinding;
你會認為那是它,但你錯了! :)這是額外的跛腳。 我將我的具體服務方法歸因於PrincipalPermission,以根據服務用戶的角色限制訪問,如下所示:
[PrincipalPermission(SecurityAction.Demand, Role = "StandardUser")]
一旦我應用了上述更改,這就開始失敗了。 原因是因為
OperationContext.Current.ServiceSecurityContext.PrimaryIdentity
最終成為一個未知的,無用戶名,未經認證的IIdentity。 這是因為實際上有兩個代表用戶的身份:一個用於通過傳輸進行身份驗證的X509證書,另一個用於用於在消息級別進行身份驗證的用戶名和密碼憑據。 當我反向設計WCF二進制文件以查看為什么它沒有給我我的PrimaryIdentity時,我發現它有一個明確的代碼行,如果它找到多個IIdentity,它會返回那個空的IIdentity。 我想這是因為它無法確定哪一個是主要的一個。
這意味着使用PrincipalPermission屬性不在窗口中。 相反,我寫了一個方法來模仿它可以處理多個IIdentities的功能:
private void AssertPermissions(IEnumerable<string> rolesDemanded)
{
IList<IIdentity> identities = OperationContext.Current.ServiceSecurityContext.AuthorizationContext.Properties["Identities"] as IList<IIdentity>;
if (identities == null)
throw new SecurityException("Unauthenticated access. No identities provided.");
foreach (IIdentity identity in identities)
{
if (identity.IsAuthenticated == false)
throw new SecurityException("Unauthenticated identity: " + identity.Name);
}
IIdentity usernameIdentity = identities.Where(id => id.GetType().Equals(typeof(GenericIdentity))).SingleOrDefault();
string[] userRoles = Roles.GetRolesForUser(usernameIdentity.Name);
foreach (string demandedRole in rolesDemanded)
{
if (userRoles.Contains(demandedRole) == false)
throw new SecurityException("Access denied: authorisation failure.");
}
}
它不漂亮(特別是我檢測用戶名/密碼憑證IIdentity的方式),但它的工作原理! 現在,在我的服務方法的頂部,我需要像這樣調用它:
AssertPermissions(new [] {"StandardUser"});
查看Codeplex - Intranet部分 ,它提供了針對不同場景的清單。 另外要提到的是,在IIS5.0和IIS6.0中不支持使用netTcpBindings - 請參閱此處的第3段 ,僅IIS7.0 +。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.