Showing posts with label owsm. Show all posts
Showing posts with label owsm. Show all posts

Thursday, April 17, 2014

Identity Propagation from OAG to REST APIs protected by OWSM

Introduction


This post describes the necessary configuration for propagating an end user identity from OAG (Oracle API Gateway) to REST APIs protected by OWSM (Oracle Web Services Manager).
The requirements are:

1) Have a Java Subject established in the REST API implementation.
2) Prevent direct access to the REST API, i.e., only OAG should be able to successfully invoke it.

A recurrent question is how OWSM protects REST APIs and which types of tokens it supports when doing so.
If we look at the current OWSM (11.1.1.7) predefined policies, we notice a policy named
oracle/multi_token_rest_service_policy, described (verbatim) as:

"This policy enforces one of the following authentication policies, based on the token sent by the client:

HTTP Basic—Extracts username and password credentials from the HTTP header.

SAML 2.0 Bearer token in the HTTP header—Extracts SAML 2.0 Bearer assertion in the HTTP header.

HTTP OAM security—Verifies that the OAM agent has authenticated user and establishes identity.

SPNEGO over HTTP security—Extracts Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO) Kerberos token from the HTTP header."

In this specific use case, we are assuming the end user has already been authenticated by some other means before reaching OAG. In other words, we are assuming OAG gets some sort of token
and validates the user locally, thus populating its authentication.subject.id attribute. This token OAG receives can be an OAM token, a Kerberos token, SAML token, you name it. It is matter of
a design decision based on OAG's client capabilities.

In a use case like this, it's very unlikely that OAG will have the end user password, which eliminates the HTTP Basic header option. The remaining three are all good candidates. In this post we deal with a SAML 2.0 Bearer token in the HTTP Header. Our flow ends up being something like this: OAG Client -> "some token" -> OAG -> SAML 2.0 Bearer -> OWSM -> REST API.

We're going to examine all necessary configuration in OAG, OWSM and in the REST API application. Buckle up, folks! And let's do it backwards.

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.

Tuesday, September 25, 2012

Attaching OWSM policies to JRF-based web services clients

I've recently came across a question in one of our internal mailing lists where a person was under the impression that he would have to write code to propagate the identity when making a web service call using OWSM policies. My answer was something like: "depending on the type of your client you may have to write some very small piece of code to attach a policy, but you should not write code at all to either retrieve the executing client identity or to do the propagation itself". Fortunately, I had an unpublished article that applied 100% to his use case. And here it is now (a little bit revamped).

OWSM (Oracle Web Services Manager) is Oracle's recommended method for securing SOAP web services. It provides agents that encapsulate the necessary logic to interact with the underlying software stack   on both service and client sides. Such agents have their behavior driven by policies. OWSM ships with a bunch of policies that are adequate to most common real world scenarios.

Applying policies to services and clients is usually a straightforward task and can be accomplished in different ways. This is well described in the OWSM Administrators Guide. Looking from the client perspective, the docs describe how to attach policies to SOA references, connection-based clients (typically ADF-based clients) and standard Java EE-based clients using either Enterprise Manager or wlst.

Oracle FMW components (like OWSM agents) are typically deployed on top of a thin software layer called JRF (Java Required Files), providing for the required interoperability with software stacks from different vendors.

This post is a step-by step showing how to code a JRF-based client and attach OWSM policies to it at development-time using Oracle JDeveloper.

Friday, October 7, 2011

Virtual Users in OIF, Weblogic and OWSM

One of the main strengths of SAML is the ability to communicate identity information across security domains that do not necessarily share the same user base. In other words, the authenticated user in one security domain does not necessarily exist in the target security domain providing the service.

Such concept is supported in all major Oracle products that consume SAML tokens: OIF, Weblogic Server and OWSM. The sole purpose of this post is to show how to configure it in these products. Setting up SAML services as a whole involves more than what’s showed here and I recommend the official product documentation for detailed steps.

I hope this can be helpful to someone out there.

OIF (Oracle Identity Federation)


OIF enables federated single sign on for users behind a web browser.

It calls the aforementioned concept “Transient Federation” and enables it via a checkbox (that should be unchecked) in Enterprise Manager OIF’s Console. Notice it also supports the concept of a "Mapped Federation", where the incoming identity is mapped to some generic user in the local identity store. But here I am talking about the case where there's no mapping. The user in the SAML assertion is simply trusted.

In order to enable a Transient Federation in OIF, simply make sure “Map Assertion to User Account” checkbox is unchecked in the Service Provider Common tab.

oif

Weblogic Server


Weblogic server provides SAML services that can be leveraged by Web SSO as well web services.
Weblogic calls the concept Virtual Users and implements it in its SAML2IdentityAsserter along with the SAMLAuthenticator.

First, you need to enable your server as a SAML Service Provider. Notice this is done at the server level. Go to Environment –> servers –> <Pick server from list> to get into the screen below:

SAMLServiceProvider

Then add a SAML2IdentityAsserter to the authentication providers list and add an Identity Provider (who does not need to be another Weblogic server) Partner to SAML2IdentityAsserter. Notice that you can add either a Web SSO partner provider or a Web service partner provider. In the case of Web SSO, Weblogic Console will ask you for the partner metadata file.

