Showing posts with label osb. Show all posts
Showing posts with label osb. Show all posts

Thursday, June 13, 2013

Part 3: Kerberos Authentication, RBAC and SAML identity propagation in OAG


This post is the third one of a series by Andre Correa and Paulo Pereira on OAG (Oracle API Gateway).

In the first post we introduced the use case and talked about the Kerberos authentication part.

In the second post we talked about Role Based Access Control.

In this one we describe how to build a SAML token out of the authenticated user and sign it so it properly interoperates with an OWSM server-side policy applied to OSB proxy service. We also cover the necessary OWSM configuration in OSB and SOA. As a reminder, a typical request in our exercise flows through OTD -> OAG -> OTD -> OSB -> OTD -> SOA -> Stub server, and we're required to keep the user identity all the way up to the Stub server.

Now we're specifically talking about the third policy in our overall circuit:



'Add SAML token policy' is as simple as:



We first add the SAML token (using the Insert SAML Authentication Assertion filter), then we sign it (using the XML Signature Generation filter). Let's look at each filter in detail.

Wednesday, October 27, 2010

Keystores and signing your SAML assertions

I've been working on a project recently that includes SOAP clients submitting messages via JMS and HTTP to Oracle Service Bus (OSB). OSB is supposed to validate the assertion, perhaps do some transformations on the SOAP, and then send the message off to some backend service. I'll probably talk about the last two thirds of the solution at some point in the future, but it's the first third that has raised a bunch of questions that other people probably have too.

Before I go into the details let me just remind you that the details here are specific to this customer's situation. I'm not advocating for, endorsing or otherwise suggesting that the info here is useful to YOUR situation. With that said...

The customer's architecture includes submitting the messages over a 2-way SSL channel and they want to use SAML 1.1 assertions with the Bearer confirmation method. Most people are probably more familiar with the Sender Vouches method in which the entity sending the SAML assertion (i.e. the SOAP client) is trusted by the receiving party - which means that every client needs a private key and the receiver needs to have the associated Certificate (or the issuer's Certificate).

In Bearer the sender of the message isn't necessarily the party that actually created the SAML Assertion; if they were then you could just use Sender Vouches and be done. In Bearer you're saying that the communication channel authenticates the caller; this also means that the party sending the message could get a SAML assertion from somewhere else and then just includes it in the SOAP message. There are a bunch of dangers in this sort of architecture, but you can mitigate most of them through appropriate architectural choices. What those dangers are and how you mitigate them is a subject for a separate post. There's a third confirmation method called Holder of Key which is, again, a great subject for another post.

In any case when you use the Bearer Confirmation Method you might want the assertion to be signed, especially if it comes from somewhere other than the client. So who has the private key? And what certificates do you need to put in the keystore?

When you install WebLogic the installer automatically creates a couple of keystores for you. Gerard Davison discusses this a bit in a blog post in the context of setting up SSL. We can use the same keystores for testing SOAP.

First here's the table (shamelessly copied) from Gerard's post:

Property Value
Trust store location wlserver_10.3/ server/lib/DemoTrust.jks
Trust store passwordDemoTrustKeyStorePassPhrase
Key store location wlserver_10.3/ server/lib/DemoIdentity.jks
Key store passwordDemoIdentityKeyStorePassPhrase
Private key passwordDemoIdentityPassPhrase

As you can see there are two JKS files - DemoTrust.jks and DemoIdentity.jks. Take a look at the contents of them by using keytool:

keytool -list -keystore DemoIdentity.jks -storepass DemoIdentityKeyStorePassPhrase
keytool -list -keystore DemoTrust.jks -storepass DemoTrustKeyStorePassPhrase

You'll find that DemoIdentity.jks contains a Private Key and DemoTrust.jks contains the associated Certificate (and a few others).


I'm a huge fan of automated unit test tools. I'll often invest literally tens of hours writing test drivers that will be thrown away when the project is done just so I can be sure that I haven't missed anything. More importantly I want to be 100% sure that we've got test coverage for all of the positive and negative cases we've thought of in the design phase.


So for my little SOAP tester in my test environment I can use the DemoIdentity.jks file to sign the SAML Assertion and as long as OSB is configured to use DemoTrust.jks it should accept my SAML Assertion.

In the real world you definitely won't be using the DemoTrust keystore. Instead you'll create another Key Store to be used on the OSB server and you'll probably put the Certificate of the SAML assertion generator in there (if it's a self-signed cert) or the Cert chain needed to verify the signature.

Hope this helps!

Monday, June 14, 2010

Oracle Service Bus, SOA Suite Human Workflow Web Services and SAML

A customer recently was looking for some guidance on how to apply SAML and OSB to SOA Suite Human Workflow Web Services. I'd never looked at those particular web-services, but applying SAML to OSB and SOA Suite in 11g is pretty straightforward - OWSM...right? Well...this really depends on the requirements and which particular services are being used. In this post, I'll explain a little bit about the identity propagation options for the Human Workflow Web Services, and layout two different approaches to deploying OSB and SAML to protect them.

Identity Propagation Options for Human Workflow Web Services



The customer that I'm working with was particularly interested in the TaskService and TaskQueryServices. After looking at the documentation, I started by pulling down the WSDL. Most of the WSDL and dependent schemas are XML representations of the Workflow Services, but there is one part that caught my attention:


<service name="TaskQueryService">
<port name="TaskQueryServicePortSAML" binding="tns:TaskQueryServiceSOAPBinding">
<soap:address location="http://192.168.56.102:18001/integration/services/TaskQueryService/TaskQueryService2/*"/>
</port>
<port name="TaskQueryServicePort" binding="tns:TaskQueryServiceSOAPBinding">
<soap:address location="http://192.168.56.102:18001/integration/services/TaskQueryService/TaskQueryService"/>
</port>
</service>

The service has two ports - TaskQueryServicePort and TaskQueryServicePortSAML. They both have the same binding, so they support the same messages, just two different ports. Digging a little deeper, I took a look at the WSDL on the SAML Port by going to http://192.168.56.102:18001/integration/services/TaskQueryService/TaskQueryService2?ORAWSDL and look what I found....OWSM policy!

<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:oralgp="http://schemas.oracle.com/ws/2006/01/loggingpolicy" xmlns="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="wss10_saml_token_service_policy" xmlns:orawsp="http://schemas.oracle.com/ws/2006/01/policy" orawsp:displayName="Wss10 SAML Token Service Policy" xmlns:orasp="http://schemas.oracle.com/ws/2006/01/securitypolicy" orawsp:description="This policy authenticates users using credentials provided in SAML tokens in the WS-Security SOAP header. The credentials in the SAML token are authenticated against a SAML login module. This policy can be applied to any SOAP-based endpoint. " orawsp:attachTo="binding.server" Name="oracle/wss10_saml_token_service_policy" orawsp:category="security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" orawsp:local-optimization="check-identity">
<orasp:wss10-saml-token orawsp:Silent="false" orawsp:name="WSSecurity SAML Token" orawsp:Enforced="true" orawsp:category="security/authentication">
<orasp:saml-token orasp:is-encrypted="false" orasp:is-signed="false" orasp:confirmation-type="sender-vouches" orasp:version="1.1"/>
</orasp:wss10-saml-token>
</wsp:Policy>


People know, that I'm not really a WS-Policy purist of really even much of a fan, so I really like the ?ORAWSDL "trick" to help figure our which OWSM policy is being applied. From looking at the ORAWSDL, we've learned that the SAML end-point is protected by the oracle/wss10_saml_token_service_policy. This is a simple WS-Security SAML 1.0 Sender Vouches policy. Putting OSB 11gR1 in front of this service should be a pretty simple application of the OWSM/OSB integration. This is the one of the two approaches I'll explain in a little bit.


If there are two ports, and the 2nd port supports SAML for identity propagation, what does the 1st port use? The answer is a proprietary token. Before everyone goes BOOOOOOO, if you look at the services that are exposed through this end-point, they are synchronous, single-party, RPC style services. If all the requests are made over SSL, then there is no real advantage or requirement for message level security. I suspect, that this proprietary token was at some point an HTTP Cookie, and was probably just passed around in the HTTP transport header...also a well established (read: WebSSO) practice. I was at a different customer a few weeks back and we came to a very similar recommendation to use a transport token instead of message level security and SAML. Every environment is different, but this other way of doing identity propagation is reasonable for this situation. The token itself has been SOAP-a-sized (made SOAP friendly?) by being passed in a com:workflowContext element in all of the messages. Here's a sample:



