Quantcast
Channel: Distributed Services: Notes from the field
Viewing all 164 articles
Browse latest View live

Metadata de-serialization fails with web reference

$
0
0

Statement

==================

I have a console application, which consumes a java web service. The web service metadata is downloaded by Add Web Reference option.

1. Console application is able to form request and send to java web service.

2. Java web service is able to receive and process the request.

3. Java web service is also able to send the response payload over wire.

4. However, client application is not able to read the response (.net object appears to have null value even though payload indicates it has necessary data).

Hence, challenge is with client side de-serialization.

 

Troubleshooting

===================

We need to track the request/ response payload to troubleshoot this type of issues. For this, we can utilize Microsoft network monitor tool or Fiddler tool and read through http request and response.

 

Client

   
      static void Main(string[] args)       
      {   
           MyDevices proxy = new MyDevices();

           string desc = null;

           myDevicesResponseListResponse[] respArray = null;

           
           try

           {

                var res = proxy.getDevices(new string[] { "0123456789" }, out desc, out respArray); //but res is null, though payload response is captured in traffic

                Console.WriteLine(res);

           }

           catch (Exception ex)

           {

                Console.WriteLine(ex.ToString());

           }

           Console.ReadLine();

       }

 

Service

         https://www.myservice1.com/great/MyDevices?wsdl

Request

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

     <soap:Body>

           <getDevices xmlns="http://www.abc.com/services">

                   <imei>0123456789</imei>

           </getDevices>

     </soap:Body>

</soap:Envelope>

 

Response: Actual – validation failed

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">

       <soapenv:Body>

                <getDevicesResponse xmlns="http://abcservice.directed.com">

                         <returnCode>97</returnCode>

                </getDevicesResponse>

       </soapenv:Body>

</soapenv:Envelope>

 

Response: validation success

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">

  <soapenv:Body>  
        <getDevicesResponse xmlns="http://www.abc.com/services">

            <returnCode>97</returnCode>

        </getDevicesResponse>

  </soapenv:Body>

</soapenv:Envelope>

 

Note:

One of the ways to validate response payload against schema is to validate payload on mock service response editor.

From the comparison, it is clear that difference is with namespace.

If we will be able to modify the response namespace in payload and update with the expected value, .net objects should be able to de-serialize them. In web reference world, we have to write custom SoapExtension to suffice this objective.

 

Step-1

Create a class name TraceExtension, and add the following code:

 

using System.Web.Services.Protocols;

public class TraceExtension: SoapExtension  
{       
    Stream oldStream;

    Stream newStream;

    string filename;

    public override Stream ChainStream(Stream stream)

    {

          oldStream = stream;

          newStream = new MemoryStream();

          return newStream;

    }

    public override object GetInitializer(Type serviceType)

    {

          return null;

    }

    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)

    {

          return null;

    }

    public override void Initialize(object initializer)

    {

    }

    public override void ProcessMessage(SoapMessage message)

    {

         switch (message.Stage)

         {

               case SoapMessageStage.AfterDeserialize:

                   break;

               case SoapMessageStage.AfterSerialize:

                   ModifyRequestStream(message);

                   break;

               case SoapMessageStage.BeforeDeserialize:                       
                    ModifyResponseStream(message);

                   break;

               case SoapMessageStage.BeforeSerialize:

                   break;               

          }

    }

        
private void ModifyRequestStream(SoapMessage message)

 {

           newStream.Position = 0;

           //WriteOutput(message);

           newStream.Position = 0;

           Copy(newStream, oldStream);

 }

       
private void WriteOutput(SoapMessage message)

{

           string soapString = (message is SoapServerMessage) ? "SoapResponse" : "SoapRequest";           

}

       
private void ModifyResponseStream(SoapMessage message)

{

           Copy(oldStream, newStream);

           var doc = WriteInput(message);

           newStream.Position = 0;

           doc.Save(newStream);

           newStream.Position = 0;

}

       
private XmlDocument WriteInput(SoapMessage message)      
{           
          string soapString = (message is SoapServerMessage) ? "SoapRequest" : "SoapResponse";

          XmlDocument doc = new XmlDocument();

          XmlWriter newWriter = XmlWriter.Create(newStream);

          newWriter.Flush();

          newStream.Position = 0;

          doc.Load(newStream);

          string xml = doc.OuterXml;

          xml = xml.Replace("http://abcservice.directed.com", "http://www.abc.com/services"); //update logic for namespace

          doc.LoadXml(xml);

          return doc;       
}

        
void Copy(Stream from, Stream to)

 {

           TextReader reader = new StreamReader(from);

           TextWriter writer = new StreamWriter(to);

           writer.WriteLine(reader.ReadToEnd());

           writer.Flush();

 }

   
}

 

Step-2

Go to your config file app.config and update that you are using soap extension class:

  
     <system.web>

         <webServices>

                <soapExtensionTypes>

                         <add type="MyConsoleApplication.TraceExtension, MyConsoleApplication" priority="0" group="High" /> 

                         <!--Give fully qualified name for SOAP extension, so that it can be readable-->

                </soapExtensionTypes>

          </webServices>

      </system.web>

 

Now, run the client application, it should be able to de-serialize the response payload.

In future if you want to handle the same in service side, please update the logic in ProcessMessage() method. It just requires to follow the reverse approach of client side.

 

I hope this helps!


Add service reference fails with compilation error in WF 4.5/4.5.1 projects

$
0
0

Statement

======================

WF application consumes WCF service.

WCF service is supposed to return List<CustomClass> in response for one of its methods.

The client application adds service reference with advanced option as "System.Collections.Generic.List".

In reference.cs, List< CustomClass> member is generated.

 

However, there is a build error:

Compiler error(s) encountered processing expression "".

Reference required to assembly 'System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' containing the implemented interface 'System.Runtime.Serialization.IExtensibleDataObject'.

This problem happens if build framework is 4.5 or 4.5.1.

The same activities do not generate any error in 4.0 framework.

 

Observation

======================

This is workflow tooling issue (bug) in recent version of .net 4.5/4.5.1.

 

Work around

======================

1)

Once add service reference has been run, it generates a XAML under reference.svcmap.

2)

Go to the generated XAML file.

3)

Add the following line for Activity tag:

xmlns:srs="clr-namespace:System.Runtime.Serialization;assembly=System.Runtime.Serialization,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

4)

This particular update in Activity tag must be applied on all the proxy generated XAML files.

 

In my sample, it appears like:

Before

<Activity
mc:Ignorable="sap2010 sads"
x:Class="WorkflowConsoleApplication1.ServiceReference1.Activities.GetAllBooks"
this:GetAllBooks.EndpointConfigurationName="BasicHttpBinding_IService1"

xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities"

xmlns:p="http://tempuri.org/"

xmlns:p1="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel"

xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger"

xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation"

xmlns:sc="clr-namespace:System.ComponentModel;assembly=System"

xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib"

xmlns:this="clr-namespace:WorkflowConsoleApplication1.ServiceReference1.Activities"

xmlns:ws="clr-namespace:WorkflowConsoleApplication1.ServiceReference1;assembly=WorkflowConsoleApplication1"

xmlns:ws1="clr-namespace:WorkflowConsoleApplication1.ServiceReference1;assembly=WorkflowConsoleApplication1,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <x:Members>

 

After

<Activity
mc:Ignorable="sap2010 sads"
x:Class="WorkflowConsoleApplication1.ServiceReference1.Activities.GetAllBooks"
this:GetAllBooks.EndpointConfigurationName="BasicHttpBinding_IService1"

xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities"

xmlns:p="http://tempuri.org/"

xmlns:p1="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel"

xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger"

xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation"

xmlns:srs="clr-namespace:System.Runtime.Serialization;assembly=System.Runtime.Serialization,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

xmlns:sc="clr-namespace:System.ComponentModel;assembly=System"

xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib"

xmlns:this="clr-namespace:WorkflowConsoleApplication1.ServiceReference1.Activities"

xmlns:ws="clr-namespace:WorkflowConsoleApplication1.ServiceReference1;assembly=WorkflowConsoleApplication1"

xmlns:ws1="clr-namespace:WorkflowConsoleApplication1.ServiceReference1;assembly=WorkflowConsoleApplication1,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <x:Members>

 

Now, build the solution. This should fix the issue.

This problem persists in Visual studio 2012/ 2013.

This is taken up as an addressable bug and will be addressed in the latest version of visual studio release.

 

I hope this helps!

WCF: Time to learn SPN with WCF Kerberos Delegation

$
0
0

What is Kerberos Delegation:

Kerberos Delegation allows us to reuse the client credentials to access recourses hosted on a different server.

Box Level (if the backend server runs with Network service account)

  1. Full Delegation (we can delegation to any process on back end server application)
  2. Constrained delegation (we can delegate to selected back end server application)

User Level (if the backend server runs with Domain Account)

  1. Full Delegation (we can delegation to any process on back end server application)
  2. Constrained delegation (we can delegate to selected back end server application)

The Kerberos Delegation is configured as a property for the domain account that is used by the application as a service account. For an IIS application that is the application pool account configured for that particular application. That is the also the same domain account that you used when you configured the SPN.

 

Why do we need SPN ?

SPN is required to support MUTUAL authentication, which is supported only via KERBEROS scheme.

SPN is just a unique way to determine and confirm the backend server identity and used for encrypting the client token.

 