wls_IdpPartner

Monday, June 27, 2011

Working with OWSM Policies – Part 1 of some

In this post I discuss the available options to work with OWSM (Oracle Web Services Manager) policies in JDeveloper and Enterprise Manager. OWSM is a component available along with the Oracle SOA Suite and provides policy enforcement point (PEP) agents for SOAP-based messages.

Typically, a service policy is attached to a service endpoint to enforce some pre-defined rules (like enforcing a SAML token, Kerberos token, message confidentiality, SSL, etc). And a corresponding client policy is attached to the client in order to transform the outgoing SOAP message, making it suitable to be enforced in the server side.

OWSM Policies in JDeveloper


OWSM supports two types of repositories for its policy files: file system or database.
When working with JDeveloper, you can choose which one you want to use.
By default, JDeveloper reads policies from the file system. To check that out, go to Tools –> Preferences –>WS Policy Store (on left).

image

The File Store Default Location refers to DefaultDomain oracle/store/gmds directory under $JDEV_USER_DIR’s systemxx.x.x.x.xx.xx.xx folder. Policies are under owsm/policies and assertions are under owsm/assertiontemplates.

Monday, February 21, 2011

Non-Standard approaches to SOA Identity Propagation and Authentication

I wanted to take some time to talk about a couple non-standard approaches to identity propagation and authentication that I sometimes see people take when building their web services.

These approaches are non-standard both because they well… don’t utilize the large body of standards that exist for web service security and because they are outside of what would probably be considered best practices by most people in the industry.

Custom Security Headers

It is fairly common to see people develop services that require authentication through a custom security header.

Now, if your service will consume a token that is truly custom, which is to say outside the standard token types defined in the WSS specs, then this is a perfectly reasonable approach. A good example of this would be a service that can consume a web SSO token from Oracle Access Manager.

This approach is especially powerful if your service can consume the custom token plus one or more of the standard tokens. One example would be a service setup to consume the Oracle Access Manager cookie or a standard username token.

However, one mistake you often see people make is to hard code in a required custom security header that is in essence the same as one of the standard WSS authentication methods. Usually, it is a custom security header that just holds a username and password, which makes it equivalent to the WSS username token.

There are several reasons developers take this approach but they mostly center on not understanding the flexibility and value of the security functionality that is already built into the container that is running their service. For example, sometimes developers will say that they need a custom token since the container will only compare the username in username token to the UID attribute in the directory, which is simply not the case.

Even if custom handling of authentication is required, it is still better to utilize a standard token (with custom handling) over a custom token.

Another mistake you see people make is once they decide they need a custom token, they will just turn off security at the container level and process the token inside of their service. It is far better for custom authentication code to live inside of the container than in the service itself. Staying with the container for security will:
  • Allow you to easily use your custom token with other services
  • Allow you to switch to a standard authentication method down the road
  • Keep you compatible with authorization and auditing done at the container level which you may want to keep even if you need to do custom authentication.
I’d propose the following hierarchy of approaches to web service authentication and identity propagation, going from most ideal to least ideal:
  1. Use of standard authentication tokens that are processed by the container’s out-of-the-box functionality.
  2. Use of standard authentication tokens that are processed by custom code in the container.
  3. Use of custom authentication tokens/headers that are processed by custom code in the container.
  4. Use of custom authentication tokens/headers that are processed by code in the service itself.
Propagating Identity in the SOAP Request Body

The second approach I’d like to discuss is specific to propagating an identity from a web app or service to a “downstream” service. The approach is simply to stick the user identity into the body of the request and consume it with custom code in the service. Now obviously, this approach is inherently insecure on its own, but it is an appropriate option in situations where the client of the service can be trusted. Usually this trust will be justified by the security that is present at the transport or network level.

Sticking a user identity into the body of the request is really analogous to use of the SAML bearer confirmation method which I discussed in my first post on this blog.

So, it is probably most useful to think of its appropriateness in the same terms. In the blog post on the bearer confirmation method I discussed some advantages that it had over just sticking the user identity into the request body. Now, I’d like to turn the table and look at some possible advantages putting the identity in the request body sometimes has over SAML with bearer and over creating a custom security header of a similar ilk.

Now, you are probably saying to yourself: Hold on Brian, you just laid out a hierarchy with using standards and container functionality at the top and custom approaches executed in the service code at the bottom.

Well that is true, but there is a reason that putting the identity in the request body and extracting it in the service should sometimes be considered. The main problem with SAML with the bearer confirmation method is a lack of support for it. This lack of support applies not only to server side WSS stacks but, perhaps even more importantly client side stacks. If you are creating a service that may be widely consumed and are operating in a very heterogeneous environment where client side developers maybe be using any number of different software packages for their development, then SAML with bearer may be the wrong choice for you. Likewise, it may be harder for consumers of your service to create a custom security header than to just stick the identity in the request body. Along similar lines, it may be easier to convey the format you want to use for the user identity in the WSDL if the identity is being put in the body.