This is an example of the authenticate call that returns the token for a user authenticated by their username and password. The really interesting part is what's commented out in the com:credential element - onBehalfOfUser. That's right - the proprietary token has impersonation built it. This actually goes beyond what is available in the SAML based identity propagation. The second solution will show how to use SAML with the proprietary token including the onBehalfOf functionality.

Solution 1 - OSB proxy calling Human Workflow Web Service as Business Service using SAML


Once you figure out what's going on with the Human Workflow Web Services, fronting it with OSB 11g and OWSM is pretty straight forward. The basic idea is that you're going to create a business service from the SAML Port, and then configure OSB to use OWSM when calling the business service. One of the nice things about OWSM is that picking the corresponding client policy for a service policy is pretty straight forward.



The only thing you really need to consider is are you going to set-up OSB as an active intermediary or as a pass-through. In either case, you create a proxy-service based on the business service, and configure the OWSM policy oracle/wss10_saml_token_service_policy. This policy uses a SAML Sender-Vouches, but there is no message protection. This means that the message itself is not signed. Notice, looking back at the policy, the assertion itself isn't signed or encrypted either, so we'll need to do something at the transport/network level to ensure that the request is authentic. If there is a trusted network between OSB and SOA suite, then you may not need anything else. You can use WebLogic's Connection Filters as an additional software firewall. If this is not sufficient, then you can set-up 2-way SSL between OSB and SOA Suite to ensure that the request is valid. OSB still uses Service Key Providers to determine the Certificate to use for making 2-way SSL calls, even if OWSM message level policies are applied. Why not just change the policy protecting the services to require that the message or assertion is signed? These end-points are not proper JAX-WS Web Services running in WebLogic Server...there are just servlets, so there is no simply way to change the policies. I think the best approach here is to use 2-way SSL.

Solution 2 - OSB Proxy calling Human Workflow Web Service using Proprietary Token


But Josh, I thought this was doing to be about using SAML? It is. OSB can still expose an endpoint protected by a SAML policy, but the call to Human Workflow Web Service is going to be done using the existing token identity propagation mechanism. The trick is that the proxy pipeline is going to make a Java callout to get the token and add it into the message. The key to the whole solution is the getWorkflowContextForAuthenticatedUser method. This call will return the token for the user. But what if OSB and SOA Suite are running on two separate domains? So this is a remote call, but the OSB and SOA suite domains have to be trusted. The simplest is to just set-up Global Trust. This means that the two domains will trust eachother's signed Principals. There are a few more subtle points that can best be explained by showing the Java callout code and the pipeline.


First, let's look at the pipeline. Conceptually, we have to make the Java callout, get the token, and then add the token to the request in the com:token element of the message. It's pretty simple, and there are far betten XPath people than I, but this is how I did it.



  • Assign $body//com:onBehalfOfUser/text() to a variable - we'll talk more about this in a second.
  • Make the Java Callout. The most important point is that the call requires a Service Account of type passthrough. This makes sure that the caller's identity is passed down so we know who the user that we're requesting a token for. We're passing in the onBehalfOfUser from above as a parameter. This method returns the token as a String.
  • Assign $body/child::node() to the variable operationName. This is the child node of the body...which is the operation request. The com:workflowContext needs to be added to the first child of this element.
  • Insert the following as the first child of operationNode

    <com:workflowContext>
    <com:credential>
    <com:identityContext>jazn.com</com:identityContext>
    </com:credential>
    <com:token/>
    <com:locale>en_US</com:locale>
    <com:timeZone>US/Eastern</com:timeZone>
    </com:workflowContext>

  • Replace ./com:workflowContext/com:token in operationNode with $token - the value returned from the Java callout
  • Replace child::node() in $body with opertaionNode

This is the complete pipeline:



Now the Java callout. The code itself is actually pretty simple, but there was some tricky stuff with classloading. I started off trying to load the Client Jar Files into OSB directly, but was getting some very wierd ClassNotFoundExceptions with weblogic.jndi.WLInitialContextFactory. I decided after some fiddling that I was just going to load the bpm-services.jar
from the system classpath. This means modifying the setDomainEnv.sh to add the jar at start-up. This works really well, except that since the jar contains EJBs, they workflow services don't get deployed on the server. This is fine since for this customer OSB and SOA Suite are two separate domains. I'm sure there is a different way to fix the classloading but I was under a deadline, so this is the best for now.

Now that we have the set-up out of the way, this is the code for the callout


public static String getToken(final String runAsUser) throws Exception {
System.out.println("RunAsUser="+runAsUser);
Subject theUser = weblogic.security.Security.getCurrentSubject();

System.out.println("The User IS="+theUser);

if (theUser==null || theUser.toString().trim().equals("Subject:")) {
System.out.println("No user....");
return "";
} else {
System.out.println("'"+theUser.toString().trim()+"'");
}


System.out.println("Calling to get token for "+theUser+" from t3://localhost:8001");
Object token = weblogic.security.Security.runAs(theUser, new PrivilegedAction() {
@Override
public String run() {

try {
Class clientFactoryClass = ClassLoader.getSystemClassLoader().loadClass("oracle.bpel.services.workflow.client.WorkflowServiceClientFactory");

Method factoryMethod = clientFactoryClass.getMethod("getWorkflowServiceClient", new Class [] {String.class, oracle.bpel.services.workflow.client.config.WorkflowServicesClientConfigurationType.class, Logger.class});


WorkflowServicesClientConfigurationType wscct =
new WorkflowServicesClientConfigurationType();

List servers = wscct.getServer();
ServerType server = new ServerType();
server.setDefault(true);
server.setName("default");
servers.add(server);


RemoteClientType rct = new RemoteClientType();
rct.setServerURL("t3://localhost:18001");
rct.setInitialContextFactory("weblogic.jndi.WLInitialContextFactory");
rct.setParticipateInClientTransaction(false);

server.setRemoteClient(rct);


IWorkflowServiceClient client =
(IWorkflowServiceClient)factoryMethod.invoke(clientFactoryClass, new Object[] { WorkflowServiceClientFactory.REMOTE_CLIENT, wscct, null});

ITaskQueryService tqs = client.getTaskQueryService();

IWorkflowContext wfContext = tqs.getWorkflowContextForAuthenticatedUser();

if (runAsUser==null) {

return wfContext.getToken();

} else {

IWorkflowContext onBehalfOfCtx = tqs.authenticateOnBehalfOf(wfContext, runAsUser);
return onBehalfOfCtx.getToken();
}

} catch (Exception e) {
e.printStackTrace();
}

return "";
}

});

System.out.println("The token is "+token);
return (String)token;
}


The code itself is really pretty simple. You do need to pull the Subject using the always handy weblogic.security.Security.getCurrentSubject() call, and then invoke the PriviledgedAction. This is required so that when the client running inside of OSB makes the call to SOA Suite, the user's identity is passed, and a token for that user is returned. Again, you have to make sure that you've configured the Java callout with the passthrough service account. Finally, we check if a runAsUser (onBehaldOfUser) is passed, and if it is then instead of returning the token based upon the user's identity in SAML, you get a token onBehalf of the passed in user. In my testing, using the OOTB SAML capabilities, the information in the com:workflowContext was not processed, including the onBehalfOfUser information, so without a username and password, I think using the token based approach is the only way to leverage this functionality with SAML.

Summary



In laying out the two approaches, the simpler OOTB approach will work if you don't need the onBehalfOfUser functionality and you can secure the link between OSB and SOA Suite either at the network or transport level using 2-way SSL. The more complicated approach has the restriction of SOA Suite and OSB being in separate domains, but does have the advantage of supporting onBehalfOf. I would also make sure that for this approacj, there is transport or network security between the OSB and SOA suite since the token (though encrypted) could be replayed. There could be performace issues with the constant creation of the com:workflowContext objects, so its not hard to imagine that the Java callout is extended to use Coherence for caching of Subjects to tokens.


All and all a very good first "real test" for my new "huggable server" - multiple SOA and OSB domains + IDE + Browser without breaking a sweat. I can't but help feel that this solution can be improved, or somehow simplified. Is all of the complexity of the custom approach warranted? What do you think?

References


Edward Biedmond's Blog

Friday, May 14, 2010