SPN in play:

 

  1. Client request a TOKEN to access the backend server.
  2. DC will check what is the identity configured for the desired resource/URL (http://myserver.com or http://MyLB.com )
  3. Identity configured is nothing but the SPN created for the backend server – identity of process (domain\user)
  4. Once DC finds that for requested URL, the SPN is like this (setspn –a http/myserver domain\user1) then the DC will send a windows token for client identity which will be Encrypted using the password (hash) of server’s process identity or the SPN we have set.
  5. Once client gets the encrypted Token, he will forward it to server.

At server the process identity will be used to decrypt the incoming token and client will be authenticated

 

For IIS Kernel mode authentication:

http://blogs.msdn.com/b/autz_auth_stuff/archive/2011/05/06/kernel-mode-authentication.aspx 

 

Goal: Understanding the sample

 

I created below test utility to easily test the WCF Delegation even on production servers. Just get and run them.

1. First start the WCFBackEnd.exe at the backend server

2. Note the TCP Endpoint Identity

  •  If we start the WCFBackEnd.exe with Network Service (we will see SPN (Host/MachineName))
  • Use following command to start the command prompt with Network service account
  •                 psexec -i -u "nt authority\network service" cmd.exe
  • If we start the WCFBackEnd.exe with domain account (we will see UPN (user@domain.com))

3. While starting make sure you run as Administrator, otherwise you might get some error.

4. Useful command, in case the URL registration creates any problem for Network service account:

  • netsh http add urlacl url=http://+:9000/Service user=NetworkService

5. Once the BackEnd service is up and we log in to the Middle Tier box and start the “WCFMiddleTier.exe” application.

6. Again this app can be started with Network service or domain account.

7. Above screen indicates that I am starting the service as domain account.

8. Next be sure that you specify the right client end point identity in the “WCFMiddleTier” which will eventually be used to make the double hop call to backend server.

9. Now let’s go to client box and run the “Client.exe”

10. There will a two questions asked.

  • Press “T” to test the TCP end point
  • Press “D” to test double hop.
  • By default if you press enter you will test the Http endpoint and Single Hop.

11. Result will be displayed in the client window for success or failure.

12. Once the test completes, there will a Prompt to press “A” to start new test. Pressing Enter will terminate the client application.

13. Further you can go to individual Middle Tier and Back end servers and observe the identity received.

 

 

SCENARIO 1:

 

 

Client can Add and select the back end server (SERVER – my server name) where we want to delegate the token.

I selected service type as (HOST), because the backend server running under the Network service account.

Which will use the default MACHINE NAME SPN (HOST/SERVERNAME).

 

 

 

 

Remember:

My Front end box name is DC

My Middle Tier box name is Client

My Back End Server box name is Server

 

 

SCENARIO 2:

 

For TCP endpoint:

We set the SPN with following syntax

SETSPN –a SERVICENAME/BOXNAME DOMAIN\USER

Or SETSPN –a SERVICENAME/HostHeader DOMAIN\USER

My service name:  service

Box name or let’s say the address I am point to request to: net.tcp://client:8000/service

So I keep the box name / Host Header as CLIENT.

setspn -a service/client:8000 contoso.com\user1

 

Once done, I can see the delegation tab appeared on DC, and I trusted user for full delegation.

For Http endpoint (Middle Tier)

Again we need to set a SPN for the http protocol along with the host header user for end point address.

SETSPN –a HTTP/HOSTHEADER DOMAIN\USER

setspn -a http/client contoso.com\user1

  

 

Now we will be able to make the call to middle tier box from the client.

  

 

SCENARIO 3:

 

If we run the test where backend start running under domain account

The DELEGATION test will fail now..

 

Call from middle tier to back will fail..

Error we get while making backend call:

 

I don’t see request reaching to back end server in AUDIT LOGS:

So we need to set the SPN for the backend server as well.

Backend server SPN (back end process runs under user2 domain account):

setspn -a http/server contoso.com\user2

Middle Tier SPN:

setspn -a http/client contoso.com\user1

For TCP:

setspn -a service/server:8000 contoso.com\user2

 

 

 

 

SCENARIO 4:

  

For constrained delegation:

Over Http End Point:

Select as per below screen and say Add –

Here specify user2 (because we want to trust User 1 at DC to delegate the token for the service running under user 2)

When we try to search the service running under user 2, we will see the SPN we configured for the HTTP end point.

Indicating that there is a service type called “http” running at server (computer name)

By selecting this we are allowing user1 to delegate the token to same.

 

We do see credentials flowing to back end server.

 

For constrained delegation:

Over TCP End Point:

All we need is one more SPN for the back end TCP server…

Similar to what we did for the Middle Tier TCP WCF service

SPN for back end TCP WCF service  (Don’t forget to add the PORT number in SPN)

setspn –a service/server:8000 contoso.com\user2

 

Now we go back to DC and allow user 1 to delegate to the service running under user 2.

This time we can see that for user2, we get two services.

Once added, my screen will look like this..

 

 

 

Assessment:

With the above addition change, my user1 should be able to perform constrained delegation to the back end server running with identity of user2.

 

Thumb Rule (for adding SPN):

Always (SETSPN –a  HTTP or NAME/HOSTHEADER DOMAIN\USER)

  • Http       : Setspn –a  http/xyz.com              contoso.com\user1
  • Tcp         : Setspn – a MYSERVICE/xyz.com constoso.com\user1
  • SQL        : Setspn -a MSSQLSvc/<sql-server-host-name:1433<sql-service-user-account-name>

Don’t forget to add the PORT for anything other than Http.

 

References:

  

To run the process as Network service:

 

I am sure this utility will be useful to many DEV, who would like to learn WCF and Delegation.

You can get source code from following link:

 

ONE DRIVE: 

http://1drv.ms/1Ns9CKW

 

 

 

 

Service failure with CryptographicException - Keyset does not exist

$
0
0

Problem Statement:

I have a WCF service hosted on IIS. The service is running in an app pool using Network Service account and uses a server certificate.

The service could be browsed without any problems until I recently installed a new certificate in my LocalMachine in the store “Personal Certificates”. Now while trying use the new one I get a Cryptographic Exception as below.

Exception information:

    Exception type: CryptographicException

    Exception message: Keyset does not exist

   at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)

   at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize,

 

 The goal is to troubleshoot the CryptographicException.

 

Troubleshooting:

It is very common that the certificate may get imported successfully on a system but we still face the above error. In such scenarios there are some few simple steps to troubleshoot the issue.

Step 1:

Locate the certificate that your application is trying to use in the MMC console (Local Machine). Make sure that there is a Private Key associated with the certificate.

  • ·         Double-click the certificate.
  • ·         In the Certificate dialog box, click the Details tab.
  • ·         Scroll through the list of fields and click Thumbprint.
  • ·         Copy the Thumbprint.

        

 

Step 2:

Next we need to use the tool “FindPrivateKey.exe”.

This is a very handy tool that allows us to locate the physical container for the key using its Thumbprint. It can be download from the internet.

To download the tool, you need to install the complete WCF_WF samples from the link - http://www.microsoft.com/en-us/download/confirmation.aspx?id=21459.

Once installed, browse to the folder - <installed fodler>\WCF\Tools\FindPrivateKey\CS\ and open the .sln file. Build the project to generate the .exe in the bin folder.

 

Now, open an administrator command prompt and move to the directory where the “FindPrivateKey.exe” is present and then run the below command.

FindPrivateKey.exe My LocalMachine –t “<thumbprint>” –a

 

Example:

>> findprivatekey.exe My LocalMachine -t "f3 f7 7f a4 a1 29 2f 19 66 f4 a1 95

 ce 81 1a 74 6a f3 e6 02" -a

 

C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\20aa7ccd097d2e94551b874c4b7feb55

_3b447ade-967e-4fc9-abcf-4a1932c76845

 

So, now we see the container for this private key. In this case, we can directly go to Step 4.

 

However, during my case, in customer’s system the “FindPrivateKey” command failed with an error:

> FindPrivateKey failed for the following reason:

Unable to obtain private key file name

Use /? option for help

 

In this case we need to follow Step 3. 

 

Step 3:

For some reason the container for the private key is not getting created correctly in the path “Crypto\RSA\MachineKeys\” when the certificate is imported using MMC console.

So, to confirm the above we need to run ProcMon.exe while importing the certificate using MMC.

ProcMon can be downloaded from the link - https://technet.microsoft.com/en-us/library/bb896645.aspx

It is just a simple EXE and no installation is required.

Launce the EXE and add a new filter – PathContainsCrypto\RSA\MachineKeys\

 

Now, when we import the certificate again using MMC, we see an “Access Denied” message in ProcMon when MMC.exe is trying to create a file in the folder “Crypto\RSA\MachineKeys\”.

Double clicking on the entry, shows us the user id that tried to create the file.

 

 

 

 This means that the user id does not have access to create a file in the “MachineKeys” folder.

So, we need to log in to the Local Administrator account and provide full access to that user on the folder “C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys”.

 

Now, we can import the certificate once again using the MMC console. This time when we use the “procmon.exe” while importing we should see something like this.

 

 

Step 4:

All that is left is now to provide READ permission to the user account, under which the application is running, using the tool cacls.exe or icalcs.exe.

In my case, the customer had an application hosted on IIS, which was running under the app pool account – “Network Service

So, I had to provide “Network Service” the read access to the container.

From an admin command prompt run the command:

Cacls.exe “<path>” /e /g “<user>”:R

 

Example:

>cacls.exe "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\20aa7ccd097d2e9

4551b874c4b7feb55_3b447ade-967e-4fc9-abcf-4a1932c76845" /e /g "Network Service":R

 

processed file: C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\20aa7ccd097d2e94

551b874c4b7feb55_3b447ade-967e-4fc9-abcf-4a1932c76845

 

This will provide the desired user account read access to the container of the private key and thus avoid the “KeySet does not exist” error.

 

 

I hope this helps!

 

 

Created by

Saurav Dey (MSFT)

WCF: Unable to add service reference - System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.SecurityPermission' failed" - after installing KB 2938782

$
0
0

Issue:

Client app: Console Application running with Partial Trust

Functionality broken: Add Service Reference for SSL hosted web service

 

Error:

System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed"

 

Important condition:

Affected box has the following KB installed: KB 2938782

 

Comparison with working box:

In the “Working” trace, we found that the SchUseStrongCrypto is set to 0, however for the failing trace it is set to 1
SchUseStrongCrypto:
http://blogs.msdn.com/b/tdevere/archive/2014/11/11/ssl-handshake-clienthello-receives-encrypted-alert.aspx

SchUseStrongCrypto
http://msdn.microsoft.com/en-us/library/windows/desktop/aa379810(v=vs.85).aspx

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SchUseStrongCrypto

Instructs Schannel to disable known weak cryptographic algorithms, ciphersuites, and SSL/TLS protocol versions that may be otherwise enabled for better interoperability.

 

STACK TRACE captured from failure IDNA:
0:000> kc
Call Site
kernelbase!RaiseException
clr!RaiseTheExceptionInternalOnly
clr!IL_Throw
mscorlib_ni!System.Security.CodeAccessSecurityEngine.ThrowSecurityException(System.Reflection.RuntimeAssembly, System.Security.PermissionSet, System.Security.PermissionSet, System.RuntimeMethodHandleInternal, System.Security.Permissions.SecurityAction, System.Object, System.Security.IPermission) mscorlib_ni!System.Security.CodeAccessSecurityEngine.CheckHelper(System.Security.PermissionSet, System.Security.PermissionSet, System.Security.CodeAccessPermission, System.Security.PermissionToken, System.RuntimeMethodHandleInternal, System.Object, System.Security.Permissions.SecurityAction, Boolean) clr!CallDescrWorkerInternal clr!CallDescrWorkerWithHandler clr!DispatchCallDebuggerWrapper clr!DispatchCallSimple clr!SecurityStackWalk::CheckPermissionAgainstGrants
clr!DemandStackWalk::CheckGrant
clr!DemandStackWalk::WalkFrame
clr!Thread::StackWalkFrames
clr!DemandStackWalk::DoStackWalk
clr!SecurityStackWalk::Check_StackWalk
clr!SecurityStackWalk::Check_PLS_SW
clr!SecurityStackWalk::SpecialDemand
clr!SecurityDeclarative::DoDeclarativeActions
clr!DoDeclarativeActionsForPInvoke
clr!StubHelpers::DemandPermission
system_configuration!DomainBoundILStubClass.IL_STUB_PInvoke()
system!System.Net.RegistryConfiguration.GetAppConfigValueName()
system!System.Net.RegistryConfiguration.AppConfigReadString(System.String, System.String)
system!System.Net.ServicePointManager.EnsureStrongCryptoSettingsInitialized()
system!System.Net.TlsStream.ProcessAuthentication(System.Net.LazyAsyncResult)

 
 

Workaround:

  1. Setting HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SchUseStrongCrypto to 0.
  2. Migrate to .Net Framework 4.6.
  3. If above two version not possible, request for hot fix from MS, refer following internal hot fix number: 3064715

 

Hope this help !

 

Large file upload failure for Web application calling WCF service - 413 Request entity too large

$
0
0

Problem Statement

I have a WCF service hosted on IIS that gets called from a web application. The WCF service is used for uploading/downloading files to and from a server.

For files, less than 50 KB upload/download works. However - when the file size exceeds 50 KB, it fails. No error message is displayed on the client application.

 

Troubleshooting

Step 1: Collect and review WCF traces

We started with configuring the WCF verbose level tracing for the service and client (web app).

 For more information on configuring WCF tracing please see the link - https://msdn.microsoft.com/en-us/library/ms733025%28v=vs.110%29.aspx

After the issue was reproduced, we found that no traces were generated for the Service, but we had a set of trace for the client application.

In the traces we could find the following.

 

<Exception>

<ExceptionType>System.ServiceModel.ProtocolException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>

<Message>The remote server returned an unexpected response: (413) Request Entity Too Large.</Message>

 

Since we did not have any WCF trace come up for the service, it seems that the request from client never made it to the WCF layer of the service and must have gotten blocked in the IIS itself.

 

Step 2: Check IIS Request filtering

The first thing I checked was “IIS Request Filtering” features setting for the WCF application.

To do this, open IIS Manager. Select your application.

In the Features view you will see “Request Filtering”.

 

 Open this feature and on the right hand panel you will find “Edit Feature Settings”

 

 

Maximum Allowed Content Lengthis an Optional U-Int attribute.

It specifies the maximum length of content in a request, in bytes.  The default value is
30000000, which is approximately 28.6MB.

 

This value was already set to 28.6 MB for the customer, so we could be sure that this feature was not responsible for blocking the incoming upload/download requests.

 

Step 3: Check “UploadReadAheadSize” in IIS

 

After some research I found out there is a setting present in the IIS – “UploadReadAheadSize” that prevents upload and download of data greater than 49KB. The value present by default is 49152 bytes and can be increased up to 4 GB.

 

The UploadReadAheadSize property establishes the number of bytes a Web server will read into a buffer and pass to an ISAPI extension. This occurs once per client request. The ISAPI extension receives any additional data directly from the client. The range is from 0 to &HFFFFFFFF (4 GB).

For more details on this property please check the URL - https://msdn.microsoft.com/en-us/library/ms525310%28v=vs.90%29.aspx

 

