Streaming MTOM Attachments in WCF and Certificate Authentication

I'm looking for a definitive answer as to whether what I'm trying to do is supported or not.

Basically, I'm using WCF to stream large MTOM attachments (200 Mb), this works perfectly fine. The security requirements for the service is to use HTTPS and certificate-based authentication. I can run the service over HTTPS without any problems, but once I set IIS to “Accept client certificates” or “Require client certificates” (no change in the code), the following error is thrown (but only once the attachments get over about 80 Mb or so):

The socket connection was aborted. 
This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. 
Local socket timeout was '00:30:00'.

I found some resources, sorry can't find them now, that indicated that the failure is probably related to the fact that the incoming messages either cannot be digitally signed, or verified due to the streaming nature of the message contents. I believe the service would have to hash the whole message contents to verify the cert, but this can't be achieved because portions of the message are in transit while validation is trying to occur.

I've setup the message contract so that the body is a single Stream element, and the other elements are contained within the header:

   <MessageContract()>
Public Class StreamAttachmentRequest

    <MessageHeader(MustUnderstand:=True)>
    Public Property AttachmentName As String

    <MessageBodyMember(Order:=1)>
    Public Property Attachment As Stream

End Class

The service configuration looks as follows:

<system.serviceModel>

<!-- BINDING -->
<bindings>
<basicHttpBinding>
  <binding name="TestCaseBasicBinding"
           messageEncoding="Mtom"
           transferMode="StreamedRequest"
           maxReceivedMessageSize="2147483647"
           closeTimeout="00:30:00"
           openTimeout="00:30:00"
           receiveTimeout="00:30:00"
           sendTimeout="00:30:00">
    <security mode="Transport">
      <transport clientCredentialType="None"></transport>
    </security>
    <readerQuotas maxDepth="32"
                      maxStringContentLength="8192"
                      maxArrayLength="16384"
                      maxBytesPerRead="4096"
                      maxNameTableCharCount="16384" />
  </binding>
</basicHttpBinding>
</bindings>

<!-- BEHAVIORS -->
<behaviors>
  <serviceBehaviors>

    <!-- TEST CASE SECURE BEHAVIOR -->
    <behavior name="TestCaseSecureBehavior">
      <serviceMetadata httpsGetEnabled="true" httpGetEnabled="false" />
      <serviceDebug includeExceptionDetailInFaults="true"/>
      <serviceCredentials>
        <serviceCertificate
            storeLocation="LocalMachine"
            storeName="My"
            findValue="DistinguishedNameOfCert"
            x509FindType="FindBySubjectDistinguishedName" />
        <clientCertificate>
          <authentication certificateValidationMode="ChainTrust"/>
        </clientCertificate>
      </serviceCredentials>
    </behavior>

  </serviceBehaviors>
</behaviors>

<!-- SERVICES -->
<services>
  <service name="StreamingMutualAuthTestCase.Web.Service.TestCaseServiceImplementation" 
           behaviorConfiguration="TestCaseSecureBehavior">
    <!-- SERVICE -->
    <endpoint address=""
              binding="basicHttpBinding"
              bindingConfiguration="TestCaseBasicBinding"
              contract="StreamingMutualAuthTestCase.Web.Service.ITestCaseService" />

    <endpoint contract="IMetadataExchange" binding="mexHttpsBinding" address="mex" />

  </service>
</services>    

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

The client configuration looks like this:

 <system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpBinding_ITestCaseService" closeTimeout="00:30:00"
                openTimeout="00:30:00" receiveTimeout="00:30:00" sendTimeout="00:30:00"
                maxReceivedMessageSize="2147483647" messageEncoding="Mtom"
                transferMode="Streamed">
                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                    maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                <security mode="Transport">
                    <transport clientCredentialType="Certificate" realm="" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>

  <!-- BEHAVIORS -->
  <behaviors>
    <endpointBehaviors>
      <behavior name="SecureClientBehavior">
        <clientCredentials>
          <clientCertificate
            storeLocation="LocalMachine"
            storeName="My"
            findValue="DistinguishedNameOfCert"
            x509FindType="FindBySubjectDistinguishedName"/>
          <serviceCertificate>
            <authentication certificateValidationMode="ChainTrust"/>
          </serviceCertificate>
        </clientCredentials>
      </behavior>
    </endpointBehaviors>
  </behaviors>

    <client>
        <endpoint address="https://test7/TestCaseService/TestCaseService.svc" 
                  binding="basicHttpBinding" 
                  bindingConfiguration="BasicHttpBinding_ITestCaseService"
                  contract="TestCaseService.ITestCaseService" 
                  name="BasicHttpBinding_ITestCaseService" 
                  behaviorConfiguration="SecureClientBehavior"/>
    </client>
</system.serviceModel>

Once again, this will work just fine until I set IIS Client Certs to either Accept or Require.

Also, there is a 413 error in the IIS log...

2011-08-18 15:00:06 W3SVC1 10.39.8.111 POST /TestCaseService/TestCaseService.svc - 443 - 10.75.13.81 - - - test7 413 0 0

I've already designed an authentication service on top of my file upload service to work around the issues; but I'd really like to know if what I'm trying to do is 'do-able' or not.

Thanks a ton - Patrick

Answers


If you want to turn on client certificates in IIS you must do the same for your service (and client):

<security mode="Transport">
  <transport clientCredentialType="Certificate" />
</security>

Your client must provide certificate to the proxy:

yourProxy.ClientCredentials.ClientCertificate.SetCertificate(...);

Also your server must trust these certificates so client certificates must be either issued by certification authority trusted by the server or they must be installed to LocalMachine\Trusted people store directly on the server.

WCF endpoints don't support "Accept client certificates" - you must either use client certificates or not.


Need Your Help

How to find the users who are online

php mysql chat

I was making a chat for my website. For that I need to find how many registered users are online. I count the number of sessions and store them in a table. When they logout, their name is removed f...

What is the difference between dispatch_sync and dispatch_main?

ios multithreading concurrency grand-central-dispatch

I understand that dispatch_async() runs something on a background thread, and dispatch_main() runs it on the main thread, so where does dispatch_sync() come in?