Dynamic OWSM Policy and Policy Overrides for WLS, OSB and SOA Suite

First all, great job by Alex in his inaugural post on the blog.. I really think the OES-OVD combination solves a lot of interesting problems.

I've been spending a lot of time lately working on OSB and OWSM. As people probably know, 11gR1 OSB is released, and you can now use OWSM to secure both proxy and business services.. You have been able for a while to use OWSM for WLS JAX-WS services and of course you can use it to secure SOA Suite composites and BPEL processes. I really like, and customers really like, the idea of being able to centrally manage WS-Security policy from one place. I also like the fact that there is now a single web-services stack across all of these products that OOTB interoperates with eachother. If you want to know why this is a real improvement, I refer you to the OSB to WLS + SAML post. Also, OWSM has native WS-Security Kerberos Token profile...and I have gotten it to work very nicely with WCF (a post for another time).

Given all this, I've been sharpening my pencil on how to write custom assertions (policies) in OWSM 11g. As people will recall, I did an OES+OWSM custom assertion at OOW 2009, and to be honest, that was really the last time I looked at it. My recollection from that time, and my recent experience was that it was a little challenging. Writing the custom assertions is very similar to writing the custom SSPI plugins. Its the type of thing that you don't do that often, and when you do, once you can get the build script going and some decent samples, its not too bad. So, my contribution to this effort can be found at https://owsm-11g-custom-assertions.samplecode.oracle.com/wiki/. In addition to the OES+OWSM assertion from last year, I added a new project called OWSMAC - OWSM Annotations Compiler. What I tried to do was look at what was challenging in working with the custom assertions and try to make it really, really simple.

These are a few of things that OWSMAC does:

  • Automatically generate policy and assertion XML
  • Simplified XPath processing
  • Dynamic Reloading - no need to reboot the server after each little change
  • Consistent and predictable lifecycle
  • Programmatic selection of policy and overrides


I'm putting the cart before the horse here, as I've not really fully documented or "javadoc-ed" the project, but I have been able to solve a pretty interesting use case that a couple of customers have been interested in, so I wanted to share it now. I suggest that people join the project for updates on OWSMAC.

Dynamic Policy Selection


The scenario is that from an intermediary (likely OSB or SOA Suite composite) the request to the business service/reference requires message level security, but the specifics of the actual policy depend on some state - information in the message, or the location (network) of the destination or different targets have different security requirements - some partners want SAML and others want Username and Password. WS-SecurityPolicy once again is not sufficient or particularly helpful. You need to be able to determine the policy dynamically. I've done this through one of the samples in OWSMAC - DynamicClientPolicy


package owsmac.test;

import java.util.Map;

import javax.xml.soap.SOAPMessage;

import oracle.wsm.common.sdk.IContext;

import owsmac.annotations.Assertion;
import owsmac.annotations.AttachTo;
import owsmac.annotations.Category;
import owsmac.annotations.CustomMethod;
import owsmac.annotations.Executor;
import owsmac.annotations.MessageContextPropertyValue;
import owsmac.annotations.PolicyNameValue;

import oracle.wsm.security.util.SecurityConstants.ClientConstants;
import javax.xml.ws.BindingProvider;

import oracle.wsm.common.sdk.ISOAPBindingMessageContext;
import oracle.wsm.policyengine.IExecutionContext;

import owsmac.annotations.DestroyMethod;
import owsmac.annotations.ExecutionContext;
import owsmac.annotations.FaultMethod;
import owsmac.annotations.InitMethod;
import owsmac.annotations.PolicyPropertyValue;
import owsmac.annotations.Property;

@Assertion(displayName = "A dynamic client policy", customType = Assertion.CustomType.policy, category = Category.security, attachTo = AttachTo.binding_client)
@Executor(category = Category.security_authentication)
public class DynamicClientPolicy {

@PolicyNameValue()
public String selectedPolicy;

@PolicyPropertyValue(name="csf-key",policyNameValue="selectedPolicy")
//@MessageContextPropertyValue(name = ClientConstants.WSS_CSF_KEY)
public String csfKey;

@MessageContextPropertyValue(name = "javax.xml.ws.service.endpoint.address")
public String address;

@MessageContextPropertyValue(name = BindingProvider.USERNAME_PROPERTY)
public String samlUsername;

@MessageContextPropertyValue(name = "oracle.wsm.subject.precedence")
public String useSubjectPrecedence;

@Property(value = "localhost:389")
public static String LDAP_SERVER;

public static @ExecutionContext
IExecutionContext eCtx;

@InitMethod
public static void init() {
System.out.println("In Init: "+eCtx.getAllProperties());
System.out.println("In Init: The LDAP Server is "+LDAP_SERVER);
}

@DestroyMethod()
public static void destroy() {
System.out.println("Destroyed Dynamic Client Policy");
}


@FaultMethod()
public boolean onFault(IContext context) throws Exception {

ISOAPBindingMessageContext soapContext = (ISOAPBindingMessageContext)context;

SOAPMessage message = soapContext.getFault();

return true;
}

@CustomMethod(extendsPolicyNameValue="selectedPolicy")
public boolean selectPolicy(IContext context) throws Exception {


this.getPolicyFromContext(context);

return true;
}

/**
* This is where the custom logic goes for selecting the policy
* @param content
*/
private void getPolicyFromContext(IContext context) {

System.out.println("In the getContext.....");
Map<String,Object> properties = context.getAllProperties();

for (String property: properties.keySet()) {

Object value = properties.get(property);

System.out.println(property+"=>"+value);

}

System.out.println("The address is "+this.address);

if (this.address!=null && address.indexOf("UNT")!=-1) {

this.selectedPolicy = "oracle/wss_username_token_client_policy";
this.csfKey = "josh.creds";

} else {

this.selectedPolicy = "oracle/wss_saml_token_bearer_over_ssl_client_policy";
this.samlUsername = "foobar";
this.useSubjectPrecedence = "false";

}

System.out.println("The selected policy is "+this.selectedPolicy+" and user="+this.samlUsername);



}

}

The whole idea of OWSMAC is to allow people to use POJOs to build the assertions and let everything else happen "magically". I'll draw your attention to the selectPolicy method. This method has the @CustomMethod annotation with extendsPolicyNameValue. This basically means call this method, and when your done go invoke the policy stored in the field referenced in extendsPolicyNameValue. So, in this method, you can set the name of the policy and then also set additional policy overrides or programmatic overrides (these being the same as the properties for JAX-WS clients).

In the sample, we're just looking at the address (endpointURI) and then either invoking UNT - specifying the csf-key of the user or calling SAML and specifying the name of the user to include in the SAML assertion. In the SAML case, there is also something interesting going on - we're using 11gR1 PS SAML Identity Switching. Notice in order to this we're basically setting two properties - BindingProvider.USERNAME_PROPERTY and oracle.wsm.subject.precedence. The former is the name of the user (which doesn't have to exist in the user directory) and the latter is a flag that tells OWSM not to use the identity in the subject for the SAML assertion. Now, in order to perform identity switching, you need to grant a permission. The documentation is not particularly clear. The permission you need to grant is oracle.wsm.security.WSIdentityPermission resource=<composite name> assert. In the text box, you enter resource=<appname> not <appname>.

This is the simple composite that illustrates the scenario.



The references are to WLS web-services protected by OWSM service policies.



The policy file that gets generated by OWSMAC is then uploaded into EM to create a custom policy.



And then attach the policy to the references to the SAML and UNT services. You can also attach the same policy in OSB





Summary


In an ideal world, there would be no need to this type of programmatic extension of the core policy model. The standards would be precise and comprehensive and all of the OOTB policies would never need to be changed. But with our experience with SSPI and the WLS core security model, there are always occasions where customer requirements fall into that 20%, so its good to know that there are ways to simply extend the core product functionality. I like the simplicity of OWSM and the binding of configuration and policy is broadly very useful. Invariably there will be scenarios like the one above, where more dynamic behavior is required. My plan going forward it to continue to use the annotations model with the OWSMAC samples to illustrate how to execute these types of scenarios. I'm looking for additional samples to prove out or ideally some help developing and shaping the project, for everyone's benefit. Who's with me?

References


Creating Custom Assertions

Monday, March 29, 2010

Identity Propagation in a flow involving OAM, OSB and web services

A Sales Consultant asked for my opinion in a scenario where a customer wants to propagate end user credentials all the way from OAM (Oracle Access Manager) to web services responsible for executing business logic.