The final consideration for whether propagating the identity in the request body is a good way to go centers on how many services you might have where such trusted identity propagation is deemed as acceptable. If you have a huge SOA infrastructure with tons of applications consuming tons of internal services then it probably makes sense to standardize on bearer or develop a custom security header and corresponding container plug-in to consume the header.

However, if you only have one service that fits this description and SAML with bearer isn’t supported by your stack, then it may be hard to justify the added effort of developing a custom token. In that case, just sticking the user identity in the request body may be the best way to go.

Thursday, September 16, 2010

Using asymmetric cryptography in OWSM

Introduction

It has been somewhat a common practice replicating the same java key store across interacting WLS domains so that web services clients and web services can chat with message protection (WSS - Web Services Security). In that case, OWSM (Oracle Web Services Manager) is able to default to the private key stored in the key store to encrypt the symmetric key used for message encryption/decryption.

While that’s acceptable in developments environments, it might not be in production mode. Organizations may want to eliminate the risk of having the private key shared and compromised in distributed WLS deployments.

This article describes how that can be implemented in OWSM by examining the required OWSM configuration and two sample client applications: one Oracle ADF using a Web Service Data Control and one non-ADF using JAX-WS proxy.

Long story short: web services providers distribute their public key to consumers, who in turn explicitly refer to them when attaching OWSM client-side policies by overriding the keystore.recipient.alias property. Keep reading if you are interested in seeing how that is done.

Configuring the WLS domain hosting the web service

Let’s first understand how OWSM gets the services it needs to work with WSS.

OWSM relies on OPSS (Oracle Platform Security Services) configuration for its required services. By default, OWSM looks for the “default” jpsContext to get a hold of them. We’re interested in credstore and keystore services. OPSS configuration is held in jps-config.xml that sits in $DOMAIN_HOME/config/fmwconfig.

In the snippet below, we’re looking at the default jpsContext in jps-config.xml.


   1: <jpsContexts default="default">
   2:     <!-- This is the default JPS context. All the mendatory services and Login Modules must be configured in this default context -->
   3:     <jpsContext name="default">
   4:         <serviceInstanceRef ref="credstore"/>
   5:         <serviceInstanceRef ref="keystore"/>
   6:         <serviceInstanceRef ref="policystore.xml"/>
   7:         <serviceInstanceRef ref="audit"/>
   8:         <serviceInstanceRef ref="idstore.ldap"/>
   9:     </jpsContext>

Line 5 above tells OWSM to look for the keystore serviceInstance, which is out-of-box defined as:


   1: <!-- KeyStore Service Instance -->
   2:         <serviceInstance name="keystore" provider="keystore.provider" location="./default-keystore.jks">
   3:             <description>Default JPS Keystore Service</description>
   4:             <property name="keystore.type" value="JKS"/>
   5:             <property name="keystore.csf.map" value="oracle.wsm.security"/>
   6:             <property name="keystore.pass.csf.key" value="keystore-csf-key"/>
   7:             <property name="keystore.sig.csf.key" value="sign-csf-key"/>
   8:             <property name="keystore.enc.csf.key" value="enc-csf-key"/>
   9:         </serviceInstance>

It basically says that ./default-keystore.jks is the keystore file holding the certificate keys for WSS.

Likewise, line 6 in the first snippet informs OWSM on the credential store service. The credential store holds the credentials required for accessing the keystore as well as the aliases defined within it. Here’s how the cred store service is configured in jps-config.xml. Notice the location attribute. It points to the current directory. In reality, the credential store (as a file) is materialized as cwallet.sso file in that directory.


   1: <!-- JPS Credential Store Service Instance -->
   2:         <serviceInstance name="credstore" provider="credstoressp" location="./">
   3:             <description>File Based Credential Store Service Instance</description>
   4:         </serviceInstance>

Ok, basic OPSS configuration understood, let’s create an asymmetric key pair for the WLS server hosting the web service.

To execute the subsequent commands, have the keytool tool in your system path.

> keytool -genkeypair -keyalg RSA -alias orakey -keypass welcome1 -keystore default-keystore.jks -storepass welcome1 -validity 3600 

Now default-keystore.jks contains a self-signed certificate whose encrypting algorithm is RSA. RSA will be used for encrypting the symmetric key that encrypts the SOAP message.

Notice the storepass parameter value, which is welcome1. That defines the keystore password. Also notice the alias parameter and keystore parameter values. Those need to be properly seeded in the credential store. Please refer to this article on how to create them.

Next step is exporting the public key to a file.

> keytool -export -keystore default-keystore.jks -alias orakey -file server.crt

Such public key (server.crt) is the one that is distributed to everyone who wants to call in the WSS web services in this WLS domain.

Configuring the WLS domain hosting the web service client


The same jps-config.xml configuration applies to the WLS client domain. But in the client we just need to import the server.crt file containing the server public key into the key store.

> keytool -import -trustcacerts -alias server-public-cert -file server.crt -keystore default-keystore.jks

Notice the alias name: server-public-cert. It defines to where the public key is copied within the key store. You’ll need this name when overriding the keystore.recipient.alias property in the OWSM policy attachment in the web service client application.

Overriding the keystore.recipient.alias property