To navigate to this setting, use the following steps:

  • Launch "Internet Information Services (IIS) Manager"
  • Expand the Server field
  • Expand Sites
  • Select the site your application is in.
  • In the Features section, double click "Configuration Editor"
  • Under "Section" select: system.webServer>serverRuntime

   

 

For my case, customer had maximum file size less than 50 MB, so we set it to 52428800 i.e. 50 MB.

Please note that the value is in Bytes.

 

This resolved the issue for us and file uploads and downloads worked fine.

  

I hope this helps!

 

Created by

Saurav Dey (MSFT)

WCF: IIS hosting of net.tcp/ net.pipe services + metadata

$
0
0

A lot of time, customers around the globe question how best WCF services (running on net.tcp/ net.pipe protocol) can be hosted on IIS.

The popular bindings for the same are:

  -  netTcpBinding

  -  netNamedPipeBinding

As a naïve developer it is possible that service may not be hosted properly unless the configuration steps are followed properly.

I am sharing sample code/ configuration/ screenshots on this behalf.

 

Service Design 

1. Code 

namespace WcfService1

{

    [ServiceContract]

    public interface IService1

    {

        [OperationContract]

        string GetData(int value);

    }

}

 

namespace WcfService1

{   

    public class Service1 : IService1

    {

        public string GetData(int value)

        {

            return string.Format("You entered: {0}", value);

        }

    }

}

 

2. Configuration 

<system.serviceModel>

    <behaviors>

      <serviceBehaviors>

        <behavior name="netBehavior">

          <serviceMetadata httpGetEnabled ="true"/>

          <serviceDebug includeExceptionDetailInFaults="true" />

        </behavior>

      </serviceBehaviors>

    </behaviors>

    <bindings>

      <netTcpBinding>

        <binding name="netBinding">

          <security mode="None"/>

        </binding>

      </netTcpBinding>

      <netNamedPipeBinding>

        <binding name="pipeBinding">

          <security mode="None"/>

        </binding>

      </netNamedPipeBinding>

    </bindings>

    <services>

      <service name="WcfService1.Service1" behaviorConfiguration="netBehavior">

        <endpoint address=""  binding="basicHttpBinding" contract="WcfService1.IService1" />

        <endpoint address="" binding="netTcpBinding" bindingConfiguration="netBinding" contract="WcfService1.IService1" />

        <endpoint address="tmex" binding="mexTcpBinding" contract="IMetadataExchange" />

        <endpoint address="" binding="netNamedPipeBinding" bindingConfiguration="pipeBinding" contract="WcfService1.IService1" />

        <endpoint address="pmex" binding="mexNamedPipeBinding" contract="IMetadataExchange" />

      </service>

    </services>

  </system.serviceModel>

 

IIS hosting

a)      Deploy the service in IIS virtual directory

b)      Go to the site > Right click > Edit Bindings

               

c)       Go to the deployed application under the site

                         

d)      Here application is netPipeService1.

                    Right click > manage application > Advanced settings                              

e)      Update Enabled Protocols like the following:

                        

f)       Restart the application pool

 

Since we have an endpoint available over http, we should be able to browse the service component.

                   

 

Let’s go to the wsdl document. The <wsdl:service> content appears as the following:

              

 We can see the 3 highlighted endpoints now on the service metadata.

 

Client

Now, client application can create the proxy classes by 3 options.

Option 1

Add service reference with http endpoint:

               

Option 2

Add service reference with mex/tcp endpoint:

              

Option 3

Add service reference with mex/pipe endpoint:

           

 

In real time, it could happen you may have either of the bindings to play with. I would recommend to start from small and slowly increase breadth for other bindings.

 

 

I hope this helps! 

WCF: Client Certificate Sample/POC Using MakeCert.exe

$
0
0

Issue:

I came across one customer who was working on a POC project to demonstrate the usage of Client Certificate for authentication at transport level security.

 

Architecture:

Create a sample with following criteria:

  • Transport Security (Net.Tcp / HTTPS)
  • Client Credential as Client Certificate

 

Challenge:

Big challenge is how to test the POC/Sample with the certificates.

We need to get Server/Client certificates from Trusted Providers like RapidSSL, Go Daddy, etc

 

Solution:

We suggest our customer to use MakeCert.exe utility to create the server and client certificates.

Now remember these certificates are only meant for testing environment and should never be used on Production.

 

Using with WCF

Certificate created via MakeCert.exe are be default not so much complaint with WCF and testing always start with error.

That's the primary reason I started writing this blog.

Below steps will demonstrate using MakeCert.exe with correct commands, along with relevant WCF configuration to handle all errors :)

 

Steps (Script location at end):

  • Get the Certificate Authority ready with the help of CA.cmd script.
  • Get the Server Certificate ready with the help of SSLCert.cmd
  • Get the Client Certificate ready with the help of ClientCert.cmd

  

Please notes:

Default password used is "123".

So make sure when you install the above cert directly, use password as "123" (without quote).

 

Now lets take a look on WCF configuration needed:

Server Side WCF Configuration:

Observation:

  • Binding using the Transport level security via Net.Tcp binding
  • I have added an extra Http end point, so that I can check my service WSDL and end points easily.
  • Inside the Service Behavior, we need to add the Server Cert for Net.Tcp binding (we can skip this, if using HTTPS)
  • Finally Client Certificate Validation Mode is set to None, because certificate created via MakeCert.exe fails with Chain Build error.
 
 
Client Side WCF Configuration
 

Observation:

  • Binding using the Transport level security via Net.Tcp binding
  • I have specified the Client end point identity, as DNS value exhibited via WSDL (I used "xyz.com" because I issued the server cert to "xyz.com").
  • Inside the Client Behavior, we need to add the Client Cert used for authenticating the client.
  • Finally Server Certificate Validation Mode is set to None, because certificate created via MakeCert.exe fails with Chain Build error.

 

 

I have shared the sample certificates and scripts at below location and you can use the directly.

Sample and Certificates/Script Location:

http://1drv.ms/1NKDjcC

  

I hope this helps !


Debugging Tip: Dump the XML from System.Xml.XmlReader object

$
0
0

In a recent case, I had a difficult time in debugging to find the payload associated with XmlReader.  I am sharing what all I have done to get to the correct parameter.

 

Overall, XmlReader just acts as a pointer.

MSDN definition of System.Xml.XmlReader does not talk much where we can find the full XML associated with it.