In the customer flow, after being authenticated by OAM, the end user is redirected to a portal UI. The UI makes calls to OSB (Oracle Service Bus) which in turn invokes web services deployed on WebLogic Server. One of use case driving requirements is that the end user identity should be available for underlying web service implementation so that it can be recorded in database tables for auditing purposes. Therefore, the main question here is how to get the user identity from OAM token and make it available for web service implementation code.

OAM is a web-based SSO solution. Very roughly put, it works by means of an HTTP cookie called ObSSOCookie which is recognized by OAM's Policy Enforcement Points, known as Web Gates. A Web Gate intercepts all HTTP requests for a protected resource and evaluates whether an ObSSOCookie is present and valid, in which case the user is considered authenticated.

OSB is an SOA integration component within Oracle's SOA portfolio. It provides routing, transformation and mediation capabilities for interactions between heterogeneous services.

Ok, to the solution.

Let's first talk about what happens when an anonymous user accesses the protected portal UI page. Anonymous users don't hold an authenticated ObSSOCookie (note that anonymous users might hold an ObSSOCookie, but for the default OblixAnonymous user), therefore are challenged by OAM's Web Gate according to the configured authentication scheme.

Let's assume in this case a form-based scheme requiring username and password is set. When challenged, the user types in her username and password. Assuming those are correct, an ObSSOCookie identifying the end user is generated and added to the HTTP response. The end user is granted access and directed to the protected resource (portal UI page). Once the user lands on the portal UI page, a call is made to a web service running on a remote WebLogic Server via OSB.

At this point we need to make an important decision: how to retrieve the end user identity and move it forward. Remember that so far it only exists within the ObSSOCookie that is present in the HTTP request. There are two ways to accomplish it: Within the portal UI, i) the ObSSOCookie can be added to an Oracle proprietary SOAP header or ii) the end user identity can be obtained from portal application container's Java Subject and added to a standard SAML message within the SOAP header. In any case, an OAM identity asserter must be configured in WebLogic Server running the portal application. OWSM (Oracle Web Services Manager) also plays a vital role from now on. An OAM identity asserter basically asserts the end user identity present in ObSSOCookie against the configured Identity Store in WebLogic Server. If assertion succeeds, the user principals are added to the container's Java Subject.

OWSM is a key component in Oracle's SOA solution. It provides centralized definition and distributed enforcement of security and management policies for SOA and JavaEE applications. OWSM runtime modus operandi is very simple: simply put, agents installed at specific endpoints intercept and evaluate SOAP calls according to rules specified by attached policies. Policies come in two flavors: client-side and server-side. Client-side policies usually add data to the SOAP message while server-side policies retrieve that data and process them, many times delegating decisions to other third-party mechanisms.

Back to our flow, I recommend approach ii) as the means of propagating the end user identity. For two reasons: a) we're avoiding adding proprietary data to the SOAP message. If one decides for i), on the server side she needs to provide for understanding that proprietary data. Even considering that OWSM natively supports it, the implementation would stay tied to necessarily understanding ObSSOCookie. If your organization standardizes on SAML (Security Assertions Markup Language), for example, that might be a problem. b) OAM is unlikely to be available in a development environment, therefore relying on ObSSOCookie would simply not work. Once the ObSSOCookie is asserted, a Java Subject becomes available within WebLogic Server.

By attaching a SAML-based OWSM client-side policy to the calling endpoint, the OWSM agent will pick the principal from the Java Subject, generate a SAML message and add it to the SOAP header.

There are several options on how the SAML message can be generated: as clear-text, with message-level security (WSS) or sent through an SSL channel. I will cover the details of message-level and transport-level security in OWSM in a subsequent article.

At this point we're making a SOAP call and we've added a SAML token to the SOA header. Next element in our flow is OSB. The key point here is that we don't need to process that token in OSB. All that's required is having OSB configured in pass-through mode and the request will get intact to the invoked web service. On the web service, we attach a SAML-based OWSM server-side policy that is capable of understanding the token generated by the client-side policy. The policy extracts the token and submits it for processing, which in this case means asserting the end user identity against the identity store configured for the WebLogic Server hosting the web service. If the user is successfully asserted, a Java Subject is created and made available to running applications. One can use JAAS APIs to get the authenticated user name and stick it into any DML statements that are executed against a database.

Sunday, March 21, 2010

Oracle Service Bus and Kerberos

This topic has come up in a number of discussions at a number of customers in the past few weeks. The basic question is "How does OSB work with Kerberos (WCF) clients?". The short answer is "The simplest thing to do is just use OSB as a pass-through." I've given this answer a few times, and people seem disappointed. I think they expect that there is "more". I wanted to use this post to help people understand some of the details of OSB and Kerberos that make this the most sensible approach.

Wednesday, November 4, 2009

WebLogic Server (WLS) to Oracle Service Bus (OSB) to WebLogic Server using SAML Sender Vouches - Active/Active

