Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

Monday, May 19, 2008

WCF Security at Cleveland Day of .NET

I want to say that Cleveland Day of .NET was terrific experience for me. Between the excellent presentations and the stimulating conversations, I learned a lot and had a great time. I won't try to list all the folks I should thank, 'cause I'd leave some out, but to everyone who helped put on the event, Thank You and Well Done!

I was gratified by the turnout at my presentation, on "WCF Security", especially considering that we started at 8:30 AM! A copy of the slides and the demo code is available from the Cleveland .NET SIG site. (I've discovered I can't upload the files directly to this blog. I don't pay anything to BlogSpot, so I guess I'm not in a position to complain.) I've also sent the material to DoDN and will post a link to it on their site as soon as it's available.

I've made a bunch of enhancements to the demo code. The service host now has five different endpoints, one for each demo scenario, and the test client calls all five in succession. The demo also includes the two scenarios that I didn't have time to show in the presentation. You're welcome to post comments here or contact me directly if you have any questions or suggestions. I'm also planning to write some more blog posts about WCF Security in coming weeks.

Saturday, April 12, 2008

Fixing BizTalk's WCF Message Contract Mix-Up, Part 3

The final solution to the Message Contract Mix-Up doesn’t require you to define your own message contracts, but does require some tricks in both the operation contract and the BizTalk ports.

First, modify the operation contract by adding a MessageParameterAttribute to the argument and return type:

[OperationContract]
[return:MessageParameter(Name="OrderStatus")] PurchaseOrderStatus PlaceOrder([MessageParameter(Name="PurchaseOrder")]PurchaseOrder po);

MessageParameterAttribute lets you change the names that the parameter and return value will have in the SOAP body. The names need to be changed to “PurchaseOrder” and “OrderStatus” so BizTalk can associate the schemas PurchaseOrder.xsd and OrderStatus.xsd with those elements. (Without MessageParameterAttribute, WCF would force the names of the parameter and return value to be “po” and “PlaceOrderResult”, which mean nothing to BizTalk).

Now you need to modify the WCF adapter settings in the two-way receive location in BizTalk. For general instructions and screenshots illustrating the following steps see the BizTalk documentation here. Open the Messages tab of the WCF adapter. Under “Specify the source of the inboud BizTalk message body”, check the third radio button, “Path – content located by body path”. In the text box type an XPATH expression consisting of the name of your operation contract, followed by the name of your data contract schema:

/*[local-name()='PlaceOrder']/*[local-name()='PurchaseOrder']

Make sure the combo box below the text box, “Node encoding”, is set to XML.

The first element in the XPATH is actually the WCF-generated message contract, which has the same name as the operation contract (“PlaceOrder”). The following element is the name of the parameter inside the message contract, which we forced to be “PurchaseOrder” by applying the MessageParameterAttribute.

The consequence of these settings is that the WCF adapter will drill through the message contract and pass the PurchaseOrder element to BizTalk as the message body. Bingo!

Since this is a two-way port, we need to return a data contract back to the caller, and to do that we need to tell the WCF adapter how to wrap the data contract inside a message contract. To do that, go to the bottom half of the tab and check “Template – content specified by template.” In the text box below, be careful not to delete or change the bts-msg-body element that’s already there, since that represents the data contract part of the message. Instead, insert an XML element around the bts-msg-body to represent the message contract. The name of this element is the operation contract’s name plus the suffix “Response”, and it must include the correct XML namespace:

<PlaceOrderResponse xmlns="urn://MyCompany.Purchasing.1_0_0.com" >
<bts-msg-body xmlns="http://www.microsoft.com/schemas/bts2007" encoding="xml" >
</PlaceOrderResponse >

If this were a one-way send port we would follow the same procedure.

The result is that BizTalk will send the return value to client wrapped inside the same message contract that WCF would have generated automatically if the service were hosted in a pure .NET application. Bingo Bingo!

So there you have three ways to solve the Message Contract Mix-Up with the BizTalk WCF adapters. If you’ve read all the way through this series, I surmise that you either find the subject fascinating, or you need to solve these problems as badly as I did.

Fixing BizTalk's WCF Message Contract Mix-Up, Part 2

I know of three ways to fix the Message Contract Mix-Up discussed in the last post. The first two require changing the signatures of your operation contracts to replace the WCF-generated message contracts with message contracts you define yourself. (Perhaps you've read books or heard WCF experts who say you needn't bother defining message contracts except in the unusual case. Then I guess BizTalk is an unusual case, as you might have suspected.)

1. Change the operation contract’s parameter and return value to message contracts with IsWrapped = false, like this:

[ServiceContract(Namespace = "urn://MyCompany.Purchasing.1_0_0.com")]
public interface IPurchasing
{
[OperationContract]
OrderStatusMessage PlaceOrder(PurchaseOrderMessage message);
}

[MessageContract(IsWrapped=false)]
public class PurchaseOrderMessage
{
[MessageBodyMember(Namespace = "urn://MyCompany.Purchasing.1_0_0.com")]
public PurchaseOrder PurchaseOrder;
}

[MessageContract(IsWrapped=false)]
public class OrderStatusMessage
{
[MessageBodyMember(Namespace = "urn://MyCompany.Purchasing.1_0_0.com")]
public OrderStatus OrderStatus;
}

If you declare your own message contracts but set IsWrapped to false (the default is true), then WCF will not generate any message contract element in the SOAP message at all. Instead, WCF will place the field with the MessageBodyMember attribute – which happens to be your data contract, PurchaseOrder – directly at the root of the SOAP body. Thus the SOAP message received by BizTalk will have an element named PurchaseOrder at its message type. Bingo! The great thing about this solution is you don’t need to do anything special on the BizTalk side to make your WCF adapters behave as you originally expected.

2. Same as 1. above, except the message contracts have IsWrapped = true. This will make the message contracts visible in your SOAP bodies, but you'll control their content instead of WCF. Generate XSD schemas for the message contracts and put them in your BizTalk project. Write message maps to transform message contract schemas to and from data contract schemas. Use the message maps to transform message contracts into data contracts in your inbound ports and to transform data contracts into message contracts in your outbound ports.

This solution will increase the number of schemas and message maps you’ll maintain. But unlike the previous solution, this one makes the message structures consistent, whether you look at them from the perspective of the WCF contracts, the BizTalk schemas, or the actual SOAP messages on the wire.

3. If you can’t, or aren’t willing, to define your own message contracts, but you’re able to add some WCF attributes, you can work out a compromise that allows BizTalk to navigate through the WCF-generated message contracts without knowing all about their schemas. The final post in this series will explain how to do that.

Fixing BizTalk's WCF Message Contract Mix-Up, Part 1

WCF developers often write OperationContracts that receive and return DataContracts, like so:

[ServiceContract(Namespace = "urn://MyCompany.Purchasing.1_0_0.com")]
public interface IPurchasing
{
[OperationContract]
OrderStatus PlaceOrder(PurchaseOrder po);
}

Suppose you implement this service in BizTalk using an orchestration. First you use a tool such as XSD.exe or SVCUtil.exe to generate XSD schemas for the PurchaseOrder and OrderStatus data contracts. You add these schemas to a BizTalk project, write an orchestration that receives a PurchaseOrder and returns an OrderStatus, and deploy the schemas and orchestration to BizTalk. Then you bind the orchestration’s receive-send port to a two-way receive location using a WCF adapter and the XmlReceive pipeline.

Your may be shocked to find that when a WCF client sends a message to BizTalk, an error occurs in the receive pipeline: “Finding the document specification by message type "urn://MyCompany.Purchasing.1_0_0.com #PlaceOrder" failed. Verify the schema deployed properly.”

It turns out that the SOAP body that your WCF client sent to BizTalk doesn’t begin with the PurchaseOrder data contract. Instead it begins with a message contract that the WCF client generated behind the scenes. The name of this message contract is “PlaceOrder”, i.e. the name of the operation. Inside that message contract is an element named “po” which contains the data exposed by the data contract. Thus although BizTalk expects to receive a message type named PurchaseOrder, there is no element named PurchaseOrder anywhere in the SOAP body!

There's a symmetrical problem at the other end, when the two-way receive port tries to return an OrderStatus to the caller. If the service were hosted in a .NET application, WCF would automatically generate a message contract element named PlaceOrderResponse and wrap it around the OrderStatus. But BizTalk by default will do no such thing.

I call this impedence mismatch the "BizTalk WCF Message Contract Mix-Up". But so much for lingo, how do you fix it? I can suggest three solutions, each with its pros and cons. I’ll discuss them in the next post.

BizTalk WCF Adapters: OneWay to Nowhere

Newcomers to the WCF Adapters in BizTalk Server 2006 R2 must be aware of two “gotchas” that they're likely to face when sending or receiving WCF messages with the adapters.

The first gotcha is that the IsOneWay property of operation contracts must be false. Luckily IsOneWay is false by default, but developers sometimes set it to true in an attempt to improve performance.

But the BizTalk WCF adapters always set IsOneWay to false, and so will fail whenever sending to or receiving from a client or service that has IsOneWay = true. (There’s an exception for the NetMsmq adapter.) Details about the errors you’ll receive are in the BizTalk documentation here.

The simple solution is to set IsOneWay back to false on your client or service. If you’re able to do that, the problem goes straight away.

If not – if for instance you lack authority to change the service contract – then you’ll have to put a wrapper service between BizTalk and the real service which, you guessed it, has the same WCF contracts as the real service except that IsOneWay is always false. This can be a pain, though it’s not hard to understand what you’re doing.

The second gotcha requires some deeper knowledge of WCF, so I’ll save it for the next post.

Friday, March 28, 2008

Painless SSL On Your Desktop

I was developing a WCF solution that called for UserNameOverTransport authentication. "OverTransport" meant using SSL. Since I'm a coding/architect kind of guy, I had to dig around to find out how to get an X509 certificate installed in my local IIS so I could run SSL on it.

Creating a certificate is easy with makecert.exe, a utility that comes with the Visual Studio SDK. Getting one that works is another matter. By "works" I mean (1) you can install the certificate in IIS, (2) it will be fully trusted by WCF, and (3) you can use it with URLs containing "localhost" without hearing grief about the certificate being issued to a different name.

The keys to success turn out to be (i) installing a ginned-up "authority" into the Trusted Root Certificate Authorities store, and (2) installing a certificate issued by that "authority", in the name of "localhost", into your Personal store.

Getting makecert.exe to do these things requires a lot of fancy parameters. This fine blog post by Michael Howard explains most of what you need to do. The only shortcoming of Howard's otherwise excellent instructions is that he installs the issuer's certificate into the Personal store instead of into Trusted Root Certificate Authorities, and thus WCF won't trust it. To fix that I changed the -ss parameter from "MY" to "ROOT".

Here is text of the batch file I used to create my fully SSL-ready certificate:

@echo off

makecert.exe -r -pe -n "CN=Acme Test And Dev Root Authority" -ss ROOT -sr LocalMachine -a sha1 -sky signature "Acme Test And Dev Root Authority.cer"

makecert -pe -n "CN=localhost" -ss MY -sr LocalMachine -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -in "Acme Test And Dev Root Authority" -is ROOT -ir LocalMachine -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 localhost.cer

Once the batch file ran, I opened the IIS Admin Console and configured Default Web Site to use the certificate named "localhost" for SSL. Et voila, I can use any address on my local machine beginning with https://localhost/..., and it transports over SSL!