WCF with client certificates and "The maximum array length quota (16384) has been exceeded"

1.6k views Asked by At

I get this error sending an uploaded, large excel file in a method call from the client web app to a web service in a .net 4.5 mvc wcf application. It worked before with the same file, but after securing the client / service connection with a client certificate I got this error:

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:data. The InnerException message was 'There was an error deserializing the object of type System.Byte[]. The maximum array length quota (16384) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 2584463.'.  Please see InnerException for more details.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.     
Exception Details: System.ServiceModel.FaultException`1[[System.ServiceModel.ExceptionDetail, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]: The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:data. The InnerException message was 'There was an error deserializing the object of type System.Byte[]. The maximum array length quota (16384) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 2584463.'.  Please see InnerException for more details.    
Source Error:    
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.    
Stack Trace:         
[FaultException`1: The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:data. The InnerException message was 'There was an error deserializing the object of type System.Byte[]. The maximum array length quota (16384) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 2584463.'.  Please see InnerException for more details.]
   System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) +10733331
   System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) +336
   MyService.WebApp.ImportServiceRef.IImportService.Import(LoginContext lctx, Byte[] data, String filename) +0

After reading a lot about the issue on stackoverflow, I tried several things to increase all buffers and max-arguments in all bindings, but in the end still get the same error. Strange thing is that the setup worked before switching to client certificates. Any suggestions?

Client Web.config:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=..."/>
    <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=..."/>
  </configSections>
  <appSettings>
      ...
    </appSettings>
  <location path="FederationMetadata">
    <system.web>
      <authorization>
        <allow users="*"/>
      </authorization>
    </system.web>
  </location>
  <system.web>
    <globalization uiCulture="auto:de-DE" culture="auto:de-DE" requestEncoding="utf-8" responseEncoding="utf-8"/>
    <customErrors mode="Off" defaultRedirect="Error"/>
    <identity impersonate="false"/>
    <sessionState mode="InProc" timeout="120"/>
    <compilation debug="true" targetFramework="4.5"/>
    <httpRuntime targetFramework="4.5" requestValidationMode="4.5" maxRequestLength="2097151"/>
    <authentication mode="Windows"/>
    <authorization>
      <deny users="?"/>
    </authorization>
    <pages>
      <namespaces>
          ...
      </namespaces>
    </pages>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <handlers>
        ...
    </handlers>
    <httpProtocol>
    </httpProtocol>
  </system.webServer>
  <runtime>
      ...
  </runtime>
  <system.serviceModel>

    <behaviors>
      <endpointBehaviors>
        <behavior name="SecuredBehaviour">
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
          <clientCredentials>
            <clientCertificate findValue="CertAzusClientTeststage" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
            <serviceCertificate>
              <authentication certificateValidationMode="PeerOrChainTrust"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <bindings>
      <basicHttpBinding>
        <binding name="secureBinding" maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647">
          <security mode="Transport" />
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>
      </basicHttpBinding>
      <wsHttpBinding>
        <binding name="secureBindingWs" closeTimeout="00:01:00" openTimeout="00:01:00"
          receiveTimeout="00:15:00" sendTimeout="00:15:00" 
          messageEncoding="Text" textEncoding="utf-8"
          useDefaultWebProxy="true"
          maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647" 
          >
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>

        <binding name="wsHttpEndpointBinding" sendTimeout="12:00:00"
          maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647" 
        >
          <security mode="Message">
              <message clientCredentialType="Certificate"/>
          </security>
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>
      </wsHttpBinding>
    </bindings>
    <protocolMapping>
      <add binding="basicHttpBinding" scheme="https" bindingConfiguration="secureBinding"/>
      <add binding="wsHttpBinding" scheme="https" bindingConfiguration="secureBindingWs"/>
    </protocolMapping>
    <client>
      <endpoint address="http://server-xyz/Service/AntragService.svc"
        binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding"
        contract="AntragServiceRef.IAntragService" name="WSHttpBinding_IAntragService" behaviorConfiguration="SecuredBehaviour">
        <identity>
          <dns value="CertAzusClientTeststage"/>
        </identity>
      </endpoint>
      <endpoint address="http://server-xyz/Service/ImportService.svc"
        binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding"
        contract="ImportServiceRef.IImportService" name="WSHttpBinding_IImportService" behaviorConfiguration="SecuredBehaviour">
        <identity>
          <dns value="CertAzusClientTeststage"/>
        </identity>
      </endpoint>
      <endpoint address="http://server-xyz/Service/ReportService.svc"
        binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding"
        contract="ReportServiceRef.IReportService" name="WSHttpBinding_IReportService" behaviorConfiguration="SecuredBehaviour">
        <identity>
          <dns value="CertAzusClientTeststage"/>
        </identity>
      </endpoint>
      <endpoint address="http://server-xyz/Service/SettingsService.svc"
        binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding"
        contract="SettingsServiceRef.ISettingsService" name="WSHttpBinding_ISettingsService" behaviorConfiguration="SecuredBehaviour">
        <identity>
          <dns value="CertAzusClientTeststage"/>
        </identity>
      </endpoint>   
    </client>
  </system.serviceModel>
</configuration>