In yesterday's post on WLS to OSB to WLS with SAML Sender Vouches, OSB was not really acting as a full "active-intermediary". Basically, OSB was validating the SAML Assertion on the request and sending a new request with a new SAML Assertion to the business-service. The response from the business service (though signed) was ignored by OSB. The signature is validated by the sender. Let's for the sake of discussion call this configuration active/passive security. This model works if:

  • You are basically interesting in using OSB to authorize the use of services
  • You are not doing any transformations on the response (you'll break the signature).
  • The producer and consumer trust each other directly. This is different then they each trust the service bus.


If your deployment meets this criteria, then "Bob's Your Uncle" (I'm from Massachusetts, not California, so I'm not sure I used that right - but basically I'm saying "you're good")

If not, then read on. These are the gory details of getting this scenario to work in active/active. I'm not going to cover everything from yesterday, I'll just get down to the brass tacks of the policies.

Direction.In (Request)



Direction.Out (Response)



Configuring the WLS Service Producer


Since this is a use case primarily about processing the response, let's start with the response coming back from the service producer. OSB cannot handle <ProtectTokens> policy assertions because it does not support the STR Transform that WLS uses to generate the signature. So, this assertion has to be removed from the standard Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml.

Configuring the Business Service


A nuance of the way that OSB enforces policy is that the entire message is processed - signatures validated, messages decrypted - and then the policy is checked to see if it complies. The policy is basically a minimum that has to be met - not a prescription for how to validate the message. The idea of the "Empty" policy is to just have OSB process the signature that the Service Producer generated. There are no other conditions.

Configuring the Proxy Service


The message now needs to be signed by OSB. The pre-configured Sign.xml will work just fine.

Configuring WLS Service Consumer


You need to use two different policies for the JAX-WS client. Since the signature generated by OSB does not conform with the standard policy in two ways - it does not protect the tokens (sign) and it does include the RAW X.509 Certificate as opposed to an reference by Issuer Serial Number. So, the policy for the outputMessage (outbound) is a custom policy very similar to the in-bound policy on the service consumner, except that we have to additionally allow for the Recipient Token to be passed. The policy for the inputMessage (inbound) is the standard Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml. The code looks like this:


ClientPolicyFeature cpf =
new ClientPolicyFeature();

InputStreamPolicySource ps_out = new InputStreamPolicySource(this.getServletContext().getResourceAsStream("/WEB-INF/policy-no-protect-plus-recipient-token.xml"));
InputStreamPolicySource ps_in = new InputStreamPolicySource(this.getServletContext().getResourceAsStream("/WEB-INF/policy.xml"));

cpf.setEffectivePolicyForInputMessage(ps_in);
cpf.setEffectivePolicyForOutputMessage(ps_out);

Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,
SOAPMessage.class, Service.Mode.MESSAGE, new WebServiceFeature[] {cpf});



SOAPMessage response = dispatch.invoke(request);


Policies


Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml minus ProtectTokens



<?xml version="1.0" encoding="windows-1252" ?>
<wsp:Policy wssutil:Id="Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml" xmlns:wssutil="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<ns1:AsymmetricBinding xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns1:InitiatorToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:InitiatorToken>
<ns1:RecipientToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">

<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:RecipientToken>
<ns1:AlgorithmSuite>
<wsp:Policy>
<ns1:Basic256 />
</wsp:Policy>
</ns1:AlgorithmSuite>
<ns1:Layout>
<wsp:Policy>
<ns1:Lax />
</wsp:Policy>
</ns1:Layout>
<ns1:IncludeTimestamp />

<!-- OSB does not work well with this assertion
<ns1:ProtectTokens />
-->

<ns1:OnlySignEntireHeadersAndBody />
</wsp:Policy>
</ns1:AsymmetricBinding>
<ns2:SignedSupportingTokens xmlns:ns2="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns2:SamlToken ns2:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns2:WssSamlV11Token10 />
</wsp:Policy>
</ns2:SamlToken>
</wsp:Policy>
</ns2:SignedSupportingTokens>
<ns3:Wss10 xmlns:ns3="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns3:MustSupportRefKeyIdentifier />
<ns3:MustSupportRefIssuerSerial />

</wsp:Policy>
</ns3:Wss10>
</wsp:Policy>


Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml minus ProtectTokens plus Include Recipient Token



<?xml version="1.0" encoding="windows-1252" ?>
<wsp:Policy wssutil:Id="Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml" xmlns:wssutil="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<ns1:AsymmetricBinding xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns1:InitiatorToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:InitiatorToken>
<ns1:RecipientToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator">

<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:RecipientToken>
<ns1:AlgorithmSuite>
<wsp:Policy>
<ns1:Basic256 />
</wsp:Policy>
</ns1:AlgorithmSuite>
<ns1:Layout>
<wsp:Policy>
<ns1:Lax />
</wsp:Policy>
</ns1:Layout>
<ns1:IncludeTimestamp />

<!-- OSB does not work well with this assertion
<ns1:ProtectTokens />
-->

<ns1:OnlySignEntireHeadersAndBody />
</wsp:Policy>
</ns1:AsymmetricBinding>
<ns2:SignedSupportingTokens xmlns:ns2="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns2:SamlToken ns2:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns2:WssSamlV11Token10 />
</wsp:Policy>
</ns2:SamlToken>
</wsp:Policy>
</ns2:SignedSupportingTokens>
<ns3:Wss10 xmlns:ns3="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns3:MustSupportRefKeyIdentifier />
<ns3:MustSupportRefIssuerSerial />

</wsp:Policy>
</ns3:Wss10>
</wsp:Policy>




Empty Policy



<?xml version="1.0"?>

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="validate-bs-response"
>


<wssp:MessageAge/>

</wsp:Policy>

Sender Vouches plus Sign Timestamp



<?xml version="1.0"?>

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="saml-policy-to-business-service"
>

<wssp:Identity>
<wssp:SupportedTokens>
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-2004-01-saml-token-profile-1.0#SAMLAssertionID">
<wssp:Claims>
<wssp:ConfirmationMethod>sender-vouches</wssp:ConfirmationMethod>
</wssp:Claims>
</wssp:SecurityToken>
</wssp:SupportedTokens>
</wssp:Identity>

<wssp:Integrity>

<wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<wssp:CanonicalizationAlgorithm
URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.w3.org/TR/1999/REC-xpath-19991116">
wsp:GetHeader(./wsse:Security/wsu:Timestamp)
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>

</wssp:Integrity>

<!--
<wssp:Confidentiality>

<wssp:KeyWrappingAlgorithm URI="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>

<wssp:Target>
<wssp:EncryptionAlgorithm
URI="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:EncryptionAlgorithm
URI="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()</wssp:MessageParts>
</wssp:Target>

<wssp:KeyInfo />
</wssp:Confidentiality>
-->

<wssp:MessageAge/>

</wsp:Policy>

<wssp:MessageAge/>

</wsp:Policy>

Standard OSB SAML Sender Vouches



<?xml version="1.0"?>

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="saml-policy"
>

<wssp:Identity>
<wssp:SupportedTokens>
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-2004-01-saml-token-profile-1.0#SAMLAssertionID">
<wssp:Claims>
<wssp:ConfirmationMethod>sender-vouches</wssp:ConfirmationMethod>
</wssp:Claims>
</wssp:SecurityToken>
<!--
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
-->
</wssp:SupportedTokens>
</wssp:Identity>

<wssp:Integrity>

<wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<wssp:CanonicalizationAlgorithm
URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()
</wssp:MessageParts>
</wssp:Target>


<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>


</wssp:Integrity>

<!--
<wssp:Confidentiality>

<wssp:KeyWrappingAlgorithm URI="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>

<wssp:Target>
<wssp:EncryptionAlgorithm
URI="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:EncryptionAlgorithm
URI="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()</wssp:MessageParts>
</wssp:Target>

<wssp:KeyInfo />
</wssp:Confidentiality>
-->

<wssp:MessageAge/>

Tuesday, November 3, 2009

WebLogic Server (WLS) to Oracle Service Bus (OSB) to WebLogic Server using SAML Sender Vouches

I'm sure many people were hoping that the next post would be the much anticipated OES-OWSM Custom Assertion, but things have been very busy coming out of Open World. Oddly, I'm working with multiple customers that have been looking for a solution to the following use case:

WLS makes a Web-Services call using JAX-WS to Oracle Service Bus (OSB) passing the identity of the caller using SAML Sender Vouches (SV). OSB serves as an active-intermediary and process the SAML Assertion. OSB then goes and calls a Business Service propagating the user's identity, again using SAML SV.

What makes this use case a little tricky is that OSB at present does not understand WS-Policy 1.2 Assertions, and the JAX-WS stack on WLS does not understand the OSB proprietary assertions. This means that local policy needs to be applied 4 places

  • JAX-WS Client using WS-Policy 1.2
  • OSB Proxy Service using OSB Policy
  • OSB Business Service using OSB Policy
  • WLS Web Services service using WS-Policy 1.2


This post will cover the specific policies and how to apply them at each point.

JAX-WS Client using WS-Policy 1.2



You need to use a ClientPolicyFeature, and load the actual policy from an InputStream. In the example below, I'm running inside a web-application and packaged the policy.xml in the WEB-INF directory.


ClientPolicyFeature cpf =
new ClientPolicyFeature();



InputStreamPolicySource ps = new InputStreamPolicySource(this.getServletContext().getResourceAsStream("/WEB-INF/policy.xml"));

cpf.setEffectivePolicy(ps);


Dispatch dispatch = service.createDispatch(portName,
SOAPMessage.class, Service.Mode.MESSAGE, new WebServiceFeature[] {cpf});




The policy that I'm using is just the standardWssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml.


<?xml version="1.0" encoding="windows-1252" ?>
<wsp:Policy wssutil:Id="Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml" xmlns:wssutil="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<ns1:AsymmetricBinding xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns1:InitiatorToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:InitiatorToken>
<ns1:RecipientToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">

<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:RecipientToken>
<ns1:AlgorithmSuite>
<wsp:Policy>
<ns1:Basic256 />
</wsp:Policy>
</ns1:AlgorithmSuite>
<ns1:Layout>
<wsp:Policy>
<ns1:Lax />
</wsp:Policy>
</ns1:Layout>
<ns1:IncludeTimestamp />

<ns1:ProtectTokens />
<ns1:OnlySignEntireHeadersAndBody />
</wsp:Policy>
</ns1:AsymmetricBinding>
<ns2:SignedSupportingTokens xmlns:ns2="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns2:SamlToken ns2:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns2:WssSamlV11Token10 />
</wsp:Policy>
</ns2:SamlToken>
</wsp:Policy>
</ns2:SignedSupportingTokens>
<ns3:Wss10 xmlns:ns3="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns3:MustSupportRefKeyIdentifier />
<ns3:MustSupportRefIssuerSerial />

</wsp:Policy>
</ns3:Wss10>
</wsp:Policy>

On the client, beside setting up the policy, you'll also need to configure a SAMLCredentialMapper and a PKICredentialMapper. The PKICredentialMapper needs to have access to a keystore that contains the Private Key used to sign the request.

OSB Proxy Service using OSB Policy


The OSB policy is really just the WLS 9.2 Web Services stack.. Basically, you need to create a WS-Policy and attach it to the request in the proxy service. Also, need to make sure to configure the proxy service to process the WS-Security header.




The actual policy is below.

<?xml version="1.0"?>

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="saml-policy"
>

<wssp:Identity>
<wssp:SupportedTokens>
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-2004-01-saml-token-profile-1.0#SAMLAssertionID">
<wssp:Claims>
<wssp:ConfirmationMethod>sender-vouches</wssp:ConfirmationMethod>
</wssp:Claims>
</wssp:SecurityToken>
</wssp:SupportedTokens>
</wssp:Identity>

<wssp:Integrity>

<wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<wssp:CanonicalizationAlgorithm
URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>

</wssp:Integrity>


<wssp:MessageAge/>

</wsp:Policy>



Setting up OSB to consume the SAML Assertion and validate the signature requires the creation of a SAML Identity Asserter. NOTE: In the asserting party configuration, OSB uses a relative path Remember this if you run into trouble getting OSB to understand the assertion.

OSB Business Service using OSB Policy


This was really the only "tricky" part. I had to modify the saml-sv policy to sign the timestamp. The policy on the business service doesn't require it, but it does expect the Timestamp to have a wsu:Id, and making OSB sign it does exactly that. The set-up in OSB is the same as the proxy service, just configure the policy on the request.

The policy on the business service is as follows:


<?xml version="1.0"?>

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="saml-policy-to-business-service"
>

<wssp:Identity>
<wssp:SupportedTokens>
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-2004-01-saml-token-profile-1.0#SAMLAssertionID">
<wssp:Claims>
<wssp:ConfirmationMethod>sender-vouches</wssp:ConfirmationMethod>
</wssp:Claims>
</wssp:SecurityToken>
</wssp:SupportedTokens>
</wssp:Identity>

<wssp:Integrity>

<wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<wssp:CanonicalizationAlgorithm
URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.w3.org/TR/1999/REC-xpath-19991116">
wsp:GetHeader(./wsse:Security/wsu:Timestamp)
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>

</wssp:Integrity>

<wssp:MessageAge/>

</wsp:Policy>



When adding a business service that requires signing, the proxy service has to have a Service Key Provider. This is really just a wrapper around the policies stored in the PKI Mapper. Basically, you select an alias to use to sign the message going to that business service.



Inside of the realm, in order to be able to configure a Service Key Provider, the PKI CredMapper needs to exist. In addition, the SAML Credential Mapper is required to generate the SAML Assertion.

WLS Web Services service using WS-Policy 1.2


This is pretty straight forward.



@WebService
@Policy(uri = "policy:Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml")
public class Test {


public String hello(String in) {
System.out.println("=====>"+in);
return "Hello "+in;
}

}



Summary


In setting up a scenario as complicated as this there will undoubtedly be challenges. Use the debug settings inside of WLS, they are your friend. In my set-up I added the following JAVA_OPTIONS to my setDomainEnv.cmd


set JAVA_OPTIONS=%JAVA_OPTIONS% %JAVA_PROPERTIES% -Dwlw.iterativeDev=%iterativeDevFlag% -Dwlw.testConsole=%testConsoleFlag% -Dwlw.logErrorsToConsole=%logErrorsToConsoleFlag%
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.wsee.verbose=* -Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true -Dweblogic.xml.crypto.encrypt.verbose=true -Dweblogic.xml.crypto.dsig.debug=true-Dweblogic.xml.crypto.dsig.verbose=true -Dweblogic.wsee.security.debug=true -Dweblogic.wsee.security.verbose=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.xml.crypto.wss.debug=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.xml.crypto.wss.verbose=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.xml.crypto.keyinfo.debug=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.xml.crypto.keyinfo.verbose=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.xml.crypto.dsig.debug=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.xml.crypto.dsig.verbose=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.xml.crypto.encrypt.debug=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.xml.crypto.encrypt.verbose=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.servlet.DIEnabled=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecuritySAMLService=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecuritySAMLCredMap=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecuritySAMLAtn=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecuritySAMLLib=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecuritySAML2Service=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecuritySAML2CredMap=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecuritySAML2Atn=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecuritySAML2Lib=true
set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.debug.DebugSecurityCredMap=true


This gives you good visibility into where the issues are. Also can't hurt to have the weblogic.security.atn and weblogic.security.credmap debug categories on. When trying to figure out what is going on, more information is better. Be patient, this will spit out a lot of information, but look through it and you can normally get to the issue.

Tuesday, October 13, 2009

Oracle Service Bus: Pass-Through vs. Active Security

I was talking to a customer about inserting the Oracle Service Bus (OSB) into their SOA infrastructure. The customer was prepared for a lengthy effort to get OSB to forward the SAML assertion sent from the client on to the service.

However, the fact is, the security functionality in OSB supports this use case exactly and with minimal effort.

The processing of message level security headers can be handled in two distinct ways in OSB: Pass-Through and Active Intermediary modes.

As an active intermediary, OSB processes the security headers in the SOAP request and enforces security policies on the messages. Additionally, in this mode, OSB can add new security headers (including new authentication tokens) to the request that is forwarded on to the service. I’ll leave further discussion for other posts.

In pass-through mode, OSB leaves the SOAP message untouched and simply routes the request on to its destination service. This means that all security headers in the original request are preserved in the request sent on to the destination service. So if a SAML assertion is sent in the original request through OSB, it will be part of the request being sent on to the service.

Pass-through mode is great when OSB is being inserted into a working infrastructure (maybe for the purpose of SLA management) where the web services already have security in place and no identity transformation is required as requests move from the clients through OSB and on to the services themselves.

Wednesday, October 7, 2009

Protecting OAM (IdentityXML) with OSB

A great use case for this blog - "How to add OSB in front of OAM". This question came from a customer, and touches on a number of interesting issues

This link has all of the information about the WSDL for Identity.xml.


This link describes how to use WSDL in OSB


To clarify, you can load the WSDL as a file into OSB and use those local WSDLs to work with the services of IdentityXML. The fact that OAM does not expose the WSDL in the ?WSDL form is not an issue. OSB is happy to work with a file.

So, basically:

1 - Create a proxy service for a WSDL ( this will expose ?WSDL to clients)
2 - Create a business service for an Identity XML using the same WSDL
3 - Create a pipeline that maps that routes the requests (very simple mapping...not much work)

The most interesting part here is how to propagate the credentials through OSB.

IdentityXML uses a proprietary format - It doesn't use WS-Security. IdentityXML looks for all of the information in the body, but I would argue a "better" implementation and a better use of OSB would be to add more standard WS-Security tokens to the exposed WSDL.

For example, you could replace the username/password elements with WS-Security UsernameTokenProfile and the ObSSOToken could be mapped to a custom WS-Security BinaryToken (common) practice.

To this, you wouldn't process the WS-Security Envelope in OSB, but rather do some transformations from the exposed WSDL and WS-Security messages to their appropriate localtion inside of the actual IdentityXML endpoint running in OAM.

Thursday, October 1, 2009

Calling Oracle Service Bus from MSFT WCF Client Using an STS

I hope that this is the first of two posts. In the second post, I want to able to describe how to do this use case with out an STS. As people know from this blog, I think that an STS has a time and a place. When I first did this integration, there was a real reason for having the STS. We were implementing what was essentially the MSFT claims based authorization model. The STS was calling out to an entitlements system than needed to be invoked using native .net authentication. The alternative was to have OSB generate a Kerberos Ticket for a user that it didn't have the password, and call the entitlements service. Let's just say many people consider this against security best practices. Now that I'm faced with doing this again for another customer, I eager to figure out how to do this without the STS. That aside, here's the approach.

Also, I couldn't have done this without Symon Chang, Anand Kothari, Wil Hopkins - very very smart engineers.

Overview


WCF, by default uses windows authentication. Windows authentication is based on Kerberos, so from the WCF perspective, the most logical way of propagating identity would be to use WS-Security Kerberos Token Profile. This is the standard way of conveying a Kerberos Ticket in a SOAP Message. This is supported in WCF OOTB.


The problem is that OSB 10gR3 does not. OSB10gR3 has no support for Kerberos at the message level. OSB does have support for Kerberos as part of the transport level security provided by SPNEGO. As for message level, OSB 10gR3 has support for Usename/Password, X.509 Certificate, and SAML profiles for WS-Security 1.0. SAML provides the best fit for this use case since it allows for the identity in the windows environment to remain native, only relying on SAML when calling services on the OSB.


WCF also supports WS-Security SAML 1.1 Token Profile for WS-Security 1.0, so this seems like a good profile to use to meet the requirements, and therefore focus on. WCF requires a Security Token Service (STS) to generate the SAML Assertion. Microsoft provides a sample, but the sample needs to be modified to generate a SAML Assertion that OSB understands. Also, WCF favors symmetric bindings for WS-Security. This is probably because WS-Security Kerberos Token profile uses the Kerberos Session Id as the key. OSB 10gR3 only supports an asymmetric binding - X.509 certificates are used to sign the message and bind the SAML assertion to it.


On the OSB side, a pipeline needs to be configured to handle the WS-Security policies. The inbound policy is WS-Security SAML 1.1 Token Profile for WS-Security 1.0 and the outbound policy is that the message is signed by the service. This is because MSFT expects that an endpoint that is protected using WS-Security will secure the response as well. To support this OSB configuration, WLS Security realm needs to be configured to consume and validate SAML Assertions as well as configure Public/Private Key pairs and corresponding trust stores for the message signature operations dictated by the WS-Security policies.


The flow is that a WCF client calls the STS. The STS generates a SAML Assertion signed by the STS that contains the name of the user as the Subject. The SAML Assertion uses the sender-vouches confirmation method. The SAML Assertion is added to the WS-Security Header, and the message is signed by invoking service. The message is sent to OSB where the SAML Assertion is verified along with the message signature. Once the message is processed, the return message is signed by the OSB identity. The signature is validated by the WCF client to ensure that the message has not been tampered and was sent by the OSB.

Customizing the STS Sample to Work with OSB


The sample STS provided by Microsoft needs to be modified to work with OSB in this scenario. The sample STS has the following issues:



  • Sample STS needs to be modified to use X509RawCertificate format. OSB does not support SHA1Thumbprint
  • Sample STS needs to be modified to use Sender-Vouches confirmation method instead of Holder of Key.
  • Sample STS needs to be modified to use sign the assertion with the private key of the issuer, not the encrypted key. OSB does not the use of symmetric encrypted keys, only un-encrypted asymmetric keys.
  • Sample STS needs to be modified to include an AuthenticationStatement in the SAML Assertion. This is where OSB looks for the user's identity.
  • Sample STS needs to be modified to add a wsu:Id to the saml:Assertion, otherwise WCF cannot use it as an IssuedToken with an asymmetric binding


These issues can be addressed mainly by modifying the SamlTokenCreator.class

//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;

using System.Collections.Generic;
using System.Collections.ObjectModel;

using System.IdentityModel.Tokens;

using System.ServiceModel;
using System.ServiceModel.Security;
using System.ServiceModel.Security.Tokens;
using System.Text;
using System.Xml;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.Configuration;
using System.Security.Principal;
using Common;


namespace Microsoft.ServiceModel.Samples.Federation
{
public sealed class SamlTokenCreator
{
#region CreateSamlToken()
/// <summary>
/// Creates a SAML Token with the input parameters
/// </summary>
/// <param name="stsName">Name of the STS issuing the SAML Token</param>
/// <param name="proofToken">Associated Proof Token</param>
/// <param name="issuerToken">Associated Issuer Token</param>
/// <param name="proofKeyEncryptionToken">Token to encrypt the proof key with</param>
/// <param name="samlConditions">The Saml Conditions to be used in the construction of the SAML Token</param>
/// <param name="samlAttributes">The Saml Attributes to be used in the construction of the SAML Token</param>
/// <returns>A SAML Token</returns>
public static SamlSecurityToken CreateSamlToken(string stsName,
BinarySecretSecurityToken proofToken,
SecurityToken issuerToken,
SecurityToken proofKeyEncryptionToken,
SamlConditions samlConditions,
IEnumerable<SamlAttribute> samlAttributes)
{





// Create a security token reference to the issuer certificate
SecurityKeyIdentifierClause skic = issuerToken.CreateKeyIdentifierClause<X509RawDataKeyIdentifierClause>();
SecurityKeyIdentifier issuerKeyIdentifier = new SecurityKeyIdentifier(skic);

//Get the user
WindowsIdentity wi = ServiceSecurityContext.Current.WindowsIdentity;

// Create a SamlSubject
SamlSubject samlSubject = new SamlSubject(SamlConstants.UserNameNamespace,
SamlConstants.UserName,
wi.Name);
//Set the Confirmation method to Sender-Vouches
samlSubject.ConfirmationMethods.Add(SamlConstants.SenderVouches);

//Create the Authentication Statement
SamlAuthenticationStatement samlAuthStatement = new SamlAuthenticationStatement();
samlAuthStatement.SamlSubject = samlSubject;

// Put the SamlAttributeStatement into a list of SamlStatements
List<SamlStatement> samlSubjectStatements = new List<SamlStatement>();
samlSubjectStatements.Add(samlAuthStatement);

// Create a SigningCredentials instance from the key associated with the issuerToken.
SigningCredentials signingCredentials = new SigningCredentials(issuerToken.SecurityKeys[0],
SecurityAlgorithms.RsaSha1Signature,
SecurityAlgorithms.Sha1Digest,
issuerKeyIdentifier);


// Create the SamlAssertion
String assertionId = "_"+Guid.NewGuid().ToString();

SamlAssertion samlAssertion = new SamlAssertion(assertionId,
"uri:"+stsName.Replace(' ','_'),
DateTime.UtcNow,
samlConditions,
new SamlAdvice(),
samlSubjectStatements
);

//Wrap the SamlAssertion so that the wsu:Id can be added
CustomSamlAssertion customAssertion = new CustomSamlAssertion(samlAssertion);

// Set the SigningCredentials for the SamlAssertion
customAssertion.SigningCredentials = signingCredentials;


// Create a SamlSecurityToken from the SamlAssertion and return it
SamlSecurityToken st = new SamlSecurityToken(customAssertion);

return st;
}

#endregion

private SamlTokenCreator() { }

static X509Certificate2 LookupCertificate(StoreName storeName, StoreLocation storeLocation, string thumbprint)
{
X509Store store = null;
try
{
store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindByThumbprint,
thumbprint, false);
if (certs.Count != 1)
{
throw new Exception(String.Format("FedUtil: Certificate {0} not found or more than one certificate found", thumbprint));
}
return (X509Certificate2)certs[0];
}
finally
{
if (store != null) store.Close();
}
}

}
}


