views:

852

answers:

3

I need to call a web service running in a Windows domain that is different than the one the (Windows Forms) client is running in. The web service is secured using Windows authentication.

The domain credentials for web service's domain are saved in the client's user profile (Saved user names and passwords in XP), but I have not been able to figure out how to use these saved credentials when calling the web service. I've found plenty of examples using

WebService1.Credentials = System.Net.CredentialCache.DefaultCredentials (this doesn't work because it's the credentials for the local domain)

or

WebService1.Credentials = new NetworkCredentials(username, pwd, domain) (were the user name, password, domain are hard-coded).

I've read up on using CredEnumerate and CredRead using the Windows API, but don't know how (or if) I can convert a PCREDENTIAL to a managed NetworkCredential (ReadCred won't return passwords for stored domain credentials)

Does anyone here on SO know how to do this?

Thanks!

A: 

I do not believe you can use them directly. I believe you have to prompt the user for them and then once the user provides them, you can prompt again. Here is an article on how to do that. Afterall, if you could just grab the credentials from the DPAPI, that would defeat the purpose. :)

Here is some info. in case it helps ...

The manager that holds the data in the DPAPI is called the Key Manager. You can access the key manager from the UI by doing a start->run ...

rundll32.exe keymgr.dll, KRShowKeyMgr

In general, the API for the the place where the authentication functions are stored is ADVAPI32. You might be able to find something in there to help you. Here's a decent article on that API.

When you add your credentials to the CredentialCache, you might also try the authType "Negotiate" as described here.

Sorry, this is just a research dump -- I didn't find a spot on answer, but hopefully some of this might help you. GL.

JP Alioto
A: 

I don't think you can use them in code, afaik your only options are:

  1. Hardcoding the username/pwd as you have written (in most cases a big no-no).
  2. Setting up Kerberos with Trust between both domains and machines so that the users domain account can call the service.
mundeep
+1  A: 

OK, I found a solution so I'm going to answer my own question:

First, I found that by using the new WCF-style proxy class "Service Reference" allows you to set security settings in the app.config file. The key (in my scenario) is to set the mode for transport credentials only, specify Windows as the type, and identify the domain in the realm attribute:

<system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="Service1Soap" closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
                    bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="TransportCredentialOnly">
                        <transport clientCredentialType="Windows" realm="mydomain.com" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://server.mydomain.com/HelloWorldSvc/Service1.asmx"
                binding="basicHttpBinding" bindingConfiguration="Service1Soap"
                contract="ServiceReference1.Service1Soap" name="Service1Soap" />
        </client>
    </system.serviceModel>

Next I used Hernan de Lahitte's excellent wrapper for the CredUIPromptForCredentials API function to prompt the user for their credentials if a security exception is thrown and store it in their profile:

ServiceReference1.Service1SoapClient c = new Service1SoapClient();
retry:
try
{
    MessageBox.Show(this, c.HelloWorld());
}
catch (System.ServiceModel.Security.MessageSecurityException securityException)
{
    UserCredentialsDialog creds = new UserCredentialsDialog("*.mydomain.com", "My App",
                                                                        "Enter your enterprise credentials.  Enter your user name as \"MyDomain\\username\"");
    creds.Flags = UserCredentialsDialogFlags.Persist;
    if (creds.ShowDialog() == DialogResult.OK)
    {
        goto retry;
    }
}

c.Close();

Yes, that's a "goto". **GASP** :)

Jim Harte