Service Web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=..." requirePermission="false" />
  </configSections>
  <appSettings>
      ...
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" maxRequestLength="2097151"/>
    <globalization culture="de-DE" uiCulture="de-DE" />
  </system.web>

  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding           
            maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647">
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>
        <binding 
            maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647" 
            name="secureBinding">
          <security mode="Transport" />
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>

      </basicHttpBinding>
      <wsHttpBinding>
        <binding name="wsHttpEndpointLokal" sendTimeout="00:05:00" 
            maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647" >
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>

        <binding name="wsHttpEndpointBindingSecured" sendTimeout="00:05:00" 
            maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647" >
          <security mode="Message">
            <message clientCredentialType="Certificate"/>
          </security>
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>

        <binding name="secureBindingWs" closeTimeout="00:01:00" openTimeout="00:01:00"
          receiveTimeout="00:15:00" sendTimeout="00:15:00" 
          messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
          maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647" >
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>

        <binding name="wsHttpEndpointBinding" sendTimeout="12:00:00" 
            maxBufferPoolSize="2147483647" maxBufferSize="524288" maxReceivedMessageSize="2147483647" >
          <security mode="Message">
              <message clientCredentialType="Certificate"/>
          </security>
          <readerQuotas
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxDepth="2147483647"
            maxNameTableCharCount="2147483647"
            maxStringContentLength="2147483647" />                          
        </binding>

      </wsHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="SecuredServiceBehaviour" name="MyApp.Service.AntragService">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBindingSecured" contract="MyApp.Common.Service.IAntragService"/>
      </service>
      <service behaviorConfiguration="SecuredServiceBehaviour" name="MyApp.Service.ImportService">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBindingSecured" contract="MyApp.Common.Service.IImportService"/>
      </service>
      <service behaviorConfiguration="SecuredServiceBehaviour" name="MyApp.Service.ReportService">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBindingSecured" contract="MyApp.Common.Service.IReportService"/>
      </service>
      <service behaviorConfiguration="SecuredServiceBehaviour" name="MyApp.Service.SettingsService">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBindingSecured" contract="MyApp.Common.Service.ISettingsService"/>
      </service>    
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SecuredServiceBehaviour">
          <serviceCredentials>
            <clientCertificate>
              <authentication certificateValidationMode="PeerOrChainTrust"/>
            </clientCertificate>
            <serviceCertificate findValue="CertAzusClientTeststage" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
          </serviceCredentials>
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
        </behavior>

        <behavior>
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="SecuredBehaviour">
          <dataContractSerializer maxItemsInObjectGraph="2147483647" />
          <clientCredentials>
            <clientCertificate findValue="CertAzusClientTeststage" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
            <serviceCertificate>
              <authentication certificateValidationMode="PeerOrChainTrust"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>    
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
      <add binding="wsHttpBinding" scheme="https" bindingConfiguration="secureBindingWs"/>
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <client>
      <endpoint 
        address="http://127.0.0.1/SecondService/StammdatenSvc.svc" 
        binding="wsHttpBinding" 
        bindingConfiguration="wsHttpEndpointBinding"
        behaviorConfiguration="SecuredBehaviour"
        contract="KV.Nordrhein.STMSAT.DataContract.ISatStammdatenSvc" 
        name="SatStammdatenSvc">
        <identity>
          <dns value="CertAzusClientTeststage"/>
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <directoryBrowse enabled="true" />
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="198743680" />
      </requestFiltering>
    </security>
  </system.webServer>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <connectionStrings>
      ...
  </connectionStrings>
</configuration>
4

There are 4 answers

0
Sebastian Baltes On BEST ANSWER

The problem is the certificate signing. The signing encrypts the parts of the service method result. It does so by encrypting every primitive part of the result - if it is a list of strings, each string is encrypted seperatly. If it is a single big byte array like in my case, then the big byte array is encrypted as a whole, and the encryptor fails because it is bigger than 16 KByte. My current workaround is splitting the big byte array into a list of small byte array chunks (each 1 KByte), and now it works.

Another solution may be using streaming mode, but as cevauess wrote, streaming and message level security are not allowed together, and message level security is a requirement in my case.

1
Luca Morelli On

http://www.codeproject.com/Articles/521725/Request-Entity-Too-Large

have you tried with uploadReadAheadSize as shwon in this article?

1
Jens H On

Although you configured your all byte array sizes the message still says "maximum array length quota (16384)" this indicates a fallback to the default value.

This in turn indicates an error somewhere in your configuration. So that whatever you configure does not get applied and instead the default is used. Please re-read your Web.config very carefully.

I am not sure about your endpoint configurations there. I believe only one endpoint is allowed to have address="" configured. The other endpoints need to each specify a unique address. (A MSDN example is here: Multiple Endpoints)

If you instead want to have them all use the same address you would need to configure them with the listenUri attribute. (MSDN article here: Multiple Endpoints at a Single ListenUri)

[UPDATE]

Have a look at this code in Web.config, I think there might be a spelling mistake here.

<protocolMapping>
  <add binding="basicHttpsBinding" scheme="https" />
  [...]
</protocolMapping>

Contradictory, in the bindings configuration, you have setup all the array max lengths of the basicHttpBinding not basicHttpsBinding. => Notice the differences with the "s" of the http / https protocols here.

<system.serviceModel>
  <bindings>
    <basicHttpBinding>
    [...]
    </basicHttpBinding>
  </bindings>
</system.serviceModel>

This could be the reason why the error message quotes the default array size. You have specified the array sizes on the wrong binding type basicHttpBinding but are instead using the basicHttpsBinding, which still has all of its default values.

=> Try changing your basicHttpBinding configurations to basicHttpsBinding instead.

2
cevauess On

Message Level Security and Streaming Mode is not allowed together

see: http://msdn.microsoft.com/en-us/library/ms733137.aspx