This code above references another class - CustomSAMLAssertion.class. This class fixes the issue of the SAMLAssertion not having a wsu:Id

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Text;
using System.Xml;
using System.IO;

namespace Common
{
class CustomSamlAssertion: SamlAssertion
{


public CustomSamlAssertion(SamlAssertion theAssertion):
base(
theAssertion.AssertionId,
theAssertion.Issuer,theAssertion.IssueInstant,
theAssertion.Conditions,
theAssertion.Advice,
theAssertion.Statements)
{


}

public override void WriteXml(System.Xml.XmlDictionaryWriter writer, SamlSerializer samlSerializer, System.IdentityModel.Selectors.SecurityTokenSerializer keyInfoSerializer)
{
StringBuilder myBuilder = new StringBuilder();
XmlDictionaryWriter myWriter = XmlDictionaryWriter.CreateDictionaryWriter(XmlDictionaryWriter.Create(myBuilder));


base.WriteXml(myWriter, samlSerializer, keyInfoSerializer);

myWriter.Close();

String contents = myBuilder.ToString();

//contents = contents + "";


XmlDictionaryReader reader =
XmlDictionaryReader.CreateDictionaryReader(XmlDictionaryReader.Create(new StringReader(contents)));


StringBuilder myBuilder2 = new StringBuilder();
XmlDictionaryWriter myWriter2 = XmlDictionaryWriter.CreateDictionaryWriter(XmlDictionaryWriter.Create(myBuilder2));

try
{
while (reader.Read())
{

WriteShallowNode(reader, writer);


}
}
catch (Exception e)
{
Console.Out.WriteLine(e);
//writer.Flush();
//String contents2 = myBuilder2.ToString();

//throw e;

return;


}

//writer.Flush();

}

void WriteShallowNode(XmlReader reader, XmlWriter writer)
{

if (reader == null)
{

throw new ArgumentNullException("reader");

}

if (writer == null)
{

throw new ArgumentNullException("writer");

}



switch (reader.NodeType)
{

case XmlNodeType.Element:

//writer.WriteStartElement(reader.LocalName);

writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);

writer.WriteAttributes(reader, true);

if (reader.LocalName.Equals("Assertion")) {


writer.WriteAttributeString(
"wsu",
"Id",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
this.AssertionId);
}



if (reader.IsEmptyElement)
{

writer.WriteEndElement();

}

break;

case XmlNodeType.Text:

writer.WriteString(reader.Value);

break;

case XmlNodeType.Whitespace:

case XmlNodeType.SignificantWhitespace:

writer.WriteWhitespace(reader.Value);

break;

case XmlNodeType.CDATA:

writer.WriteCData(reader.Value);

break;

case XmlNodeType.EntityReference:

writer.WriteEntityRef(reader.Name);

break;

case XmlNodeType.XmlDeclaration:

break;


case XmlNodeType.ProcessingInstruction:

writer.WriteProcessingInstruction(reader.Name, reader.Value);

break;

case XmlNodeType.DocumentType:

writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);

break;

case XmlNodeType.Comment:

writer.WriteComment(reader.Value);

break;

case XmlNodeType.EndElement:

writer.WriteEndElement();

break;

}

}


}
}