In this example, our client application is an ADF one that invokes the web service via an ADF Web Service Data Control. I won’t go into the details of defining the data control here. That’s something that can be found in the ADF Developers Guide available at OTN or in JDeveloper itself.

Once the data control is defined, follow these steps:

1 - In Applications Navigator, click the DataControls.dcx file:

showDataControl

2 - In the DataControls.dcx structure, right click the ADF Data Control Web Service and then click Define Web Service Security… option:

defineWSS

3 – In the Edit Data Control Policies window, choose oracle/wss11_username_token_with_message_protection_client_policy. It will allow the client to propagate a fixed identity to the web service at the same time providing WSS to the entire SOAP message. Take a bit of time to read the policy description. It is worth it.

Do notice that choosing such policy implies that the web service is configured with the corresponding OWSM server-side policy.

policyAttachment

4 – Click the Override Properties button.

The csf-key property defined the key in the credential store that holds the user to be propagated to the web service in the UsernameToken header.

In order to create it, execute the following wlst online command in the client WLS domain:

> createCred(map=”oracle.wsm.security”,key=”manager-key”,user=”my-wife”,password=”yesdear”)

If you click the New Key.. button, you have the opportunity to define it right here (thus avoiding the wlst command above) and it will be pushed to the WLS domain credential store when you deploy your application (this “pushing credentials”  feature can be disabled if you want).

keystore.recipient.alias is what we’re really interested in this article. It references the alias name for the server public key in the WLS client domain’s key store.

overridingProperties

What if my client application is a non-ADF one?


If you had a JAX-WS proxy in a non-ADF client application, this is how you would do it.

Notice line 25. There we’re overriding the keystore.recipient.alias.

   1: …
   2: import java.net.MalformedURLException;
   3: import java.net.URL;
   4: import javax.xml.namespace.QName;
   5: import javax.xml.parsers.DocumentBuilderFactory;
   6: import javax.xml.ws.BindingProvider;
   7: import oracle.j2ee.ws.common.jaxws.ServiceDelegateImpl;
   8: import oracle.webservices.ClientConstants;
   9: import oracle.wsm.security.util.SecurityConstants;
  10: import org.w3c.dom.Element;
  11: …
  12:         String endpoint = "http://127.0.0.1:7101/context-root/GreetingsAppModuleService";
  13:  
  14:         URL wsdlURL = new URL(endpoint+"?WSDL");
  15:  
  16:        ServiceDelegateImpl serviceDelegate = new ServiceDelegateImpl(wsdlURL, new QName("http://xmlns.oracle.com/trunk/owsm/", "GreetingsAppModuleService"), oracle.webservices.OracleService.class);
  17:        MyAppModuleService port = serviceDelegate.getPort(new QName("http://xmlns.oracle.com/trunk/owsm/", "GreetingsAppModuleServiceSoapHttpPort"), GreetingsAppModuleService.class );
  18:        
  19:         InputStream isClientPolicy = this.getClass().getResourceAsStream("client-policy.xml");
  20:        
  21:         try {
  22:           ((BindingProvider)port).getRequestContext().put(ClientConstants.CLIENT_CONFIG, fileToElement(isClientPolicy));
  23:           ((BindingProvider)port).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);
  24:           ((BindingProvider)port).getRequestContext().put(SecurityConstants.ClientConstants.WSS_CSF_KEY, "manager-key");
  25:           ((BindingProvider)port).getRequestContext().put(SecurityConstants.ClientConstants.WSS_RECIPIENT_KEY_ALIAS, "server-public-cert");
  26:         }
  27:         catch (Exception ex) {
  28:           ex.printStackTrace();
  29:         }
  30:        
  31:  
  32:         return port.sayHello(name);
  33:  
  34:     public static Element fileToElement(InputStream f) throws IOException, Exception {
  35:        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
  36:        builderFactory.setValidating(false);
  37:        builderFactory.setNamespaceAware(true);
  38:        builderFactory.setIgnoringElementContentWhitespace(true);
  39:        builderFactory.setIgnoringComments(true);
  40:        return builderFactory.newDocumentBuilder().parse(f).getDocumentElement();
  41:     }


The client-policy.xml would be:



   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <oracle-webservice-clients>
   3:    <webservice-client>
   4:        <port-info>
   5:            <policy-references>
   6:                <policy-reference uri="oracle/wss11_username_token_with_message_protection_client_policy" category="security"/>
   7:             </policy-references>
   8:        </port-info>
   9:    </webservice-client>
  10: </oracle-webservice-clients>

Conclusion

In this article I showed how to use real asymmetric cryptography in OWSM by examining the keystore configuration details and looked at how web services client applications should be configured to support it. I believe this is a very important topic, since it eliminates the risk of having a single private key shared across WLS domains that participates in web services interactions.

Saturday, September 4, 2010

Hands-on WSRP Security in Oracle Fusion Middleware

Introduction

This article shows how to securely propagate a user identity to a portlet, have this identity asserted and authorized to view the portlet.

WSRP (Web Services Remote Portlets) are web services. In Oracle Fusion Middleware, the way of enabling identity propagation and authentication is via OWSM (Oracle Web Services Manager) policies. The concept is no different that securing any regular web service.