I used netext (http://netext.codeplex.com/) extension to debug on this.

 

0:001> .load netext

0:001> !windex –tree  

Now, investigate what this thread is doing. From callstack, it is clear that certain XML read operation is underway. I am not sharing the callstack details out here as it is common to grab callstacks (by using command k or kL).

 

0:001> !wstack  

.

.

021b4fd8 70ba4c90   0  0         12 System.Xml.XmlTextReader

.

. 

Now, dump System.Xml.XmlTextReader object

0:001> !wdo 021b4fd8

.

70ba4690                     System.Xml.XmlTextReaderImpl +0000                                     impl 021b508c

 

Now, dump System.Xml.XmlTextReaderImpl object 

0:001> !wdo 021b508c

73db05f4                                     System.Int64 +0000                  maxCharactersInDocument 0 (0n0)

73db05f4                                     System.Int64 +0008                maxCharactersFromEntities 989680 (0n10000000)

73db05f4                                     System.Int64 +0010                     charactersInDocument 0 (0n0)

73db05f4                                     System.Int64 +0018                   charactersFromEntities 0 (0n0)

70ba4524         System.Xml.XmlTextReaderImpl+LaterInitPa +0020                           laterInitParam 00000000

73d60cbc                                  System.Object[] +0024                                    nodes 021b6828

70ba497c            System.Xml.XmlTextReaderImpl+NodeData +0028                                  curNode 021db6d0

73d60cbc                                  System.Object[] +002c                     attrDuplSortingArray 00000000

70b862fc                          System.Xml.XmlNameTable +0030                                nameTable 021b4fe4

70b84be0                           System.Xml.XmlResolver +0034                              xmlResolver 021b67ec

73da3e18                                    System.String +0038                                      url 021b2394 ../../../../SavedFiles/Version2NoOrder.xml

73d9d0a8                 System.Threading.CompressedStack +003c                          compressedStack 021b6a88

70ba487c                   System.Xml.XmlNamespaceManager +0040                         namespaceManager 021b690c

73da3e18                                    System.String +0044                               lastPrefix 021db6ac i

70ba49d0          System.Xml.XmlTextReaderImpl+XmlContext +0048                               xmlContext 021dba48

7104db58         System.Xml.XmlTextReaderImpl+ParsingStat +004c                       parsingStatesStack 00000000

73da3e18                                    System.String +0050                          reportedBaseUri 021b7c9c file:///D:/Sample/SavedFiles/Version2NoOrder.xml

73da5e9c                             System.Text.Encoding +0054                         reportedEncoding 021db614

70b8c258                              System.Xml.IDtdInfo +0058                                  dtdInfo 00000000

70b860fc                      System.Xml.XmlParserContext +005c                    fragmentParserContext 00000000

70b963e8                System.Xml.IncrementalReadDecoder +0060                           incReadDecoder 00000000

70b94d68                         System.Xml.BinHexDecoder +0064                            binHexDecoder 00000000

70ba2f84                         System.Xml.Base64Decoder +0068                            base64Decoder 00000000

70b95334           System.Xml.IncrementalReadCharsDecoder +006c                         readCharsDecoder 00000000

70b92a48              System.Xml.IValidationEventHandling +0070                  validationEventHandling 00000000

70b9888c         System.Xml.XmlTextReaderImpl+OnDefaultAt +0074                    onDefaultAttributeUse 00000000

73da660c                        System.Text.StringBuilder +0078                            stringBuilder 021b68ac

70b99c5c                        System.Xml.IDtdEntityInfo +007c                               lastEntity 00000000

73da41b8                                    System.Object +0080                          currentEntities 00000000

70ba4e50                             System.Xml.XmlReader +0084                              outerReader 021b4fd8

73da3e18                                    System.String +0088                                      Xml 021b5200 xml

73da3e18                                    System.String +008c                                    XmlNs 021b5214 xmlns

70b9746c         System.Xml.XmlTextReaderImpl+ParseTextSt +0090                       lastParseTextState 00000000

73db0bfc         System.Threading.Tasks.Task<System.Tuple +0094                      parseText_dummyTask 021b52e0

70b95ddc         System.Xml.XmlTextReaderImpl+ParsingFunc +0098                          parsingFunction 8 (0n8) PopEmptyElementContext

70b95ddc         System.Xml.XmlTextReaderImpl+ParsingFunc +009c                      nextParsingFunction 0 (0n0) ElementContent

70b95ddc         System.Xml.XmlTextReaderImpl+ParsingFunc +00a0                  nextNextParsingFunction 0 (0n0) ElementContent

73da560c                                     System.Int32 +00a4                                    index 1 (0n1)

73da560c                                     System.Int32 +00a8                             curAttrIndex ffffffff (0n-1)

73da560c                                     System.Int32 +00ac                                attrCount 2 (0n2)

73da560c                                     System.Int32 +00b0                            attrHashtable 44000 (0n278528)

73da560c                                     System.Int32 +00b4                        attrDuplWalkCount 0 (0n0)

70ba45c0                    System.Xml.WhitespaceHandling +00b8                       whitespaceHandling 0 (0n0) All

70ba43f8                         System.Xml.DtdProcessing +00bc                            dtdProcessing 2 (0n2) Parse

70b8f5a8                        System.Xml.EntityHandling +00c0                           entityHandling 2 (0n2) ExpandCharEntities

73da560c                                     System.Int32 +00c4                         lineNumberOffset 0 (0n0)

73da560c                                     System.Int32 +00c8                       linePositionOffset 0 (0n0)

73da560c                                     System.Int32 +00cc                    parsingStatesStackTop ffffffff (0n-1)

70ba4b10                           System.Xml.XmlNodeType +00d0                             fragmentType 9 (0n9) Document

70b96f20         System.Xml.XmlTextReaderImpl+Incremental +00d4                             incReadState 0 (0n0) Text

73da560c                                     System.Int32 +00d8                             incReadDepth 0 (0n0)

73da560c                                     System.Int32 +00dc                      incReadLeftStartPos 0 (0n0)

73da560c                                     System.Int32 +00e0                        incReadLeftEndPos 0 (0n0)

73da560c                                     System.Int32 +00e4               attributeValueBaseEntityId 0 (0n0)

73da560c                                     System.Int32 +00e8                             nextEntityId 1 (0n1)

70b9f5e0         System.Xml.XmlTextReaderImpl+ParsingMode +00ec                              parsingMode 0 (0n0) Full

70b860b8                             System.Xml.ReadState +00f0                                readState 1 (0n1) Interactive

73da560c                                     System.Int32 +00f4                     documentStartBytePos 0 (0n0)

73da560c                                     System.Int32 +00f8                          readValueOffset 0 (0n0)

70b99a58         System.Xml.XmlTextReaderImpl+ParseEndEle +00fc                 parseEndElement_NextFunc 0 (0n0) CheckEndTag

70b91568         System.Xml.XmlTextReaderImpl+ParseTextFu +0100                   parseText_NextFunction 0 (0n0) ParseText

73daf91c                                   System.Boolean +0104                                 useAsync 0 (False)

73daf91c                                   System.Boolean +0105                  attrNeedNamespaceLookup 0 (False)

73daf91c                                   System.Boolean +0106                          fullAttrCleanup 0 (False)

73daf91c                                   System.Boolean +0107                    nameTableFromSettings 0 (False)

73daf91c                                   System.Boolean +0108                                normalize 0 (False)

73daf91c                                   System.Boolean +0109                        supportNamespaces 1 (True)

73daf91c                                   System.Boolean +010a                                ignorePIs 0 (False)

73daf91c                                   System.Boolean +010b                           ignoreComments 0 (False)

73daf91c                                   System.Boolean +010c                          checkCharacters 0 (False)

73daf91c                                   System.Boolean +010d                               closeInput 1 (True)

73daf91c                                   System.Boolean +010e                                 v1Compat 1 (True)

73daf91c                                   System.Boolean +010f                                 fragment 0 (False)

73daf91c                                   System.Boolean +0110           emptyEntityInAttributeResolved 0 (False)

73daf91c                                   System.Boolean +0111               validatingReaderCompatFlag 0 (False)

73daf91c                                   System.Boolean +0112         addDefaultAttributesAndNormalize 0 (False)

73daf91c                                   System.Boolean +0113                        rootElementParsed 1 (True)

73daf91c                                   System.Boolean +0114                               standalone 0 (False)

73daf91c                                   System.Boolean +0115                          afterResetState 0 (False)

73daf91c                                   System.Boolean +0116             disableUndeclaredEntityCheck 0 (False)

73daf91c                                   System.Boolean +0117                         xmlResolverIsSet 0 (False)

70ba4fe0                           System.Xml.XmlCharType +0118                              xmlCharType -mt 70BA4FE0 00000000

70ba4ab8         System.Xml.XmlTextReaderImpl+ParsingStat +011c                                       ps -mt 70BA4AB8 00000000

70b9e430                              System.Xml.LineInfo +0164                          incReadLineInfo -mt 70B9E430 00000000

 

Now, dump System.Xml.XmlTextReaderImpl+NodeData object 

0:001> !wdo 021db6d0

73da3e18                                    System.String +0000                                localName 021eb61c Person

73da3e18                                    System.String +0004                                   prefix 021b1228

73da3e18                                    System.String +0008                                       ns 021dba0c Tests.FCTests

73da3e18                                    System.String +000c                              nameWPrefix 021eb61c Person

73da3e18                                    System.String +0010                                    value 021b1228

73da47d8                                    System.Char[] +0014                                    chars 021db2c8

70ba497c            System.Xml.XmlTextReaderImpl+NodeData +0018                       nextAttrValueChunk 00000000

73da41b8                                    System.Object +001c                               schemaType 00000000

73da41b8                                    System.Object +0020                               typedValue 00000000

70ba4b10                           System.Xml.XmlNodeType +0024                                     type 1 (0n1) Element

73da560c                                     System.Int32 +0028                            valueStartPos ffffffff (0n-1)

73da560c                                     System.Int32 +002c                              valueLength 4 (0n4)

73da560c                                     System.Int32 +0030                                    depth 1 (0n1)

73da560c                                     System.Int32 +0034                                 entityId 0 (0n0)

73da4810                                      System.Char +0038                                quoteChar "

73daf91c                                   System.Boolean +003a                         isEmptyOrDefault 1 (True)

73daf91c                                   System.Boolean +003b                         xmlContextPushed 0 (False)

70b9e430                              System.Xml.LineInfo +003c                                 lineInfo -mt 70B9E430 00000000

70b9e430                              System.Xml.LineInfo +0044                                lineInfo2 -mt 70B9E430 00000000

70ba497c Static     System.Xml.XmlTextReaderImpl+NodeData +0090                                   s_None 021d1dfc

 

Now, dump System.Char[] object 

0:001> !wdo 021db2c8

.

Data Start: 021db2d0

<People xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="Tests.FCTests">

  <AnotherPerson z:Id="2" i:type="AnotherPerson">

    <FriendPerson z:Id="3">

      <Name z:Id="4">Person</Name>

    </FriendPerson>

    <Name z:Id="5">AnotherPerson</Name>

  </AnotherPerson>

  <Person z:Ref="3" i:nil="true" />

</People>

 

Now, we have found out the XML payload contained in the XmlReader object.

SOS extension can also be used the same way (it is just the commands will be different). 

Pre-requisite: Windbg tool must be installed (reference: http://blogs.msdn.com/b/rodneyviana/archive/2015/03/10/getting-started-with-netext.aspx) 

  

Happy debugging … I hope this helps!

WCF: Net.Pipe - Endpoint not found exception - Admin/Non Admin mode

$
0
0

Issue Definition:

Following error reported from client app calling the WCF service over net.pipe.

There was no endpoint listening at net.pipe://localhost/XYZ/MyService that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.

 

Observation:

Client fails to locate the MyService - although server creation doesn't have any issue.

 It happens because some other process has created a netpipe server on "net.pipe://localhost" in admin mode. While our client tries to locate MyService it actually finds first in this "net.pipe://localhost" and tries to locate "XYZ/MyService". This happens because this initial service is created in admin mode so it gets priority over the other netpipe "net.pipe://localhost/XYZ" which got created in non-admin mode

 

When we create MyService also in admin mode - this problem doesn't come. But we need to support our App in non-admin mode as well.

The Client and Server processes always resides on the same machine so ideally WCF Net-pipes is the ideal choice

  

Working for Net.Pipe client

The first attempt will be for the name derived from "net.pipe://+/MyWCFConnection" in the Global namespace.

The second attempt will be based on the variant "net.pipe://+/", and this will match the name of the unwanted service's shared memory mapping published in the Global namespace.

Because of the search order, it will never get to our service's metadata published in the Local session namespace (if main WCF service is running under the non admin account).

 

Workaround:

  1. Run the required WCF service with Admin privilege
  2. We can try to use the “Absolute Address” and avoid using base address, that should register the pipe correctly and you should be able to use Non Admin mode to run the service.

Works:

        <services>

             <service behaviorConfiguration="servBeh" name="Component.Blah">

               <!--<host>

                 <baseAddresses>

                   <add baseAddress="net.pipe://localhost"/>

                 </baseAddresses>

               </host>-->

                <endpoint address="net.pipe://localhost/MyEndpoint" binding="netNamedPipeBinding"

                    bindingConfiguration="noSec" name="MyEndpoint" contract="Component.IBlah" />

            </service>

        </services>

 

3. Allow the selected non admin user to publish the pipe in Global session. we need to add the user to policy, as described below

References:

http://technet.microsoft.com/en-us/library/dn221972.aspx

 This policy setting determines which users can create global objects that are available to all sessions.

Possible values

• User-defined list of accounts

• Default accounts listed below

To edit this policy:

http://technet.microsoft.com/en-us/library/cc758168(v=WS.10).aspx

Once done, restart the box and see if the setting helps.

4. Try setting - hostNameComparisonMode="Exact" at the Net.Pipe binding level  and see if it helps.

 

Little more theory:

Running the handle.exe tool we can see the shared memory name generated by WCF:

\BaseNamedObjects\net.pipe:EbmV0LnBpcGU6Ly8rLw==

in this case, the base64 is: net.pipe://+/

Which is pretty general.

Also, we know that we can't create this base address using two different services if they're running in the same session.

If you run as admin (right click, run as admin) it will run in session 0.  Or if you run as a Windows service it will run in session 0.

You can run two different services in different sessions, but if you do, the client will try to use the one with the more generic name.

i.e, if you create the shared memory net.pipe://+/ in session 1 and net.pipe://BLAH/ in session 0, the client message will try to send to the net.pipe://+/ service.

Setting an absolute address will bypass this and will create a pipe specific to the service. 

You can get the baseaddress to work as expected by setting the hostNameComparisonMode of the binding to Exact.

        <netNamedPipeBinding>

          <binding name="blah" hostNameComparisonMode="Exact">

            <security mode="None"/>

          </binding>

        </netNamedPipeBinding>

 

Doing this will allow you actually set a unique base address host name.  The default is StrongWildcard which just puts a '+' as the host name.  Which means that it doesn't matter what the hostname of the baseaddress is, it will just use net.pipe://+/.

public static string BuildSharedMemoryName(Uri uri, HostNameComparisonMode hostNameComparisonMode, bool global)

        {

            string path = PipeUri.GetPath(uri);

            string host = null;

            switch (hostNameComparisonMode)

            {

                case HostNameComparisonMode.StrongWildcard:

                    host = "+";

                    break;

                case HostNameComparisonMode.Exact:

                    host = uri.Host;

                    break;

                case HostNameComparisonMode.WeakWildcard:

                    host = "*";

                    break;

            }

             return PipeUri.BuildSharedMemoryName(host, path, global);

 

 Please understand:

This is a known issue and it’s by design (for security reasons).

I hope this helps in understanding the way we need to use Net.Pipe binding in WCF.

 

Thanks

Saurabh Somani and Scott Mason (http://blogs.msdn.com/b/distributedservices/)

WPF/WinForm: NetHttpBinding Timeout/Deadlock issue on main UI thread - using web sockets

$
0
0

Issue:

We are trying to use NetHttpBinding and expect to use the web sockets, along with callback implementation.
Now web sockets are by default available if we have a callback contract implemented (when using NetHttpBinding), and it also can be forced on a request/response channel.
Synchronous and Asynchronous call to the service via console app works fine via web sockets (default behavior when calling service with callback contract).
But the moment I try to use Win Form / WPF app, my client application gets in hang state and never gets the callback.
Please note that the everything works as expected when using the wsDualHttpBinding.
 
While using NetHttpBinding, when I create a WCF service with callback contract implemented, my client does get the web Socket end point, when I add a service reference.
 

Stack Trace:

<ExceptionType>System.TimeoutException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>WaitForMessage timed out after 00:00:09.9829894. The time allotted to this operation may have been a portion of a longer timeout.</Message>
<StackTrace>
at System.Runtime.Diagnostics.EtwDiagnosticTrace.WriteExceptionToTraceString(XmlTextWriter xml, Exception exception, Int32 remainingLength, Int32 remainingAllowedRecursionDepth)
at System.Runtime.Diagnostics.EtwDiagnosticTrace.ExceptionToTraceString(Exception exception, Int32 maxTraceStringLength)
at System.Runtime.Diagnostics.EtwDiagnosticTrace.GetSerializedPayload(Object source, TraceRecord traceRecord, Exception exception, Boolean getServiceReference)
at System.Runtime.TraceCore.ThrowingException(EtwDiagnosticTrace trace, String param0, String param1, Exception exception)
at System.Runtime.ExceptionTrace.TraceException[TException](TException exception, String eventSource)
at System.Runtime.ExceptionTrace.AsError(Exception exception)
at System.ServiceModel.Channels.WebSocketTransportDuplexSessionChannel.WebSocketMessageSource.Receive(TimeSpan timeout)
at System.ServiceModel.Channels.SynchronizedMessageSource.Receive(TimeSpan timeout)
at System.ServiceModel.Channels.TransportDuplexSessionChannel.Receive(TimeSpan timeout)
at System.ServiceModel.Channels.TransportDuplexSessionChannel.TryReceive(TimeSpan timeout, Message&amp; message)
at System.ServiceModel.Dispatcher.DuplexChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&amp; msgData, Int32 type)
at TestService.IService.GetString()
 

More details:

It happens because WCF channel on client side tries to use the default UI Synchronization Context..
Reference :
http://www.codeproject.com/Articles/17704/WCF-Duplex-Operations-and-UI-Threads

When the service operation is invoked from the UI thread, it will block until the return value has been received. However, the service operation invokes a callback to the client prior to the sending the return value. The callback operation will be marshaled to the UI thread. 
Since the UI thread is still waiting for the return value, a deadlock will occur. The root of this problem has to do with synchronization contexts.

By default, WCF provides thread affinity to the current synchronization context that is used to establish a connection with a service. 
All service requests and replies will be executed on the thread of the originating synchronization context. In some situations, this may be desired behavior. 
At other times, it may not. In our case, it is causing a problem. 

So as per this article, if we set the UseSynchronizationContext to false, Call initiated from client app should not go in deadlock..
Article suggests to use following callbackBehavior - [CallbackBehavior(UseSynchronizationContext = false)]
Which does allow my client to handle the callback even from Synchronous call…

However it only works for WsDualHttpBinding .. I want to use it for NetHttp binding and it does not work there…
SynchronizationContext:
https://msdn.microsoft.com/magazine/gg598924.aspx
 

Reason and workaround:

Problem:
The problem being exposed is in the IChannel.Open path … the issue is if we open in the GUI thread, things fail were as if we open in another thread things work.

Summary:
We identified a potential bug in existing design (way we handle capturing the current synchronizationContext) and confirmed that client.Open() is not working as expected.

Workaround:
The way the client opens the channel needs to be changed.
1. Being/End methods
Try opening the client explicitly after you create it using the following code:
DuplexChannelFactory<IDuplexService> cf = new DuplexChannelFactory<IDuplexService>(ix, "wsDualHttpDuplexServiceEndpoint");
_duplexService = cf.CreateChannel(ix);
((IChannel)_duplexService).EndOpen(((IChannel)_duplexService).BeginOpen(null, null))
The same applies to the non-duplex client!

2. Try calling IChannel.Open explicitly using a different/background thread.
ManualResetEvent mre = new ManualResetEvent(false);
new Thread((s) => { ((IChannel)_duplexService).Open(); mre.Set(); }).Start();
mre.WaitOne();


Explanation
========
Now, as for the reason we see calls alternating … that’s because when we call the methods in a background thread, we’re implicitly opening the channel in a background thread, thus the open succeeds, 
whereas when we call the service method on the GUI thread, we’re implicitly opening the channel on the GUI thread which causes the deadlock.

I hope this helps WCF DEV to use this callback feature carefully when working with NetHttpBinding.

WCF: SSL/TLS Failure during Add Service Reference (System.Net.Security.SslState.ProcessAuthentication)

$
0
0

Issue:   WCF Client application unable to consume web service metadata over SSL.

Symptoms:  Unable to use “svcutil.exe” and “Add Service Reference” feature from .net  framework and visual studio.

Point of confusion:   Is it a Visual Studio – Add service reference problem or with svcutil.exe

Reason for failure:   Client app sends TLS 1.0 as part of hand shake and server rejects it.

Verified from System.Net traces and Network traces

 

At server: If the service is available over internet, we can check the SSL requirement via –

https://sslcheck.globalsign.com/en_US 

 

 

Architecture:

Svcutil.exe internally uses the Add service reference functionality.

Stack from svcutil.exe:

 

  • So eventually it’s deep inside the System.ServiceModel Framework DLL.
  • After more digging we find that WCF DLL internally calls System.Net DLL to create the secure Channel.

system_ni!System.Net.TlsStream.ProcessAuthentication(System.Net.LazyAsyncResult)

system_ni!System.Net.Security.SslState.ProcessAuthentication(System.Net.LazyAsyncResult)

  • Which further calls 

System.Net.Security.SecureChannel..ctor()

system_ni!System.Net.Security.SslState.ValidateCreateContext()

  • Deep inside the Service Point Manager class, we do have the default value specified for the SSL protocol usage.

public class ServicePointManager

{

private static SecurityProtocolType s_SecurityProtocolType = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;

}

  • As we can see the default usage is limited to MAX TLS 1.0, in my case my application is using TLS 1.0 always and not using other protocols… for SSL handshake…

However inside the GET part, we do have extra code to use other versions…

public static SecurityProtocolType SecurityProtocol

{

 get

 {

  ServicePointManager.EnsureStrongCryptoSettingsInitialized();  <--------------------------

  return ServicePointManager.s_SecurityProtocolType;

 }

 set

 {

  ServicePointManager.EnsureStrongCryptoSettingsInitialized();

  ServicePointManager.ValidateSecurityProtocol(value);

  ServicePointManager.s_SecurityProtocolType = value;

 }

}

  • Digging more into this method reveals that there is a registry check being done, and if the value in registry is ZERO we use to lower version of TLS, otherwise high version.

private static void EnsureStrongCryptoSettingsInitialized()

{

 if (ServicePointManager.disableStrongCryptoInitialized)

 {

  return;

 }

 lock (ServicePointManager.disableStrongCryptoLock)

 {

  if (!ServicePointManager.disableStrongCryptoInitialized)

  {

   bool flag2 = true;

   int num = RegistryConfiguration.GlobalConfigReadInt("SchUseStrongCrypto", 0);

   flag2 = (num != 1);

   if (flag2)

   {

    ServicePointManager.s_SecurityProtocolType = (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls);  <---------------------

   }

   else

   {

    ServicePointManager.s_SecurityProtocolType = (SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12);

    string value = RegistryConfiguration.AppConfigReadString("System.Net.ServicePointManager.SecurityProtocol", null);

    try

    {

     SecurityProtocolType value2 = (SecurityProtocolType)Enum.Parse(typeof(SecurityProtocolType), value);

     ServicePointManager.ValidateSecurityProtocol(value2);

     ServicePointManager.s_SecurityProtocolType = value2;

    }

    catch (OverflowException)

    {

    }

   }

   ServicePointManager.disableStrongCrypto = flag2;

   ServicePointManager.disableStrongCryptoInitialized = true;

  }

 }

}

  • So clearly we can see that “ SchUseStrongCrypto” registry key will determine the code flow.
  • What is this Registry key- SchUseStrongCrypto ?

  • After some more research looks like this is key is designed to support old protocols for interop scenarios, ideally we should always set this to 1, make sure we don’t use weak cipher and protocol when communicating over SSL channel.

  •  Key is introduced in this security magazine:

http://blogs.technet.com/b/srd/archive/2013/11/12/security-advisory-2868725-recommendation-to-disable-rc4.aspx

  • For some reason this key was not set on the affected box, hence when .Net app will call S Channel dll it will internally run into the if block and eventually use the TLS 1.0 as highest protocols and client were unable to get the MetaData.

Solution:

  • Add the following registry setting and restart the box.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SchUseStrongCrypto

  •  If the application is 32bit running on x64 windows, we need to modify the same key under the HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\ SchUseStrongCrypto

 

Hope this helps!

Debugging Tip: Dump ASMX call payloads

$
0
0

In ASMX services, it is always that request and response payloads are point of interest. As per scenario, we can have crash dumps or manual memory dumps only. How can we read the payloads?

 

Step-1

Get to the thread where we have web service call

Step-2

Dump stack objects on the thread (via !dso command)

Step-3

Find the System.Web.HttpContext type object and dump it

Step-4

Find property called _raw in it and dump it (of type System.Web.HttpRawUploadedContent)

Step-5

Data will appear like the following:

0:026> !do 00000002006e5498
Name:        System.Web.HttpRawUploadedContent
MethodTable: 000007fed82f8488
EEClass:     000007fed7f2dd18
Size:        56(0x38) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_64\System.Web\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Web.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fef7ee3980  4001167       18         System.Int32  1 instance            81920 _fileThreshold
000007fef7ee3980  4001168       1c         System.Int32  1 instance              487 _expectedLength
000007fef7ee8c48  4001169       2c       System.Boolean  1 instance                1 _completed
000007fef7ee3980  400116a       20         System.Int32  1 instance              487 _length
000007fef7ee6888  400116b        8        System.Byte[]  0 instance 00000002006e54d0 _data
000007fed82ff080  400116c       10 ...dContent+TempFile  0 instance 0000000000000000 _file
000007fef7ee3980  400116d       24         System.Int32  1 instance                0 _chunkOffset
000007fef7ee3980  400116e       28         System.Int32  1 instance                0 _chunkLength
We can see _length has value of greater than 0. It means we definitely have some contents captured in it.
 
 
Step-6
Dump _data (of type System.Byte[])
0:026> dc 00000002006e54d0
00000002`006e54d0  f7ee6888 000007fe 000001e7 00000000  .h..............
00000002`006e54e0  453a733c 6c65766e 2065706f 6e6c6d78  <s:Envelope xmln
00000002`006e54f0  3d733a73 74746822 2f2f3a70 65686373  s:s="http://sche
00000002`006e5500  2e73616d 736c6d78 2e70616f 2f67726f  mas.xmlsoap.org/
00000002`006e5510  70616f73 766e652f 706f6c65 20222f65  soap/envelope/"
00000002`006e5520  6e653a73 69646f63 7453676e 3d656c79  s:encodingStyle=
00000002`006e5530  74746822 2f2f3a70 65686373 2e73616d  "http://schemas.
00000002`006e5540  736c6d78 2e70616f 2f67726f 70616f73  xmlsoap.org/soap
 
Step-7
Format the contents and print
0:026> .printf "%ma", 00000002`006e54e0
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
       <s:Body>
              <u:my_download xmlns:u="http://mywebservice">
                     <device1>1231-21212-212</device1>
                     <model>Lumia950</model>
                     <osVer>Version 10.0.3</osVer>
                     <systemVer/>
              </u:my_download>
       </s:Body>
</s:Envelope>

Step-8 (optional)

If we go by netext extension, life will be much easier.

1. dump _data object of type System.Byte[] by !wdo <address>

              >  The payload can be read now.

2. !whttp and can traverse to individual http context objects. It will have all the basic information like request, response, etc. and will have options to go for other relevant objects.

Step-9 (for client side)
When client applications make web service calls, it passes via System.Net.HttpWebRequest.
1. Dump object of type System.Net.HttpWebRequest
2. Dump property called _WriteBuffer of type System.Byte[]
3. In order to read the contents, either dc or !wdo commands can be followed.

Troubleshooting: 413: Request size too large with WCF Service

$
0
0

Troubleshooting: 413: Request size too large with WCF Service

Problem Statement:

A WCF service configured with BasicHpptBinding throws 413: Request size too large error at the client application.

This gives an indication that there is some bottleneck at IIS is occurring while WCF service is configured for all the properties to Max.Int32.

maxBufferSize="2147483647"

maxBufferPoolSize="2147483647"

maxReceivedMessageSize="2147483647"

 

ReadersQuoata values are also set for max:

maxStringContentLength="2147483647"

maxArrayLength="2147483647"

maxBytesPerRead="2147483647"

maxNameTableCharCount="2147483647" 
  
  
  
  
 
  
   

From the initial point, this error is misguiding and will indicate towards IIS bottleneck but later on I discovered that it is WCF pipeline choking it.

Investigation:

From the IIS logs you will see that the status of the request is 413. Generally, status 4XX and 5XX correspond to IIS errors, that is why I am naming this error as misguiding error. You will see that WCF config is set for maximum while you are getting the 413: Request size too large. This exception will also get validated from System.Net traces.

 

System.Net Verbose: 0 : [7340] Exiting ConnectStream#55642016::Read()         -> Int32#0

   
DateTime=2015-09-15T19:18:17.4572956Z

System.Net Error: 0 : [7340] Exception in HttpWebRequest#14015468::GetResponse - The remote server returned an error: (413) Request Entity Too Large..

 

As a next step, we will increase the IIS limit to handle the bigger requests by increasing the following settings:

<add header="Content-type" sizeLimit="100" />

uploadReadAheadSize to
2147483647

but still we are getting the same exception. Now what to do!!!

As a next step, we will collect the dump of the w3wp process under which our WCF service is running and will see if there is any http request which is returning 413. Use DebugDiag to collect the 2 full memory dumps.

1-      As soon as the application starts.

2-      Just after the you get the exception at the client side.

Or you can configure DebugDia to collect the dump at following exception type:

Type: System.ServiceModel.ProtocolException

 

Now we will analyze the dump with WinDbg. I am using the extension netext as WCF services are involved so we can dump all the WCF related objects as well.

First of all, I will dump all the http request in the memory dump to see the status of all the http request objects in the dump.

1c414674       -- 00:00:00 Finished    413  POST     http://localhost:80/CustomerGlobal/GlobalInformation.svc

Here I got a request which is throwing 413 and this indicates that request is passing through the IIS and reaching till WCF layer and WCF service is throwing it back saying request size too large. So it is not a bottleneck at IIS instead WCF service is blocking this request due to its size.

So, the question arises, why WCF service is throwing this exception when it is configured to have Readers Quota values as maximum.

In the second step I will dump the binding details of the WCF service to check the settings.

 

Service Info

================================

Address            :13A922E4

Configuration Name : Test.GlobalInformation

State              :Opened

EndPoints          : 0n1

Base Addresses     : 0n2

Behaviors          : 0n9

Runtime Type       : Test.GlobalInformation

Is Throttled?      : True

Calls/Max Calls    : 0n0/0n128

Sessions/Max       : 0n0/0n800

Events Raised      : No
Event raised

Handles Called     : OnOpeningHandle OnOpenedHandle

Session Mode       : False

Extensions         : 1daac844

 

Service Behaviors

================================

Concurrency Mode   : Single

Instance Mode      : PerSession

Add Error in Faults: false

Max Items Obj Graph: 0n2147483647

Isolation Level    : Unspecified

Session Shutdown   : Automatic

ASP.NET Compatib   : Allowed

Http Get Enabled   : true

Https Get Enabled  : false

Mex Enabled        : true

All Service Behav  : 17ab94ec

 

Service Base Addresses

================================

http://localhost/CustomerGlobal/GlobalInformation.svc

https://localhost/CustomerGlobal/GlobalInformation.svc

 

Channels

================================

Address            : 17ABDCE4

Listener URI       : http:// localhost /CustomerGlobal/GlobalInformation.svc

Binding Name       : http://tempuri.org/:BasicHttpBinding

Aborted            : No

State              : Opened

Transaction Type   : No transaction

Listener State     : Opened

Timeout settings   : Open [00:01:00] Close [00:01:00] Receive: [00:10:00] Send: [00:01:00]

Server Capabilities: SupportsServerAuth [No ] SupportsClientAuth [No ] SupportsClientWinIdent [No ]

Request Prot Level : None

Response Prot Level: None

Events Raised      : No Event raised

Handles Called     : OnOpeningHandle OnOpenedHandle

Session Mode       : False

 

Address            : 17AC5998

Listener URI       : http:// localhost /CustomerGlobal/GlobalInformation.svc

Binding Name       : ServiceMetadataBehaviorHttpGetBinding

Aborted            : No

State              : Opened

Transaction Type   : No transaction

Listener State     : Opened

Timeout settings   : Open [00:01:00] Close [00:01:00] Receive: [00:10:00] Send: [00:01:00]

Server Capabilities: SupportsServerAuth [No ] SupportsClientAuth [No ] SupportsClientWinIdent [No ]

Request Prot Level : None

Response Prot Level: None

Events Raised      : No Event raised

Handles Called     : OnOpeningHandle OnOpenedHandle

Session Mode       : False

 

Address            : 17AC77AC

Listener URI       : https:// localhost /CustomerGlobal/GlobalInformation.svc

Binding Name       : ServiceMetadataBehaviorHttpGetBinding

Aborted            : No

State              : Opened

Transaction Type   : No transaction

Listener State     : Opened

Timeout settings   : Open [00:01:00] Close [00:01:00] Receive: [00:10:00] Send: [00:01:00]

Server Capabilities: SupportsServerAuth [Yes] SupportsClientAuth [No ] SupportsClientWinIdent [No ]

Request Prot Level : EncryptAndSign

Response Prot Level: EncryptAndSign

Events Raised      : No Event raised

Handles Called     : OnOpeningHandle OnOpenedHandle

Session Mode       : False

 

Endpoints 

================================

 

Address            : 17AB85C8

URI                : http:// localhost /CustomerGlobal/GlobalInformation.svc

Is Anonymous       : False

Configuration Name : Test.IGlobalInformation

Type Name          : Test.IGlobalInformation

Listening Mode     : Explicit

Class Definition   : 04113e30 Test.IGlobalInformation

Behaviors          : 17ab94b8

Binding            : 17ab6b34

 

0:000> !wselect * from 17ab6b34

[System.ServiceModel.BasicHttpBinding]

(string)System.String name = BasicHttpBinding

(string)System.String namespaceIdentifier = http://tempuri.org/

System.TimeSpan closeTimeout = -mt 737F7E8C 00000000 00:01:00

System.TimeSpan openTimeout = -mt 737F7E8C 00000000 00:01:00

System.TimeSpan receiveTimeout = -mt 737F7E8C 00000000 00:10:00

System.TimeSpan sendTimeout = -mt 737F7E8C 00000000 00:01:00

System.ServiceModel.Channels.HttpTransportBindingElement httpTransport = 17AB6B7C

System.ServiceModel.Channels.HttpsTransportBindingElement httpsTransport = 17AB6C28

System.ServiceModel.Channels.TextMessageEncodingBindingElement textEncoding = 17AB6C94

System.ServiceModel.Channels.MtomMessageEncodingBindingElement mtomEncoding = 17AB8250

System.ServiceModel.BasicHttpSecurity basicHttpSecurity = 17AB8294

(int32)System.ServiceModel.WSMessageEncoding messageEncoding = 0 (0n0) Text

0:000> !wselect * from 17ab6c28

[System.ServiceModel.Channels.HttpsTransportBindingElement]

(int64)System.Int64 maxBufferPoolSize = 80000 (0n524288)

(int64)System.Int64 maxReceivedMessageSize = 10000 (0n65536)

(bool)System.Boolean manualAddressing = 0 (False)

(bool)System.Boolean allowCookies = 0 (False)

(bool)System.Boolean bypassProxyOnLocal = 0 (False)

(bool)System.Boolean decompressionEnabled = 1 (True)

(string)System.String method = System.Uri proxyAddress = 00000000

(string)System.String realm = System.ServiceModel.Channels.WebSocketTransportSettings webSocketSettings = 17AB6BE0

System.Net.IWebProxy webProxy = 00000000

System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy extendedProtectionPolicy = 17AB6C10

System.ServiceModel.Channels.HttpAnonymousUriPrefixMatcher anonymousUriPrefixMatcher = 00000000

System.ServiceModel.Channels.HttpMessageHandlerFactory httpMessageHandlerFactory = 00000000

(int32)System.Net.AuthenticationSchemes authenticationScheme = 8000 (0n32768) Anonymous

(int32)System.ServiceModel.HostNameComparisonMode hostNameComparisonMode = 0 (0n0) StrongWildcard

(int32)System.Int32 maxBufferSize = 10000 (0n65536)

(int32)System.Net.AuthenticationSchemes proxyAuthenticationScheme = 8000 (0n32768) Anonymous

(int32)System.ServiceModel.TransferMode transferMode = 0 (0n0) Buffered

(int32)System.Int32 maxPendingAccepts = 0 (0n0)

(bool)System.Boolean keepAliveEnabled = 1 (True)

(bool)System.Boolean inheritBaseAddressSettings = 0 (False)

(bool)System.Boolean maxBufferSizeInitialized = 0 (False)

(bool)System.Boolean unsafeConnectionNtlmAuthentication = 0 (False)

(bool)System.Boolean useDefaultWebProxy = 1 (True)

System.TimeSpan requestInitializationTimeout = -mt 737F7E8C 00000000 00:00:00

System.ServiceModel.MessageSecurityVersion messageSecurityVersion = 00000000

(bool)System.Boolean requireClientCertificate = 0 (False) 

 

0:000> !wselect * from 17ab6c94

[System.ServiceModel.Channels.TextMessageEncodingBindingElement]

System.Xml.XmlDictionaryReaderQuotas readerQuotas = 17AB81C0

System.ServiceModel.Channels.MessageVersion messageVersion = 17AB8160

System.Text.Encoding writeEncoding = 1DAA6344

(int32)System.Int32 maxReadPoolSize = 40 (0n64)

(int32)System.Int32 maxWritePoolSize = 10 (0n16)

0:000> !wselect * from 17ab81c0

[System.Xml.XmlDictionaryReaderQuotas]

(int32)System.Int32 maxStringContentLength = 2000 (0n8192)

(int32)System.Int32 maxArrayLength = 4000 (0n16384)

(int32)System.Int32 maxDepth = 20 (0n32)

(int32)System.Int32 maxNameTableCharCount = 4000 (0n16384)

(int32)System.Int32 maxBytesPerRead = 1000 (0n4096)

(int32)System.Xml.XmlDictionaryReaderQuotaTypes modifiedQuotas = 0 (0n0)

(bool)System.Boolean readOnly = 0 (False)

static System.Xml.XmlDictionaryReaderQuotas defaultQuota = 17AB81E4

static System.Xml.XmlDictionaryReaderQuotas maxQuota = 17AB8208 

 

From the highlighted you can see that WCF binding is not taking the values that is configured in the web.config, instead it is taking the default values available with default BasicHttpBinding.

Outcome: 

The binding configured for WCF service is incorrect. Now we will write a new binding itself and will check if that works. 

After writing new binding, everything worked as expected when we configured following settings as shown below:

maxBufferSize="2147483647"

maxBufferPoolSize="2147483647"

maxReceivedMessageSize="2147483647"

 

ReadersQuoata values are also set for max:

 

maxStringContentLength="2147483647"

maxArrayLength="2147483647"

maxBytesPerRead="2147483647"

maxNameTableCharCount="2147483647"

 

So what was the problem with the previous binding!!! After thorough checking, I found that the name of the WCF service was incorrect and this lead the WCF to use the default BasicHttpBinding that will be available even if you do not declare one. 

So if you are getting a 413 with WCF service, following data must be collected at a very first step: 

1-      FREB traces.

2-      WCF traces. 

FREB will highlight the module setting up the 413 and from here we will get the indication if 413 is coming from WCF.

WCF traces will show you if the service is using default binding.

Finally, dump will give you all the values in the binding so you can conclude that WCF service is somehow not reading the configured binding and this is leading to this (413) Request Entity Too Large…

 

Hope this helps!!

 

Ashutosh Tripathi

MSFT(India)

CAPI2 code that will try to translate a CSP handle into a CNG handle

$
0
0

This blog post is with respect to CAPI2 and CNG.

We might encounter situations where in our CAPI2 code we see that the CSP handle being used is CNG. This might be tricky to understand as we are using a CAPI2 provider.

The reason is: There are many places in CAPI2 code that will try to translate a CSP handle into a CNG handle. This is the way the code is implemented and the selection criteria is based either on the ALG_ID or on the nature of the provider. If ALG_ID indicates CNG OR if a CNG provider is used, the CNG flag is set.

By default for OID "1.2.840.113549.1.1.11", if we don't use a CNG provider and do not initialize the CRYPT_OID_INFO structure (that contains the ALG_ID attribute), it sets the values as shown below: 

 0:000> dx -r1 (*((CRYPT32!_CRYPT_OID_INFO *)0x768bc02c))

(*((CRYPT32!_CRYPT_OID_INFO*)0x768bc02c))       [Type: _CRYPT_OID_INFO]

    [+0x000] cbSize           : 0x24

    [+0x004] pszOID          : 0x768bc2e0 : "1.2.840.113549.1.1.11" [Type: char *]

    [+0x008] pwszName    : 0x768bc2cc : "sha256RSA" [Type: wchar_t *]

    [+0x00c] dwGroupId    : 0x4

    [+0x010] dwValue       : 0xffffffff

    [+0x010] Algid            : 0xffffffff

    [+0x010] dwLength     : 0xffffffff

    [+0x014] ExtraInfo        [Type: _CRYPTOAPI_BLOB]

    [+0x01c] pwszCNGAlgid    : 0x768bb83c : "SHA256" [Type: wchar_t *]

    [+0x020] pwszCNGExtraAlgid : 0x768bc53c : "RSA" [Type: wchar_t *]

 

0:000> dx -r1 (*((CRYPT32!_CRYPT_ALGORITHM_IDENTIFIER*)0x15f688))

(*((CRYPT32!_CRYPT_ALGORITHM_IDENTIFIER*)0x15f688))                [Type: _CRYPT_ALGORITHM_IDENTIFIER]

    [+0x000] pszObjId         : 0x1123424 : "1.2.840.113549.1.1.11" [Type: char *]

    [+0x004] Parameters       [Type: _CRYPTOAPI_BLOB]

 

For example, CryptSignCertificate() checks the ALG_ID and based on its value it sets the CNG flag. In the above case since Algid is 0xffffffff (CALG_OID_INFO_CNG_ONLY) so the CNG flag is set.

If we set the ALG_ID to say CALG_SHA_256, the CNG route is not followed. The issue here is not with the CAPI2 CSP, but it's the way the CAPI2 code is implemented.


Authenticated Encryption - CAPI2 does not support authenticated encryption mode

$
0
0

Authenticated Encryption (AE) or Authenticated Encryption with Associated Data (AEAD) is a block cipher mode of operation which simultaneously provides confidentiality, integrity, and authenticity assurances on the data; decryption is combined in single step with integrity verification. - Referenced from Wikipedia. See reference below.

Crypto API’s or CAPI2 does not support authenticated encryption mode. This means there are no API's in CAPI2 that can be used to implement authenticated encryption.
It can only be done using CNG where BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure can be passed to CNG API’s for example BCryptEncrypt() to implement authenticated encryption.

Additional Info:

CryptoAPI’s such as CMS functions – they don’t have a way to pass an authenticated cipher mode structure. The API CryptMsgOpenToEncode or CryptMsgUpdate bails out with CRYPT_E_INVALID_MSG_TYPE when CMS encrypted message type is passed to it.

The open forum "Authenticated Symmetric Encryption in .NET" (Link: http://blogs.msdn.com/b/shawnfa/rss.aspx?Tags=Cryptography) shows a way to implement authenticated encryption in .Net. The open source also uses the implementation of AuthenticatedAesCng which is in Security.Cryptography.dll and is built on top of  CNG.

In summary, authenticated encryption was implemented over CNG's BCrypt APIs.

References:
https://msdn.microsoft.com/en-us/library/windows/desktop/cc562981(v=vs.85).aspx
https://en.wikipedia.org/wiki/Authenticated_encryption

XML signature & digest verification

$
0
0

.NET SignedXml class was only designed to adhere to the XMLDSIG specification. It has no knowledge of SAML, SAML 2.0, SOAP or any other higher level XML protocol. If you are using SignedXml for any other protocol other than XMLDSIG, the scenario is not supported.

SignedXml.CheckSignature method determines whether the signature property verifies for the specified key.
Ideally the CheckSignature method does two things:

1. Matches Signature
2. Validates digest value

How to match the signature? See code below.

For example consider the string named xmlString which has the signed XML.
We can do the following to get the SignedXml object say verifier.

XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.LoadXml(xmlString);
SignedXml verifier = new SignedXml();

Next, the SignedXml class must be given the value of the signature it is to validate. This can be done by looking for elements with the tag name of Signature. See code below:
verifier.LoadXml(doc.GetElementsByTagName("ds:Signature")[0] as XmlElement);

// Get the certificate from the XML. Say it is rawCertData (byte array)
X509Certificate2 x509 = new X509Certificate2();
x509.Import(rawCertData);

// Get the public key
AsymmetricAlgorithm key = x509.PublicKey.Key;
// =================================================================================
if (key == null)
    throw new ArgumentNullException("key");

SignatureDescription signatureDescription = CryptoConfig.CreateFromName(verifier.SignatureMethod) as SignatureDescription;
if (signatureDescription == null)
    throw new CryptographicException("123456789"); // "123456789" is an arbitrary value.

// Let's see if the key corresponds with the SignatureMethod
Type ta = Type.GetType(signatureDescription.KeyAlgorithm);
Type tb = key.GetType();
if ((ta != tb) && !ta.IsSubclassOf(tb) && !tb.IsSubclassOf(ta))
    // Signature method key mismatch
    return;

HashAlgorithm hashAlgorithm = signatureDescription.CreateDigest();
if (hashAlgorithm == null)
    throw new CryptographicException("123456789");

//====================================================
byte[] digestedSignedInfo = null;

string baseUri = (doc == null ? null : doc.BaseURI);
XmlResolver resolver = new XmlSecureResolver(new XmlUrlResolver(), baseUri);
XmlDocument docProcessed = PreProcessElementInput(verifier.SignedInfo.GetXml(), resolver, baseUri);
Transform c14nMethodTransform = verifier.SignedInfo.CanonicalizationMethodObject;
c14nMethodTransform.Resolver = resolver;
c14nMethodTransform.LoadInput(docProcessed);
digestedSignedInfo = c14nMethodTransform.GetDigestedOutput(hashAlgorithm);
//====================================================
AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = signatureDescription.CreateDeformatter(key);

// Verify the signature
bool isSignatureOK = asymmetricSignatureDeformatter.VerifySignature(digestedSignedInfo, verifier.SignatureValue);
if (!isSignatureOK)
{
    Console.WriteLine("Signature invalid.");
}

Here is the code to verify the digest value.

string ObjId = ((System.Security.Cryptography.Xml.Reference)(verifier.SignedInfo.References[0])).Uri.Replace("#", "");
XmlElement xRefElement = verifier.GetIdElement(doc, ObjId);
XmlDocument xRefElementDoc = new XmlDocument();
xRefElementDoc.PreserveWhitespace = true;
xRefElementDoc.LoadXml(xRefElement.OuterXml);
// Get the digest value - customers code
byte[] rgb1 = ((System.Security.Cryptography.Xml.Reference)(verifier.SignedInfo.References[0])).DigestValue;

string strDigestValue = Convert.ToBase64String(rgb1);
// Verify the digest
bIsValid = VerifyDigest(xRefElementDoc, strDigestValue);

if (!bIsValid)
{
    Console.WriteLine("The computed digest & the digest present in the XML does not match.");
}

bool VerifyDigest(XmlDocument xAssertionDoc, string strDigestValue)
{
    // if xmlsignature is available in the XML then remove the signature node
    xAssertionDoc.DocumentElement.RemoveChild(xAssertionDoc.GetElementsByTagName("ds:Signature")[0]);

    //create c14n instance and load in xml file
    XmlDsigC14NTransform c14n = new XmlDsigC14NTransform(false);
 
   // Loading the Assetion Node into the canonicalization
   c14n.LoadInput(xAssertionDoc);

   //get canonalised stream
   Stream canonalisedStream = (Stream)c14n.GetOutput(typeof(Stream));

   //Creating SHA1 object to get Hash
   SHA1 sha1 = new SHA1CryptoServiceProvider();

   Byte[] output = sha1.ComputeHash(canonalisedStream);

   //Getting the Base64 version of digest Value computed
   string xmlDigestValue = Convert.ToBase64String(output);

   // If Computed and original digest value matches then return true else false.
   if (xmlDigestValue == strDigestValue)
   {
       Console.WriteLine("The computed hash matches with the Digest contained in the XML.");
       return true;
   }

   return false;
}

Reference:
https://msdn.microsoft.com/en-us/library/kd4wwa16(v=vs.110).aspx

WCF: Suppress multiple 401s in windows authentication

$
0
0

This case study is about discussing 401 challenges in case of windows authentication for WCF service consumers. It covers the perspectives from both IIS hosted and self-hosted service applications. My intention is to show case on suppressing multiple 401s in windows authentication.

 

WCF service application

  • Hosted on IIS 7.5
  • Windows authentication is enabled with Providers order Negotiate, NTLM respectively
                       
 
  • Binding configuration

                       <basicHttpBinding>

                             <binding name="BasicHttpBinding_IService1">

                                     <security mode="Transport">

                                            <transport clientCredentialType="Windows" />

                                     </security>

                            </binding>

                       </basicHttpBinding>

  • Application pool identity is set to NetworkService account

                              

Client application – console

  • App.config

                         <basicHttpBinding>

                                 <binding name="BasicHttpBinding_IService1">

                                           <security mode="Transport">

                                                      <transport clientCredentialType="Windows" />

                                           </security>

                                 </binding>

                         </basicHttpBinding>


                         <client>

                                 <endpoint address="https://myserver:2092/WcfPersistnonnNtlmService/Service1.svc"

                                           binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"

                                           contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />

  • Program.cs

                          static void Main(string[] args)

                          {

                                      // initialize proxy

                                      var p = new ServiceReference1.Service1Client("BasicHttpBinding_IService1");

 

                                      // make service method calls in a loop

                                      for (int i = 0; i <= 2; i++)

                                      {

                                              Console.WriteLine(p.GetData(i));

                                      }

 

                                      // close the proxy

                                      p.Close();

 

                                     Console.ReadLine();

                          }

  • When we call this client application from a remote machine and simultaneously allow Fiddler to capture traffic, we will see the following sequence of events are recorded.

                          

Observation

  • The client application went through the service method calls 3 times.
  • Each service method call is preceded by a 401 challenge. Why?
  • 1st call:  Client did not pass any token. Hence, service marked the request as unauthorized – resulted in http status code 401.

                          

  • 2nd call: Client passed Negotiate token. Hence, service accepted and processed the request – resulted http status code 200               

                        

  • It means client will have to pay a penalty for every request.
  • Over here, client is expected to make 3 communications to server. However, it ended in 3*2 = 6 number of communications.
  • Each 200 request has passed a Negotiate token.

 

Let us think on a larger perspective. If expected number of calls from client to server machines are more, then client will have to bear 2 times of the expected number of communication. This could cause performance bottleneck when network bandwidth is less. So, the ideal next step from application side will be to verify whether there are any ways we can reduce the traffic. Yes, there is a way.

1.       Go to the IIS application
2.       Select Configuration Editor under Management [in Features view]
3.       Set authPersistNonNTLM to true

                            

4.       Apply

 

Now, let us go to the client application and observe the communication in Fiddler.

                   

 

It resulted in the following:

There is only one 401 and 3 normal service calls are followed by.

If we will observe the 1st 200 request, we can say it has a negotiate token passed for authorization.

If we will observe the 2nd, 3rd onwards 200 requests, we will not see any negotiate token passed.

How does service authenticate the request then?

When authPersistNonNTLM set to true

1. It changes Kerberos authentication from request based to session based

2. TCP session is used to identify the authenticated client

          More details can be found the article (http://blogs.msdn.com/b/benjaminperkins/archive/2011/10/31/kerberos-authpersistnonntlm-authentication-request-based-vs-session-based-authentication.aspx).

Self-hosted

Can the similar improvements (as of IIS) be achieved for self-hosted WCF service application?

  • There is no way to suppress Kerberos negotiation for a self-hosted WCF service application.
  • However, there is a way for NTLM authentication which will behave the same as IIS configuration change. The authPersistNonNTLM setting seems to be something specific to IIS.
  • Looking at HttpListener's properties, they have a similar setting for NTLM called UnsafeConnectionNtlmAuthentication, but there is no such setting for Kerberos.
  • WCF supports the NTLM setting on HttpTransportBindingElement, but there’s no Kerberos setting for us to hook into.

Case 1

  • Service is self-hosted the following binding configuration.

     <customBinding>

             <binding name="myBinding">

          <textMessageEncoding />

          <transactionFlow />

          <httpsTransport authenticationScheme="Negotiate" />

             </binding>

      </customBinding>

  • When we run the client application on a remote machine, fiddler indicates the http traffic like:

                     

  • We can see there are three 401s before a 200.
  • Each success http request (200) ensures that it passes a Kerberos negotiate token.

 Case 2

  • Service is self-hosted the following binding configuration.

     <customBinding>

        <binding name="myBinding">

          <textMessageEncoding />

          <transactionFlow />

          <httpsTransport authenticationScheme="Ntlm" unsafeConnectionNtlmAuthentication="true" />

        </binding>

      </customBinding> 

  • When we run the client application on a remote machine, fiddler indicates the http traffic like:

                    

  • We can say there are 401s only 1 time.
  • The 1st success call (http status code 200) only presents the NTLM authorization token.
  • If we observe the 2nd / 3rd http requests, they do not present any authorization token.
  • Because of the httpsTransport property unsafeConnectionNtlmAuthentication, NTLM authentication is performed once on each TCP operation. Hence, we made it TCP based now.
  • It means TCP session preserved the past NTLM authorization token.

Just an FYI on a generic traffic flow:

                Network -> TCP -> HTTP

  • This time client application had to bear one time overhead of 401.
  • Hence, network traffic is optimized now.

 

 

 I hope this helps.

 

 

HTTP 503 Service Unavailable due to Reserved URI

$
0
0

Problem Statement

I have a WCF REST service hosted on the IIS. The URL to invoke the method of the REST service looks like – http://sauravpc1.fareast.corp.microsoft.com/RestWCF/RestServ/GetData.

Now, while trying to invoke this URL or browse it directly I get an error – 503 Service Unavailable.

I am able to browse the help page of the service and the above address is present as the correct endpoint.

Again, if the method name in the WCF service is changed to anything else (ex- GetData1 or GetRestData etc.) it works absolutely fine.

So there is an issue with the particular URI or that particular method name.

 

Troubleshooting

Step 1:

We can start with verifying the entries in the IIS logs for both the URLs i.e. while using GetData and while using any other method name, say GetData1.

I noticed in my case the URI with GetData did not have its corresponding entries in IIS logs. However, the GetData1 was present.

 

Step 2:

We should check the HTTPERR log file to find out the reason why the request was not getting processed for GetData method.

To do that open Run (Windows + R) and type “Logfiles” and press enter. You will have a folder named HTTPERR. Inside that open the most recent file.

If you don’t see the most recent requests, please open the command prompt in an admin mode and type – NETSH HTTP FLUSH LOGBUFFER. Now you will be able to find the most recent entries in the Log File.

For my case there were no reason code available. It was just N/A.

        2015-11-24 00:52:31 209.232.245.109 61200 192.168.1.9 80 HTTP/1.1 GET /RestWCF/RestServ/GetData 503 - N/A -

 

Step 3:

From the above steps we could understand the request was failing at the HTTP.SYS layer but we do not know why. So to better understand we can make use of the tool PerfView. It can be downloaded from here.

To capture perfview data follow the below steps:

a)    Open PerfView.exe

b)    Select menu “Collect” -> “Collect”

c)   Enable options as below.  
  
  
  
  
d)    Hit the button Start Collection and browse the URL 2-3 times. Once done, stop the data collection.

e)  Let the merging complete and then open the “Events”.

f)    Search with the string – httpstatus=”503”. We can see 3 requests which had failed.

 


 g)   Copy the activity ID from any one of the request and then search again with that string.

h)    We see the reason as – UrlGroupLookupFailed.

 

Step 4:

Next, please open a command prompt in admin mode and run the command – NETSH HTTP SHOW URLACL

We see:

        Reserved URL: http://+:80/RestWCF/RestServ/GetData/    -> This URL seems reserved which is causing the error.

        User: domain\user

        Listen: Yes

        Delegate: No

        SDDL: D:(A;;GX;;;S-1-5-21-3704706833-119-42420-1001)

 

This entry needs to be deleted from the list of reserved URLs.

To do that run the command -  netsh http delete urlacl http://+:80/RestWCF/RestServ/GetData/  

 

Step 5:

Once done, we just need to remove the existing application from IIS and host it again.

 

This should take care of the issue and we should be able to browse the GetData URL again.

 

Hope this helps!

 

Created by:

Saurav Dey (MSFT)

Negotiate V/s NTLM

$
0
0

Definitions:

Negotiate: Microsoft Negotiate is a security support provider (SSP) that acts as an application layer between Security Support Provider Interface (SSPI) and the other SSPs.

When an application calls into SSPI to log on to a network, it can specify an SSP to process the request.

If the application specifies Negotiate, Negotiate analyzes the request and picks the best SSP to handle the request based on customer-configured security policy.

Currently, the Negotiate security package selects between Kerberos and NTLM.

Negotiate selects Kerberos unless it cannot be used by one of the systems involved in the authentication or the calling application did not provide sufficient information to use Kerberos.

 

NTLM: Windows Challenge/Response (NTLM) is the authentication protocol used on networks that include systems running the Windows operating system and on stand-alone systems.

 

 

This is how challenge based authentication works (NTLM, BASIC Selected)

System.Net Information: 0 : [15824] AcquireCredentialsHandle(package  = NTLM, intent   = Outbound, authdata = System.Net.SafeSspiAuthDataHandle)

 

Stack

============

   at System.Net.SSPIWrapper.AcquireCredentialsHandle(SSPIInterface SecModule, String package, CredentialUse intent, SafeSspiAuthDataHandle& authdata)

   at System.Net.NTAuthentication.Initialize(Boolean isServer, String package, NetworkCredential credential, String spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)

   at System.Net.NTAuthentication..ctor(Boolean isServer, String package, NetworkCredential credential, String spn, ContextFlags requestedContextFlags, ContextAwareResult context, ChannelBinding channelBinding)

   at System.Net.NTAuthentication..ctor(String package, NetworkCredential networkCredential, SpnToken spnToken, WebRequest request, ChannelBinding channelBinding)

   --->at System.Net.NtlmClient.DoAuthenticate(String challenge, WebRequest webRequest, ICredentials credentials, Boolean preAuthenticate)

   --->at System.Net.NtlmClient.Authenticate(String challenge, WebRequest webRequest, ICredentials credentials)

   --->at System.Net.AuthenticationManagerDefault.Authenticate(String challenge, WebRequest request, ICredentials credentials)

 

 

This is how challenge based authentication works (Negotiate, NTLM, BASIC Selected)

===========

System.Net Information: 0 : [17644] AcquireCredentialsHandle(package  = Negotiate, intent   = Outbound, authdata = System.Net.SafeSspiAuthDataHandle)

    DateTime=2015-12-25T10:12:06.7823985Z

 

Callstack=  

   at System.Net.SSPIWrapper.AcquireCredentialsHandle(SSPIInterface SecModule, String package, CredentialUse intent, SafeSspiAuthDataHandle& authdata)

   at System.Net.NTAuthentication.Initialize(Boolean isServer, String package, NetworkCredential credential, String spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)

   at System.Net.NTAuthentication..ctor(Boolean isServer, String package, NetworkCredential credential, String spn, ContextFlags requestedContextFlags, ContextAwareResult context, ChannelBinding channelBinding)

   at System.Net.NTAuthentication..ctor(String package, NetworkCredential networkCredential, SpnToken spnToken, WebRequest request, ChannelBinding channelBinding)

   --->at System.Net.NegotiateClient.DoAuthenticate(String challenge, WebRequest webRequest, ICredentials credentials, Boolean preAuthenticate)

   --->at System.Net.NegotiateClient.Authenticate(String challenge, WebRequest webRequest, ICredentials credentials)

   --->at System.Net.AuthenticationManagerDefault.Authenticate(String challenge, WebRequest request, ICredentials credentials)

   at System.Net.AuthenticationState.AttemptAuthenticate(HttpWebRequest httpWebRequest, ICredentials authInfo

 

So all mystery lies within this method:

============

System.Net.AuthenticationManagerDefault.Authenticate(String challenge, WebRequest request, ICredentials credentials)

This method reads the servers challenge and decide which scheme will be used by client.

 

When to use what?

NTLM: Good option to use on standalone/workgroup machines.

Negotiate: Good option to use on Domain/Network joined client machines.

Kerberos:

Advanced and highly secure authentication scheme for Network joined machines.

 

Note:

As we can see above, if Negotiate is selected along with NTLM and BASIC at IIS. Client will always pick the highest protocol and in this case NEGOTIATE scheme (internally token sent can be NTLM).

Like this:

Authorization: Negotiate TlRMTVNTUAADAAAAGAAYAIIAAABUAVQBmgAAAAAAAABYAAAAEgASAFgAAAAYABgAagAAABAAEADuAQAANYKI4gYBsR0AAAAPTDrRMsXwFwu0tIpp5EAEAWQAZgBmAHIAYQBkAG0AaQBuADEANgA5ADYAOQAtAEsASQBCAEIARQBZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABG5b7SNAeavLgPYfUsUc2EBAQAAAAAAAIUnRQrjMdEBGzZAyJKwTz4AAAAAAgAQAEYASABGAEcARAAtAE4AVAABAA4ARgBTAC0AQQBQAFAANwAEABgAZgBpAG4AbgBlAGcAYQBuAC4AYwBvAG0AAwAoAGYAcwAtAGEAcABwADcALgBmAGkAbgBuAGUAZwBhAG4ALgBjAG8AbQAFABgAZgBpAG4AbgBlAGcAYQBuAC4AYwBvAG0ABwAIAIUnRQrjMdEBBgAEAAIAAAAIADAAMAAAAAAAAAAAAAAAADAAAFtbnjnjb9OP7hJ0W2ZOQ6ltA1SrydJ3x0a9GREZ+6LiCgAQAAAAAAAAAAAAAAAAAAAAAAAJADIASABUAFQAUAAvAGYAcwAtAGEAcABwADcALgBmAGkAbgBuAGUAZwBhAG4ALgBjAG8AbQAAAAAAAAAAAAAAAAA66vnSb1h9qY85OmxzJoVu

 

In SSPI terms, this means selecting (the call to AcquireCredentialsHandle to have) “Negotiate” instead of “NTLM”. 

  

When we need to use NTLM, we need to make sure Negotiate is not enabled on IIS hosting application.

  

This is now NTLM authentication works:

http://blogs.msdn.com/b/chiranth/archive/2013/09/21/ntlm-want-to-know-how-it-works.aspx

http://www.innovation.ch/personal/ronald/ntlm.html

  

Credential Cache:

https://msdn.microsoft.com/en-us/library/system.net.credentialcache(v=vs.110).aspx

The CredentialCache class stores credentials for multiple Internet resources. Applications that need to access multiple resources can store the credentials for those resources in a CredentialCache instance that then provides the proper set of credentials to the Internet resource when required. When the GetCredential method is called, it compares the Uniform Resource Identifier (URI) and authentication type provided with those stored in the cache and returns the first set of credentials that match.

 

So it’s good to use Credential Cache when at the application startup we want to set credentials for accessing multiple applications.

For challenge based authentication like BASIC and NTLM, when consuming the single web service, we can stick with below code:

NetworkCredential cred = new NetworkCredential(userName, pass, domain);

myProxy.Credentials = cred;

 

This code will create the token based on challenge received via server, usually BASIC or NTLM when talking to web service hosted on IIS.

 

On other side:

Below code will store the CREDENTIAL in cache for specific URL.

 NetworkCredential cred = new NetworkCredential(userName, pass, domain);

            CredentialCache cc = new CredentialCache();

            string url = System.Configuration.ConfigurationManager.AppSettings["url"].ToString();

            cc.Add(new Uri(url), "Negotiate", cred);

            cc.Add(new Uri(url), "NTLM", cred);

            cc.Add(new Uri(url), "Kerberos", cred);

            cc.Add(new Uri(url), "Digest", cred);

 myProxy.Credentials = cc;

 

Remember:

When the GetCredential method is called, it compares the Uniform Resource Identifier (URI) and authentication type provided with those stored in the cache and returns the first set of credentials that match.

 

If we still need to use Credential Cache then we need to use below code to make sure we have relevant credential in cache depending on challenge received via server.

            NetworkCredential cred = new NetworkCredential(userName, pass, domain);

            CredentialCache cc = new CredentialCache();

            string url = System.Configuration.ConfigurationManager.AppSettings["url"].ToString();

            cc.Add(new Uri(url), "Negotiate", cred);

            cc.Add(new Uri(url), "NTLM", cred);

            cc.Add(new Uri(url), "Kerberos", cred);

            cc.Add(new Uri(url), "Digest", cred);

            myProxy.Credentials = cc;

 

Also Remember, avoid using other schemes (like Negotiate, Kerberos, Digest), if server is only supposed to take NTLM...

 

Another important thing to remember is NTLM Token validation

Once token is received as part of final challenge from client, it is sent to DC for validation…

 

This is how it looks in Network traces from server side:

  

 

Initial request to DC:

 

 

Response from DC, indicates whether NTLM Token is valid or not…

 

 

 

And eventually based on result, application send a 200 back to client.

 

I assume we call it as Non Interactive Log on process:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa378779(v=vs.85).aspx

 

Noninteractive authentication is performed when an application uses the Security Support Provider Interface (SSPI) and a security package to establish a secure network connection.

Noninteractive authentication is the mechanism at work when a user connects to multiple machines on a network without having to re-enter logon information for each machine

 

Screenshot and Sample app:

https://onedrive.live.com/redir?resid=7A701D22BF5927B8!2149&authkey=!ANuAVHH1iS3_Dao&ithint=folder%2cdocx 

 

I hope this helps on when to use which scheme and how configurations needs to be done at IIS level.

 

Thanks

Saurabh

Viewing all 164 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>