Configuring the WCF Client


WCF supports a large number of authentication methods and profile bindings simply and easily. This is typically done by modifying the configuration file through the WCF Service Configuration Editor. Unfortunately, there is no way through configuration to set-up the client. This needs to be done programmatically. The WS-Policy that OSB uses is essentially as follows:

<?xml version="1.0"?>
<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
>
<sp:AsymmetricBinding>
<wsp:Policy>
<sp:InitiatorToken>
<wsp:Policy>
<sp:X509Token
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssX509V3Token10/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:InitiatorToken>
<sp:RecipientToken>
<wsp:Policy>
<sp:X509Token
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
<wsp:Policy>
<sp:WssX509V3Token10/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:RecipientToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Lax/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:ProtectTokens/>
<sp:OnlySignEntireHeadersAndBody/>
</wsp:Policy>
</sp:AsymmetricBinding>
<sp:SignedSupportingTokens>
<wsp:Policy>
<sp:SamlToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssSamlV11Token10/>
</wsp:Policy>
</sp:SamlToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
<sp:Wss10>
<wsp:Policy>
<sp:MustSupportRefKeyIdentifier/>
<sp:MustSupportRefIssuerSerial/>
</wsp:Policy>
</sp:Wss10>
</wsp:Policy>


The translation between this policy and the WCF APIs is pretty straight forward with one exception - the SAML Token itself. In WCF, the SAML Token is retrieved from the STS, so the WCF client needs to be configured to communicate to it. In WCF, authentication from a token retrieved from an STS is called IssuedToken. All of this can be done programmatically through the WCF APIs. For simplicity sake, the creation of the custom AsymmetricSecurity binding can be encapsulated as a WCF Binding Element Extension. This allows for the inclusion of custom binding elements () inside of a custombinding.


<extensions>
<bindingElementExtensions>
<add name="osbsecurity" type="OSBWCFExtensions.OSBSecurityElement, OSBWCFExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=63fc46aa660659ca" />
</bindingElementExtensions>
</extensions>

<bindings>
<customBinding>
<binding name="HelloWorldServiceServiceSoapBinding">
<textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
messageVersion="Soap12" writeEncoding="utf-8">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</textMessageEncoding>
<osbsecurity STSAddress="http://fedtest/FederationSample/HomeRealmSTS/STS.svc"/>
<httpsTransport manualAddressing="false" maxBufferPoolSize="524288"
maxReceivedMessageSize="165536" allowCookies="false" authenticationScheme="Anonymous"
bypassProxyOnLocal="false" hostNameComparisonMode="WeakWildcard"
keepAliveEnabled="true" maxBufferSize="165536" proxyAuthenticationScheme="Anonymous"
realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
useDefaultWebProxy="true" requireClientCertificate="true"/>



</binding>
</customBinding>

Inside of the OSBSecurityElement, the WCF API calls are made that create the proper binding for sending a SAML Assertion to OSB.

protected override System.ServiceModel.Channels.BindingElement CreateBindingElement()
{

//Retrieve the STS Address from the config
ConfigurationProperty stsConfig = this.Properties["STSAddress"];


//Set-up the Asymmetric binding with the recipient and initiator's parameters
//Keys are identified by the issuersSerial as required by the policy
//OSB does not support derived keys, so they are disabled
X509SecurityTokenParameters initiatorParams = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.IssuerSerial, SecurityTokenInclusionMode.AlwaysToRecipient);
initiatorParams.RequireDerivedKeys = false;


X509SecurityTokenParameters recipientParams = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.IssuerSerial, SecurityTokenInclusionMode.Never);
recipientParams.RequireDerivedKeys = false;

AsymmetricSecurityBindingElement security = new AsymmetricSecurityBindingElement(recipientParams, initiatorParams);


security.SecurityHeaderLayout = SecurityHeaderLayout.Lax;
security.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
security.SetKeyDerivation(false);

//Configure the STS and the resulting SAML Assertion as a signed supporting token
WSHttpBinding stsBinding = new WSHttpBinding();

//This credential type is how the caller identifies themself to the STS
stsBinding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;

IssuedSecurityTokenParameters issuedTokenParamters =
new IssuedSecurityTokenParameters("", new EndpointAddress((String)base["STSAddress"]), stsBinding);

issuedTokenParamters.RequireDerivedKeys = false;
issuedTokenParamters.ReferenceStyle = SecurityTokenReferenceStyle.Internal;
issuedTokenParamters.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
security.EndpointSupportingTokenParameters.Signed.Add(issuedTokenParamters);

//Set this to process the signature of the response
security.AllowSerializedSigningTokenOnReply = true;

return security;
}
}

By using the custom binding element extension, the client code remains unchanged:

HelloWorldClient client = new HelloWorldClient();
Console.Out.WriteLine(client.test1("WCF Client"));

Configuring OSB Domain's Security Domian


The inbound SAML processing requires the creation and configuration of a SAML Identity Asserter. For this scenario, the SAML V2 Identity Asserter should be used. It supports SAML 1.1 sender-vouches subject confirmation method. It needs to be configured with an asserting party that corresponds to the STS. Since the SAML Assertion is signed, OSB needs to be configured to trust the signer of the assertion. This can be done my adding the certificate authorities (CAs) that make up the STS's certificate chain to the list of trusted CAs. Which keystore to add them to depends of the trust mode that the OSB domain is running, but by default, these can be added to the cacerts keystore found in JRE_HOME/jre/lib/security.

In some scenarios, the identity being asserted by the SAML assertion can be trusted, and in others, the identity needs to be validated against some other authentication source - mainly Active Directory. OSB domain can be configured to support both. To trust the identity, a SAML Authentication Provider needs to be added to the realm. Make sure to configure it with an appropriate JAAS Control Flag. The simplest way to avoid any conflicts is to mark all of the authentication providers as OPTIONAL. Also, the asserting party configuration in the SAML Identity Asserter needs to be configured to Allow Virtual Users. Otherwise, the SAML Authentication Provider will not work. If "Allow Virtual Users" is not checked for the asserting party, then the security realm will try to validate the user against the authentication providers configured for the realm. The name that the STS above generates is of the form domain/username. In most cases, a custom username mapper will need to be written and configured on the SAML Identity Asserter to split off the domain portion of the name.

A PKI CredMapper needs to be configured so that OSB can generate digital signatures for outbound requests. The PKI CredMapper is configured to point to a Java Keystore. The identity of the OSB should be available in this keystore, and should be the same identity as the ServiceCert configured in the WCF client.

Configuring the OSB Pipeline


The OSB service needs to be configured to process the WS-Security header sent by WCF. The inbound request message needs to be configured with the SAML Token Profile 1.0 - Sender Vouches policy.
<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="samlPolicy-sender-vouches-simple">
<wssp:Identity>
<wssp:SupportedTokens>
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-2004-01-saml-token-profile-1.0#SAMLAssertionID">
<wssp:Claims>
<wssp:ConfirmationMethod>sender-vouches</wssp:ConfirmationMethod>
</wssp:Claims>
</wssp:SecurityToken>
</wssp:SupportedTokens>
</wssp:Identity>
</wsp:Policy>

The OSB response needs be signed. This can be done by creating a Service Key Provider that points to the identity of the OSB, and then adding the predefined Sign.xml policy to the response operation.

References


The mapping of WCF configuration to WS-Policy and the security capabilities are nicely described

A description of why wsu:Id needs to be added to the SAML Assertion

A good discussion of a variation of this use case

The sample STS from Microsoft, which has extended to integrate with OSB