Portlets authorization is handled by the OPSS (Oracle Platform Security Services) layer once authentication is done.


Note: What follows has been implemented in Oracle Fusion Middleware 11g PS1 stack (JDeveloper, WLS, Web Center, OWSM and OPSS)

OWSM policies

OWSM  is the component that implements web services security and allows for runtime enforcement and declarative policy attachment within Oracle Fusion Middleware.

OWSM provides policies for different types of use case scenarios. For example, there are policies supporting SAML token profile, kerberos, WSS1.0, SSL, etc. I will soon write an article covering OWSM in detail.

For WSRP, let’s pick a SAML token profile with WSS1.0. It basically means that the portlet will require a SAML token in the incoming SOAP header and the whole message will be digitally signed and encrypted according to WSS1.0 standards.

On the portlet producer side, we’ll attach wss10_saml_token_with_message_protection_service_policy. And on the consumer side, we’ll attach wss10_saml_token_with_message_protection_client_policy.

The policy attached to the consumer adds the SAML token to the SOAP header and performs message signing/encryption. The policy attached to the provider verifies the signature, decrypts the message, retrieve the SAML token and delegates the assertion to the OPSS layer, which verifies its validity against the configured identity store.

Securing the Producer

Authentication

The policy wss10_saml_token_with_message_protection_service_policy needs to be attached to the portlet producer. Here I will show how to do it by updating oracle-webservices.xml. Do notice this can also be accomplished via Oracle Enterprise Manager.

oracle-webservices.xml is a packaging-time artifact, meaning it is not available in JDeveloper during design-time. Therefore, to edit the file, first deploy your application to an ear file, extract the file, update it and repackage it into the ear file.

The update itself is very simple. Look for port-component name=”WSRP_v2_Markup_Service” and add the highlighted snippet shown below:

<port-component name="WSRP_v2_Markup_Service" style="document"
                                  bindingQName="{urn:oasis:names:tc:wsrp:v2:bind WSRP_v2_Markup_Binding_SOAP"
                                  enabled="true"
                                  schemaValidateInput="false">
   
<policy-references>
     <policy-reference enabled="true" 
                                       uri="oracle/wss10_saml_token_with_message_protection_service_policy"
                                       category="security"/>
   </policy-references>

   <operations>
     ...
   </operations>
   <!-- start:deployment time generated info -->
   <deployment>
    <tie-class-name>oasis.names.tc.wsrp.v2.bind.runtime.WSRP_v2_Markup_Binding_SOAP_Tie</tie-class-name>
    <service-qname namespaceURI="urn:oasis:names:tc:wsrp:v2:wsdl"
localpart="WSRP_v2_Service"/>
    <soap-version>soap1.1</soap-version>
   </deployment>
   <!-- end:deployment time generated info   -->
   <servlet-link>WSRP_v2_Markup_Service</servlet-link>
 </port-component>



Note: The portlet producer application needs to be deployed in order to be called by consumers. You cannot “Click & Run” a secured portlet application from JDeveloper.


Key store and Credential store configuration

This is a necessary step only because the OWSM policies we’re using require message-level protection.

Both key store and credential store are WLS domain wide artifacts and WLS expects them to be found under $DOMAIN_HOME/config/fmwconfig folder.

In a nutshell, the key store contains the signing and encryption keys used to sign/encrypt/decrypt messages. Each key and the key store itself are password protected. They are also referred by an alias. These aliases and the corresponding passwords are stored in the credential store. When access to key store is required, the credential store is first queried for the necessary aliases and passwords.


Key store contents

The key store is created by using JDK’s keytool command. Assuming JDK is on your $PATH, type keytool in a command prompt and you will see the command options.

To list the contents of the key store, type:

keytool –list –v –keystore <keystore_filename> -storepass <keystore password>


How to create a certificate key

In order to protect the symmetric key used to actually encrypt the SOAP message, we need a PKI certificate in the key store. For the sake of this example, let's have this certificate under an alias called “orakey”, with password equals to “welcome1”.

keytool -genkeypair -keyalg RSA -alias orakey -keypass welcome1 -keystore default-keystore.jks -storepass welcome1 -validity 3600


Note: JDK 6 is required to generate a key store compatible with OWSM in FMW 11g.


Credential store contents

A credential store is split into maps. Each map contains a set of keys. Each key points to the actual credential. The aliases created in the key store must have a corresponding credential in the credential store. For example, if we have named our alias as “orakey” in the key store, there must be a credential named “orakey” in the credential store. Such credential is pointed by a predefined key names.

For OWSM, these key names are sign-csf-key and enc-csf-key, which are used to sign and encrypt/decrypt messages. Another important key is keystore-csf-key, which holds the key store alias and password and is used to open the key store.

OWSM uses a predefined credential map named oracle.wsm.security.


How to populate the credential store

The credential store can be populated through WLST or Oracle Enterprise Manager.

In WLST, execute these online commands:

wls:/DefaultDomain/serverConfig> createCred(map="oracle.wsm.security", key="keystore-csf-key", user="owsm", password="welcome1", desc="Keystore key")
wls:/DefaultDomain/serverConfig> createCred(map="oracle.wsm.security", key="enc-csf-key", user="orakey", password="welcome1", desc="Encryption key")
wls:/DefaultDomain/serverConfig> createCred(map="oracle.wsm.security", key="sign-csf-key", user="orakey", password="welcome1", desc="Signing key")

Such commands update your domain level credential store. Normally, it corresponds to cwallet.sso under $DOMAIN_HOME/config/fmwconfig.


Verifying key store and credential store configuration in jps-config.xml

Open jps-config.xml located at $DOMAIN_HOME/config/fmwconfig and check whether the following entry exists. This should be out of box configured.

<serviceInstance location="./" provider="credstoressp" name="credstore"> 
  <description>File Based Credential Store Service Instance</description>
 </serviceInstance>
 <serviceInstance name="keystore" provider="keystore.provider" location="./default-keystore.jks">
   <description>Default JPS Keystore Service</description>
   <property name="keystore.type" value="JKS"/>
   <property name="keystore.csf.map" value="oracle.wsm.security"/>
   <property name="keystore.pass.csf.key" value="keystore-csf-key"/>
   <property name="keystore.sig.csf.key" value="sign-csf-key"/>
   <property name="keystore.enc.csf.key" value="enc-csf-key"/>
 </serviceInstance>


Also, make sure the “default” context references the key store and credential store service instances above:

<jpsContext name="default">
   <serviceInstanceRef ref="credstore"/>
   <serviceInstanceRef ref="policystore.xml"/>
   <serviceInstanceRef ref="audit"/>
   <serviceInstanceRef ref="idstore.ldap"/>
   <serviceInstanceRef ref="keystore"/>
 </jpsContext>




Note: If this configuration is already in place, you don’t need to restart your WLS domain. Otherwise, make the changes and restart your WLS domain.


Authorization


Portlets authorization is delegated to OPSS layer, i.e., the decision whether or not a portlet is available to a user is done against the OPSS policy store. Portlets are just one way of exposing local ADF task flows to remote applications. There’s a component called portlet brigde that is responsible for providing the glue between portlets and task flows. It basically makes possible exposing a task flow as a portlet.

In the example below, the policy store is file-based. Normally it is represented by system-jazn-data.xml located under $DOMAIN_HOME/config/fmwconfig folder. That can be easily changed to be and LDAP-based one.

Once the right task flow permissions are already in place in the policy store, there’s only one more permission that needs to be added, and it should be granted to the authenticated-role:

<permission> 
  <class>oracle.adf.controller.security.TaskFlowPermission</class>
   <name>/WEB-INF/adfp-portlet-bridge-container.xml#adfp-portlet-bridge-container</name>
   <actions>view</actions>
 </permission>


