簡體   English   中英

使用證書的WCF傳輸安全性忽略了鏈信任

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM