Monday, June 7, 2010

Interop: Microsoft WCF & Intel SOA Expressway

Intel SOA Expressway and Microsoft WCF are supporting wide variety of Web Services standards, both of them supports WS-Security. In this article will be shown how to create and deploy SOA Expressway application to send encrypted and signed requests to WCF-hosted Web-Service.
We will create securing-proxy application for "Intel SOA Expressway". This application will receive plain requests sign/encrypt them and forward secured message to WCF-service, then it will receive encrypted WCF-service response decrypt/verify it and forward plain response to client.

Code of WCF service is listed below

//service.cs
using System;
using System.Configuration;
using System.ServiceModel;
using System.Net.Security;

namespace example {
[ServiceContract(ProtectionLevel=ProtectionLevel.EncryptAndSign)]
public interface Example {
[OperationContract]
string hello(string s);
}

[ServiceBehavior]
public class ExampleService : Example {
public string hello(string s) {return "Hello " + s + "!";}
public static void Main() {
using (ServiceHost serviceHost =
new ServiceHost(typeof(ExampleService))) {
serviceHost.Open();
Console.ReadLine();
}
}
}
}

Configuration for WCF service
<?xml version="1.0" encoding="utf-8" ?><!--service.exe.config-->
<configuration>
<system.serviceModel>
<bindings><basicHttpBinding>
<binding name="free">
<security mode="Message">
<message
algorithmSuite="Basic128Rsa15"
clientCredentialType="Certificate"
/>
</security>
</binding>
</basicHttpBinding></bindings>
<services>
<service
name="example.ExampleService"
behaviorConfiguration="behavior">
<host><baseAddresses>
<add baseAddress="http://hostname:1234/hello"/>
</baseAddresses></host>
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="free"
contract="example.Example">
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="behavior">
<serviceMetadata httpGetEnabled="True"
policyVersion="Policy12"/>
<serviceDebug includeExceptionDetailInFaults="True" />
<serviceCredentials>
<serviceCertificate storeLocation='CurrentUser'
storeName='My'
findValue='server'
x509FindType='FindBySubjectName'/>
<clientCertificate>
<authentication trustedStoreLocation='CurrentUser'
certificateValidationMode='PeerOrChainTrust'
revocationMode='NoCheck'/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>


We'll need set of security tokens

  • ca_cert.cer - Certificate Authority certificate

  • client_cert.cer - client certificate

  • client.pfx - client key-pair

  • server_cert.cer - server certificate

  • server.pfx - server key-pair



SOA Expressway acts as secured-client it will use

  • ca_cert.cer - to verify response

  • client.pfx - to sign request and to decrypt response

  • server_cert.cer - to encrypt request and verify response



WCF-service acts as secured-server it will use

  • ca_cert.cer - to verify request

  • server.pfx - to sign response and to decrypt request

  • client_cert.cer - to encrypt response and to verify request



First of all we need to get WSDL of Web-Service.
Deploy security tokens into windows keystore (just doubleclick and import them) and compile service.cs with csc.exe, put service.exe.config in directory with service.exe, then
start service.exe; WSDL will become accessible via URL http://hostname:1234/hello?wsdl

Start Intel Services Designer and create new Intel SOA Expressway Project



Create new synchronous workflow


Click on Receive action and select "Use existing WSDL". Click on "Import WSDL" button, select "URL", type "http://hostname:1234/hello?wsdl" and click "Download" button


Workflow's Receive action is bound to service's WSDL. Now we need to create back-end service invocation action. Take "Invoke" action from palette and drop it between Receive and Reply.


Set "Use existing WSDL" checkbox in Properties of Invoke and select "hello" operation


Set Request Message Data parameters field of Invoke action to "$Receive.parameters" to forward client request to back-end server


Click on Reply and set Response Message Data parameters to "$invoke.parameters" to forward response of back-end server to client


Now our workflow is able to receive plain message from client, forward it to back-end server, receive WCF service's response and forward it to client. But WCF service will reject plain messages, so we need to sign/encrypt requests and decrypt/verify responses.
Web-Service is secured with EncryptAndSign protection level it means both request and responses should be Signed then Encrypted together with signature. WCF service uses Basic128Rsa15 algorithmSuite it means it will use aes128 for symmetric encryption, Rsa15 for asymmetric encryption and SHA1 for digest calculation.

We need to create four policies to implement these four actions (sign/encrypt/decrypt/verify)

Create new WS-Security policy with operation type "Sign" and name it "sign"


On "Settings" tab of "sign.wsSecurity" set

  • Signature generation method = rsaWithSha1

  • Digest generation method = sha1

  • Token name = client



WCF doesn't accept messages without singed timestamp, set "Include WS-Security Timestamp in generated signature" checkbox on targets tab of "sign.wsSecurity"


Create new WS-Security policy with operation type "Encrypt" and name it "encrypt". On "Settings" tab of "encrypt.wsSecurity" set

  • Data encryption generation method = aes128

  • Key encryption generation method = rsaV1.5

  • KeyInfo type = SKI

  • Token name = server



By default "Encrypt" policy encrypts only SOAP:Body, but WCF requires signatures to be encrypted as well. On tab "Targets" add namespace "http://www.w3.org/2000/09/xmldsig#" with prefix "dsig" and add XPath Target Expression /soap:Envelope/soap:Header/wsse:Security/dsig:Signature with Include Tags = Yes


Create new WS-Security policy with operation type "Decrypt" and name it "decrypt". On "Settings" tab of "decrypt.wsSecurity" set

  • Data encryption methods accepted - AES-128

  • Key encryption methods accepted - RSA V1.5

  • Token name - client




Create new WS-Security policy with operation type "Verify" and name it "verify". On "Settings" tab of "verify.wsSecurity" set

  • Signature methods accepted - RSA with SHA1

  • Digest method accepted - SHA1 (160-bit)

  • Canonicalization methods accepted -

  • X509 Certificate Token name - server

  • Authentication Policy Token name - policy




WS-Security policies are ready, now we need to apply them to Invoke action. Open Process.bpel, click on Invoke action then on WS-Security tab of Invoke properties and add sign.wsSecurity, encrypt.wsSecurity to "WS-Security policies for request" table and decrypt.wsSecurrity, verify.wsSecurity to "WS-Security policies for response" table. Order of policies is important, policies are applied to message in same order as they are listed in table.


Workflow is complete now and can be exported for further deployment.


Login to SOAE and go to Configuration tab. Create configuration with name "WCF-example".


Click on "Security Packages" in left column and create new security package with name "example-sec-conf", it will be used to deploy security tokens.

  • Put client.pfx into "Asymmetric Key Pair" section of security configuration with name "client"

  • Put server_cert.cer into "Certificate" section of security configuration with name "server"

  • Put ca_cert.cer into "Trusted CA Group" section of security configuration with name "example-ca"

  • Create new "Web Service Authentication Policy" in security configuration with name "policy" and Trusted CA Group = "example-ca"





Security configuration is ready now, click "OK" to save it, then click on Applications->Upload Application in left column to deploy workflow. Upload application bundle (wcf_example.tgz) and link it to Security Configuration "example-sec-conf"


If Intel SOA Expressway in installed on same machine with WCF Service it's necessary to alter port of Input Server, because default listen port is taken from WSDL is 1234 which is already busy by WCF-service. To alter listen port, click on "Input Servers" in left column, then click on "Edit" in column "Actions", set port 1235 for example.

Click "OK" to save Input Server changes.

Application is deployed and linked to security configuration with necessary tokens. Click "Activate" to save and activate this configuration. After activation Intel SOA Expressway is ready to process request. Let's test it with simple request sent by curl:


[pbtbskaplou /home/bskaplou ]$ cat req
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body><hello xmlns="http://tempuri.org/"><s>test</s></hello></s:Body>
</s:Envelope>
[pbtbskaplou /home/bskaplou/ ]$ curl -v --data-binary @req -H 'content-type: text/xml' \
-H 'soapaction: "http://tempuri.org/Example/hello"' \
http://pblcbrxw028:1235/hello
* About to connect() to pblcbrxw028 port 1235
* Trying 10.125.133.17... connected
* Connected to pblcbrxw028 (10.125.133.17) port 1235
> POST /hello HTTP/1.1
> User-Agent: curl/7.15.5
> Host: hostname:1235
> Accept: */*
> content-type: text/xml
> soapaction: "http://tempuri.org/Example/hello"
> Content-Length: 149
>
> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body><hello xmlns="http://tempuri.org/">
<s>test</s></hello>
</s:Body>
</s:Envelope>
< HTTP/1.1 200 OK
< Content-Type: text/xml;charset=utf-8
< Content-Length: 390
<?xml version="1.0" encoding="utf-8"?>
Connection #0 to host hostname left intact
* Closing connection #0
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Body><helloResponse
xmlns="http://tempuri.org/"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<helloResult>Hello test!</helloResult></helloResponse></SOAP:Body></SOAP:Envelope>

We've successfully received response from SOA Expressway.

No comments:

Post a Comment