Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / ASP.NET / Web Services / September 2004

Tip: Looking for answers? Try searching our database.

Problem when switching from SoapClient to WebServicesClientProtocol

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Wil Rodriguez - 31 Aug 2004 01:46 GMT
I have a working C# class library that acts as a soap client. It was
implemented by extending the SoapClient object which gave me full control
over the formatting of the soap body:

   public class InventoryProxy : SoapClient
   { ...working code goes here...}

The vendor providing the web service will be requiring that all soap
messages (requests) be digitally signed, and later on also encrypted.  So,
I've switched the base class for my library from SoapClient to
Microsoft.Web.Services2.WebServicesClientProtocol. The only problem that
I've run into so far is trying to get the message body formatted properly.
It all boils down to the "this.invoke()" method requiring an object array
and me not being able to figure out how to populate this array such that the
resulting soap body looks like this:

<soap:Body>
<InventoryRequest xmlns="Vendor.WebServices/Inventory>
 <Widgets>
   <Widget DeptId="2">Hammer</Widget>
   <Widget DeptId="2">Nail</Widget>
 </Widgets>
</InventoryRequest>
</soap:Body>

The closest I've gotten so far has been passing in a string array that gets
cast as an object array when calling the this.invoke() method:

//populate the test widgets array
string[] widgets = new string[] {"<Widget DeptId=\"2\">Hammer</Widget>",
"<Widget DeptId=\"2\">Nail</Widget>"};

//pass the array into the invoking function
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://my.vendor
.com/Inventory.asmx", RequestNamespace="Vendor.WebServices/Inventory",
ResponseElementName="InventoryResponse",
ResponseNamespace="Vendor.WebServices/Inventory",
Use=System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public object[]
InventoryRequest([System.Xml.Serialization.XmlArrayAttribute(IsNullable=true
)] [System.Xml.Serialization.XmlArrayItemAttribute("Widget",
IsNullable=false)] string[] widgets) {
   return this.Invoke("InventoryRequest", new object[] {widgets});
}

But, when I trace the client input the soap body looks like this (and is
rejected by the web service):

<soap:Body>
<InventoryRequest xmlns="Vendor.WebServices/Inventory">
 <Widgets>
   <Widget>&lt;Widget DeptId="2" &gt;Hammer&lt;/Widget&gt;</Widget>
   <Widget>&lt;Widget DeptId="2"&gt;Nail&lt;/Widget&gt;</Widget>
 </Widgets>
</InventoryRequest>
</soap:Body>

It looks like the parameters in the Widgets[] array are treated as Inner
Text.  If I just put the product name in the string array (i.e. string[]
widgets = new string[] {"Hammer", "Nail"};) then I get XML elements that
look normal, but then I'm missing the required DeptId attributes for each
Widget element:

<soap:Body>
<InventoryRequest xmlns="Vendor.WebServices/Inventory">
 <Widgets>
   <Widget>Hammer</Widget>
   <Widget>Nail</Widget>
 </Widgets>
</InventoryRequest>
</soap:Body>

Yes, I am a still a newbie, so any help/pointers on what I'm doing wrong or
how I can better control the formatting of the message body would be highly
appreciated.

Thanks,

Wil
SA - 02 Sep 2004 19:17 GMT
Wil,

I am not sure what causes the problem, but I see no need to use the
WebServicesClientProtocol as the base class instead of the SoapClient.

I use SoapClient as the base class for my clients, and there is no issue in
signing and encrypting the messages.

Signature

Sven

> I have a working C# class library that acts as a soap client. It was
> implemented by extending the SoapClient object which gave me full control
[quoted text clipped - 29 lines]
>
> //pass the array into the invoking function

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://my.vendor
> .com/Inventory.asmx", RequestNamespace="Vendor.WebServices/Inventory",
> ResponseElementName="InventoryResponse",
> ResponseNamespace="Vendor.WebServices/Inventory",
> Use=System.Web.Services.Description.SoapBindingUse.Literal,
> ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
> public object[]