Here’s an example of a complete set of grants required for portlet authorization. In the example, the propagated user must be granted approle1 application role in order to view the portlet (assuming /WEB-INF/my-task-flow.xml#my-task-flow is taskflow exposed as a portlet).

<jazn-policy> 
  <grant>
     <grantee>
       <principals>
         <principal>
           <class>oracle.security.jps.internal.core.principals.JpsAuthenticatedRoleImpl</class>
           <name>authenticated-role</name>
         </principal>
       </principals>
     </grantee>
     <permissions>
       <permission>
         <class>oracle.adf.controller.security.TaskFlowPermission</class>
         <name>/WEB-INF/adfp-portlet-bridge-container.xml#adfp-portlet-bridge-container</name>
         <actions>view</actions>
       </permission>
     </permissions>
   </grant>
   <grant>
     <grantee>
       <principals>
         <principal>
           <class>oracle.security.jps.service.policystore.ApplicationRole</class>
           <name>approle1</name>
         </principal>
       </principals>
     </grantee>
     <permissions>
       <permission>
         <class>oracle.adf.controller.security.TaskFlowPermission</class>
         <name>/WEB-INF/my-task-flow.xml#my-task-flow</name>
         <actions>view</actions>
       </permission>
     </permissions>
   </grant>
 </jazn-policy>


Securing the Consumer


Securing a portlet consumer basically means enabling identity propagation. This is done during portlet producer registration in JDeveloper. When doing it, make sure you select/enter the right information in step 5 at WSRP Portlet Producer Registration wizard, as shown below:





Note: the configuration made here is written to connections.xml


Key store and Credential store configuration


Within the portlet consumer WLS domain, both key store and credential store can be the same used by the portlet producer. Have them at $DOMAIN_HOME/config/fmwconfig, as explained previously.

It is true we have a potential security issue here, since the certificates used by WSS are the same across WLS domains, and I’ll cover how to harden it in a next article.

Friday, June 18, 2010

Securing WebLogic WebServices with Oracle Entitlements Server and Oracle Web Services Manager

In a way this post is actuall an update on a few previous posts. The first is an update on the custom assertions for OWSM. I've been making some changes, and now the latest revision - revision 41 - is pretty solid. I've also published the JavaDoc and will continue to add more documentation as time permits. The second is on the OES and OWSM custom assertion from OOW2009. This is really the focus of this post...how to use this assertion to secure the services with OES and OWSM, but its is worth noting that the new version of this assertion, does use the OWSMAC framework. It definately greatly simplifed the development effort. For people who are interested in the new custom assertion, here's a link to the code.

Overview


This solution uses OES 10gR3CP4 protecting a JAX-WS webservice running inside of WLS 10.3.2 (11gR1SP). The domain is a JRF domain, which means that I can use OWSM to protect the web-service. I installed and configured the WLS SM and used the config tool to create a scoped application - scoped in this case means that I'm going to use the entitlementsadministration GUI - apps and orgs. I created in Eclipse the worlds simplest Web Service and deployed it. The webservice is nothing more than a "helloWorld". Once deployed, I could use the WLS admin console to define the policies for web-service.



Notice that I applied two policies...and authentication policy using oracle/wss10_saml_token_service_policy, and then the custom policy owsmac/owsmac_oes_AuthorizationPolicy. NOTE: The oracle/wss10_saml_token_service_policy has no message protection and has to be used in a production environment in conjunction with network or transport security. You'll see why when you look at the sample messages.

Why use OES Custom Assertion for OWSM?


Since the endpoint that we're protecing in a JAX-WS webservice, it is protected by the WLS SM by default. As we discussed previously, the container first checks the access controls policy for the URL and then the WebService. With OWSM in the picture, the sequence is:

  • URL Authorization Check - the resource is type=>url<, application=sample_service, contextPath=/sample_service, uri=/Service1Service, httpMethod=GET. You can do authorization only on the URL of the endpoint. Nothing in the body
  • Call OWSM - calls OES Custom Assertion - we'll discuss more in a second what you can do
  • WebServices Authorization Check - the resource is type=>webservices<, application=sample_service, contextPath=/sample_service, webService=Service1Port, method=hello, signature={java.lang.String}. Notice you have a lot more information like the Port and the operation as well as the signature of the method. Also, if you use attribute retriever in OES, you should be able to access a lot of the relevent appContext elements. The list is pretty expansive.


This means that without doing any custom work, you get two cracks at the request, and with OES in place and the WLS SM, there is quite a bit you can do. So why use the OES OWSM custom assertion? These are the additional use cases I've come up with:

  • You don't want to use OES to secure the WLS resources - when I did this solution last year at OOW, I installed the WLS SM, but also had the DefaultAuthorizer and used the OES Adjudicator to pull the two together. In this case, OES won't get called for URL and Webservices resource types
  • Policy Based access to SOAP:Body and SOAP:Env - If you use OOTB WLS SM to secure web-services, then you have to code a custom attribute retriever to operate on the SOAP Message. This means that if there are changes, then you'll need to re-code. The approach of having the XPath defined in the OES policy allows for changes to be made w/o coding
  • You want to update the message - In the updated version of the OES Custom Assertion, you can write policy that updates the values. What is the use case for this - outbound datamasking at the perimeter. For example, you can't control what the web service is going to return, but you want to make sure that the contents don't expose any PII. You can write a policy that will mask it.
  • You want to do authorization on the response - This is related to the previous point, but it allows you to do one more authorization check based upon the information in the response. This makes sense if the authorization cannot be determined until after invoking the operation. These really only makes sense in read operations, since the authorization failure is happending "presumably" after the transaction has been committed.

If you have any of the use cases above, read on and I'll explain a little more about the policies in OES.

Modelling the Policies for the OES Custom Assertion


The OES custom assertion works with 4 actions - request_lookup, execute_request, response_lookup, and execute_response. They are performed in that order. All of the magic happens in the responses (i.e. report_as function in the constraints). The values of the report_as in the lookup phases are assumed to be XPath queries that should be performed. The resulting values are then sent down in the execute phase. If the user is granted access, any report_as values returned that reference the attributes passed in are assumed to be updates which the OWSM assertion then applies.


For example, the constraint report_as("hello_out","*****") in the execute_response method will replace the value of the hello_out attribute (defined as the XPath in the response_lookup privilege) with ****.

I used the Vordel SOAP Box to test, including the creation of the unsigned SAML assertion required by the oracle/wss10_saml_token_service_policy. Basically, the policies allow any requests except if the hello in message is abc or if the response is Hello abcd - which means that the input of abcd will cause authorization to fail. Otherwise, the response is masked with ****. I also DENY any requests is the authentication method in the SAML assertion is urn:oasis:names:tc:SAML:1.0:am:unspecified. The XPath can be used on either the header of the body of the SOAP message.



I've included below the export from PolicyIX. It shows all of the authorization policies in detail.


Reference: Policy Export For Sample Application



<?xml version="1.0" encoding="UTF-8"?>
<xb:policy_propagation xmlns:xb="http://policypropagation.ales.com/xmlbean">
<xb:policy_propagation_data_v2>
<xb:scopes>
<xb:application_entry value="RootOrg!defaultOrg!sample_service" boundSSM="oes10gR3cp4wlsssm">
<xb:admin_roles>
<xb:admin_role_entry value="AppAdmin" isPrimary="true" description="Primary Application Admin Role of current Application">
<xb:admin_role_privileges>
<xb:admin_role_privilege_entry object="action" action="adminmanage"/>
<xb:admin_role_privilege_entry object="authorizationPolicy" action="adminmanage"/>
<xb:admin_role_privilege_entry object="authorizationPolicyReport" action="adminmanage"/>
<xb:admin_role_privilege_entry object="directory" action="adminview"/>
<xb:admin_role_privilege_entry object="extension" action="adminmanage"/>
<xb:admin_role_privilege_entry object="group" action="adminview"/>
<xb:admin_role_privilege_entry object="policyDistribution" action="adminmanage"/>
<xb:admin_role_privilege_entry object="policySimulator" action="adminmanage"/>
<xb:admin_role_privilege_entry object="resource" action="adminmanage"/>
<xb:admin_role_privilege_entry object="role" action="adminmanage"/>
<xb:admin_role_privilege_entry object="rolePolicy" action="adminmanage"/>
<xb:admin_role_privilege_entry object="rolePolicyReport" action="adminmanage"/>
<xb:admin_role_privilege_entry object="user" action="adminview"/>
</xb:admin_role_privileges>
</xb:admin_role_entry>
</xb:admin_roles>
<xb:resources>
<xb:resource_entry value="//resources/SOAPMessage" isVirtualResoureAllowed="true"/>
<xb:resource_entry value="//resources/SOAPMessage/Service1Service" isVirtualResoureAllowed="true"/>
<xb:resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port" isVirtualResoureAllowed="true"/>
<xb:resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port/hello" isVirtualResoureAllowed="true"/>
<xb:resource_entry value="//resources/url" isVirtualResoureAllowed="true"/>
<xb:resource_entry value="//resources/webservices" isVirtualResoureAllowed="true"/>
</xb:resources>
<xb:actions>
<xb:action_entry value="any"/>
<xb:action_entry value="execute_request"/>
<xb:action_entry value="execute_response"/>
<xb:action_entry value="request_lookup"/>
<xb:action_entry value="response_lookup"/>
</xb:actions>
<xb:dynamic_attributes>
<xb:dynamic_attribute_entry name="auth_method" type="string"/>
<xb:dynamic_attribute_entry name="hello_in" type="string"/>
<xb:dynamic_attribute_entry name="hello_out" type="string"/>
</xb:dynamic_attributes>
<xb:roles>
<xb:role_entry value="Anonymous" parent=""/>
</xb:roles>
<xb:policies>
<xb:membership_rule_entry>
<xb:policy_effect value="grant"/>
<xb:policy_roles>
<xb:policy_role_entry value="Anonymous"/>
</xb:policy_roles>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/url"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
</xb:membership_rule_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="deny"/>
<xb:policy_actions>
<xb:policy_action_entry value="execute_response"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port/hello"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
<xb:policy_constraint value="sys_defined ( hello_out ) and hello_out = &quot;Hello abcd&quot;"/>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="deny"/>
<xb:policy_actions>
<xb:policy_action_entry value="execute_request"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port/hello"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
<xb:policy_constraint value="sys_defined ( hello_in ) and hello_in = &quot;abc&quot;"/>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="deny"/>
<xb:policy_actions>
<xb:policy_action_entry value="execute_request"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port/hello"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
<xb:policy_constraint value="sys_defined ( auth_method ) and auth_method = &quot;urn:oasis:names:tc:SAML:1.0:am:unspecified&quot;"/>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="response_lookup"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port/hello"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
<xb:policy_constraint value="report_as ( &quot;hello_out&quot; , &quot;body:.//tns:helloResponse/return&quot; )"/>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="any"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/url"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="request_lookup"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port/hello"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
<xb:policy_constraint value="report_as ( &quot;hello_in&quot; , &quot;body:.//tns:hello/arg0&quot; )"/>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="request_lookup"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port/hello"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
<xb:policy_constraint value="report_as ( &quot;auth_method&quot; , &quot;header:.//saml:AuthenticationStatement/@AuthenticationMethod&quot; )"/>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="execute_request"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/SOAPMessage/Service1Service/Service1Port/hello"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
<xb:policy_constraint value="sys_defined ( hello_in ) and report_as ( &quot;hello_in&quot; , &quot;*****&quot; )"/>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="execute_response"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/SOAPMessage"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
</xb:authorization_policy_entry>
<xb:authorization_policy_entry>
<xb:policy_effect value="grant"/>
<xb:policy_actions>
<xb:policy_action_entry value="any"/>
</xb:policy_actions>
<xb:policy_resources>
<xb:policy_resource_entry value="//resources/webservices"/>
</xb:policy_resources>
<xb:policy_subjects>
<xb:policy_group_entry name="allusers" directory="defaultUsers" scope="RootOrg!defaultOrg"/>
</xb:policy_subjects>
</xb:authorization_policy_entry>
</xb:policies>
</xb:application_entry>
</xb:scopes>
<xb:security_configuration_data>
<xb:scms>
<xb:scm_entry name="adminconfig">
<xb:ssms>
<xb:ssm_entry name="asiadmin"/>
<xb:ssm_entry name="oes10gR3cp4wlsssm"/>
</xb:ssms>
</xb:scm_entry>
</xb:scms>
</xb:security_configuration_data>
</xb:policy_propagation_data_v2>
</xb:policy_propagation>