InventoryRequest([System.Xml.Serialization.XmlArrayAttribute(IsNullable=true
> )] [System.Xml.Serialization.XmlArrayItemAttribute("Widget",
> IsNullable=false)] string[] widgets) {
[quoted text clipped - 35 lines]
>
> Wil
Wil Rodriguez - 02 Sep 2004 20:11 GMT
Thanks for the reply.

The reason I switched the base class was because every SOAP example I
found that included a digital signature and encryption didn't use the
SoapClient.  I guess this is one of those times when Google failed me.
I would appreciate any links to examples of digitally signing, and
encrypting, a soap message using only the SoapClient.

Wil

> Wil,
>
[quoted text clipped - 3 lines]
> I use SoapClient as the base class for my clients, and there is no issue in
> signing and encrypting the messages.
Yves Langisch - 03 Sep 2004 08:30 GMT
Sven,

I thought in order to able to add security tokens to a request I need
the proxy's SoapContext. That's what I read in MS Tutorials. A small
code snippet which shows how to sign a request with a SoapClient based
proxy would be very appreciated.

Thanks!
Yves

> Wil,
>
[quoted text clipped - 3 lines]
> I use SoapClient as the base class for my clients, and there is no issue in
> signing and encrypting the messages.
SA - 03 Sep 2004 18:25 GMT
Yves, Wil,

In order to sign or encrypt, you need the envelope's Soap Context. Here's a
code snippet from my code (not sure if any samples exist online):

Public Class SoapProxy
   Inherits SoapClient

   <snip lots of stuff />

   Public Function SampleWebMethod(ByVal Message As MyClass) As MyResults
       ' Create new SoapEnvelope
       Dim env As New SoapEnvelope
       ConfigureEnvelope(env)
       env = MyBase.SendRequestResponse("SampleWebMethod", env)
       ' Do stuff with the reply
   End Function

Protected Sub ConfigureEnvelope(ByRef Envelope As SoapEnvelope)

If (Not Envelope Is Nothing AndAlso _
 Me.IsConfigured) Then

 ' Configure the context of the envelope
 If (Me.Configuration.SendCredentials) Then
  Envelope.Context.Security.Tokens.Add( _
   New UsernameToken(m_Configuration.Username, m_Configuration.Password,
PasswordOption.SendHashed))
 End If

Else
 ' TODO: Raise exception if envelope is Nothing or proxy is not configured
End If     ' Envelope Is Nothing AndAlso Me.IsConfigured

End Sub

End Class

HTH,

Signature

Sven

> Sven,
>
[quoted text clipped - 13 lines]
> > I use SoapClient as the base class for my clients, and there is no issue in
> > signing and encrypting the messages.
Wil Rodriguez - 03 Sep 2004 20:44 GMT
SA,

Thanks for the code snippet.  It's exactly what I've been looking for.
I've attached some simple demo code in CSharp that shows how to sign
and/or encrypt a soap message that uses a custom SoapClient proxy class
in hopes that it would be helpful to others.

Wil

> Yves, Wil,
>
[quoted text clipped - 35 lines]
>
> HTH,
SA - 06 Sep 2004 16:17 GMT
Wil,

Thanks. Reviewing my message, I figured out I left out the most important
part, namely the signing and encrypting. (that happens when you snip a code
sample, I guess).

I think your example is good, but I would like to note two things:

A)
Microsoft recommends that you use a derived token to encrypt and sign,
rather than the  X509 certificate itself. (I don't believe it applies to
UsernameTokens, at least, it doesn't work for me with UsernameTokens)

B)
I would not put the responsibility of encrypting (or even create the
envelope itself) in the client. The reason you are creating a proxy
(MySoapClient class in your case, SoapProxy in my case) is to encapsulate
that functionality. Otherwise, there is no reason at all to inherit
SoapClient.

HTH,

Signature

Sven

> SA,
>
[quoted text clipped - 44 lines]
> >
> > HTH,

----------------------------------------------------------------------------
----

> using System;
> using Microsoft.Web.Services2;
[quoted text clipped - 48 lines]
>
>             MySoapClient client = new MySoapClient(new
Uri("http://localhost/InventoryService/InventoryService.asmx"));

>             //Use the WSE X.509 Certificate Tool to look these up
>             string signatureTokenBase64KeyId =
"I54aep46Cb8F0lF4YADcXBd9heI=";  // private client key
>             string encryptionTokenBase64KeyId =
"ONZYTaXbTLCcqdJ13eVRefII0QA="; // service public key
>             X509CertificateStore store = null;
>
[quoted text clipped - 6 lines]
>                 // Sign the SOAP message using the private key
>                 store =
X509CertificateStore.LocalMachineStore(X509CertificateStore.MyStore);
>                 X509SecurityToken signatureToken =
RetrieveTokenFromStore(store, signatureTokenBase64KeyId);

>                 if (signatureToken == null)
>                     throw new ApplicationException("Unable to obtain
client security token.");

>                 request.Context.Security.Tokens.Add(signatureToken);
>
>                 //Sign only the Body portion of the envelope (optional)
>                 MessageSignature signature = new MessageSignature(
signatureToken );
>                 signature.SignatureOptions =
SignatureOptions.IncludeSoapBody;

>                 request.Context.Security.Elements.Add(signature);
>             }
[quoted text clipped - 3 lines]
>                 // Encrypt the soap message using the service's public key
>                 store =
X509CertificateStore.LocalMachineStore(X509CertificateStore.TrustStore);
>                 X509SecurityToken encryptionToken =
RetrieveTokenFromStore(store, encryptionTokenBase64KeyId);

>                 if (encryptionToken == null)
>                     throw new ApplicationException("Unable to obtain
service security token.");

>                 request.Context.Security.Elements.Add(new
EncryptedData(encryptionToken));
>                 request.Context.Security.Timestamp.TtlInSeconds = 60;
>             }
>
>             //populate the body of the SOAP message
>             System.Xml.XmlElement body = request.CreateBody();
>             body.InnerXml = "<InventoryRequest
xmlns=\"Vendor.WebService/Inventory\">"
>                 + "<Widgets>"
>                 + "<Widget DeptId=\"1\">Hammer</Widget> "
[quoted text clipped - 14 lines]
>         //Right out of WSE 2.0 Quickstarts example applications
>         private static X509SecurityToken
RetrieveTokenFromStore(X509CertificateStore store, string keyIdentifier)
>         {
>             if ( store == null )
[quoted text clipped - 7 lines]
>                 {
>                     X509CertificateCollection certs =
store.FindCertificateByKeyIdentifier( Convert.FromBase64String(
keyIdentifier ) );

>                     if (certs.Count > 0)
>                     {
>                         token = new X509SecurityToken( ((X509Certificate)
certs[0]) );
>                     }
>                 }
[quoted text clipped - 9 lines]
>     }
> }

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.