Saturday, August 10, 2013

OWSM SAML Policy Tests


This post assumes readers have the basic understanding of OWSM policy framework.
In this post, I’ll explore two examples of OWSM SAML policies. There are more than a dozen SAML policies that come with OWSM. One reason for the numerous SAML policies is OWSM provides two collections of SAML policies for WSS 1.0 (WS Security 1.0) and WSS 1.1. You can explore the difference between the WSS 1.0 and 1.1 that is outside the scope of this discussion. Suffice to say that when you pick policies on the client and service sides, you need to make sure that WSS versions match.

Nearly all of the OWSM SAML policies require some kind of encryption, except two wss10_saml(20)_token_service_policy. We will experiment with the non-encryption policy first, then we’ll discuss wss10_saml(20)_token_with_message_protection_service_policy.

For the experiments, I’ll create two local weblogic domains. “Domain 1” runs on port 7001. “Domain 2” runs on 7011. (Refer to previous post on how to createmultiple domains on your localhost).

For demonstration purpose, I use the following services:
  •         A JavaEE web service – deployed to domain 2 and locked by a SAML security service policy
  •         An OSB business service – on domain 1, it hooks up to the JavaEE web service. It has an attached SAML client policy.
  •         OSB proxy service – on domain 1, locked up by WS name token security, which routes to the business service.
You can find jar files for these services here: javaEESvc and osb-config-jar. You can deploy OSB config jar to domain1, javaEESvc jar to domain2.

The steps below illustrate the test scenarios:

1.      use soap UI or other client tool to call the proxy service with ws name token header (with user name, password)
2.      OSB/OWSM authenticates the user. In this case, it uses Weblogic server’s default security realm. So you need to have a proper user defined with the Weblogic server. Proxy is invoked if the user authentication is successful.
3.      OWSM create a SAML assertion based on the authentication of step #2 above.
4.      OSB attaches the SAML assertion (user name only, no password) to the outbound business service call to the JavaEE service
5.      Weblogic/OWSM authenticates the web service call with SAML header. In this case, the authentication only verifies the user name exists in the security realm. The password doesn’t matter. If the authentication is successful, the JavaEE service is invoked.
6.      JavaEE service response is routed back to SoapUI (via business service and proxy service)

WSDL
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://tempuri.org/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  name="samlPocService" targetNamespace="http://tempuri.org/">
  <types>
    <xs:schema xmlns:tns="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
      version="1.0" targetNamespace="http://tempuri.org/">
      <xs:element name="sayHello" type="tns:sayHello" />
      <xs:element name="sayHelloResponse" type="tns:sayHelloResponse" />
      <xs:complexType name="sayHello">
        <xs:sequence>
          <xs:element name="arg0" type="xs:string" minOccurs="0" />
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="sayHelloResponse">
        <xs:sequence>
          <xs:element name="return" type="xs:string" minOccurs="0" />
        </xs:sequence>
      </xs:complexType>
    </xs:schema>
  </types>
  <message name="sayHello">
    <part name="parameters" element="tns:sayHello" />
  </message>
  <message name="sayHelloResponse">
    <part name="parameters" element="tns:sayHelloResponse" />
  </message>
  <portType name="samlPoc">
    <operation name="sayHello">
      <input message="tns:sayHello" />
      <output message="tns:sayHelloResponse" />
    </operation>
  </portType>
  <binding name="samlPocPortBinding" type="tns:samlPoc">
    <soap:binding style="document"
      transport="http://schemas.xmlsoap.org/soap/http" />
    <operation name="sayHello">
      <soap:operation soapAction="" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
  </binding>
  <service name="samlPocService">
    <port name="samlPocPort" binding="tns:samlPocPortBinding">
      <soap:address location="http://localhost:7011/javaeesvc/samlPocPort" />
    </port>
  </service>
</definitions>

JavaEE service:
It’s a no thrill simple hello world java function:
package javaeesvc;
import javax.jws.WebService;
@WebService(targetNamespace = "http://tempuri.org/")
public class samlPoc {
    public samlPoc() { super(); }   
    public String sayHello(String s) {
        System.out.println("######Hello " + s + ", this is saml poc");
        return "######Hello " + s + ", this is saml poc";
    }
}

JavaEE service is deployed to domain 2:

Biz Service Configuration, no policy attached yet:

Proxy service:

Then attach WS name token policy to the proxy

Experiment 1SAML Policy without Encryption
Testing “oracle/wss10_saml20_token_client_policy
In this experiment, we’ll demonstrate the SAML policy without encryption (plain SAML policies). Without encryption, there will be no additional server configurations involved.  You can merely attach the corresponding SAML policies on both the JavaEE service and OSB business service, that’s it.
In the next section we’ll see when message protection (encryption) is involved, it becomes a different ball game. You need to configure quite a few things before SAML policies can work.
Let’s do the simple one first.

Attach SAML policy to JavaEE service:

Under “Web Service Endpoints” tab, click on endpoint name “samlPocPort”:

Click on “Attach/Detach” icon:

Select “wss10_saml20_token_service_policy” then press “Attach”
Attach SAML policy to OSB business service:


SOAP UI client test:

What has happened?
Part 1: Soap UI calls Proxy Service with WSS name token
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:tem="http://tempuri.org/">
  <soapenv:Header>
    <wsse:Security soapenv:mustUnderstand="1"
      xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken wsu:Id="UsernameToken-2"
        xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <wsse:Username>weblogic</wsse:Username>
        <wsse:Password
          Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">welcome1
</wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <tem:sayHello>
      <arg0>soupui</arg0>
    </tem:sayHello>
  </soapenv:Body>
</soapenv:Envelope>

Part 2: Business Service Invokes JavaEE service with SAML token

Internally, OWSM first authenticates user “weblogic” with password, then it generate SAML token (assertion). OSB business service attaches the SAML assertion in the header. Business service calls JavaEE service with the SAML assertion.

<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <wsse:Security soapenv:mustUnderstand="1"
    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <saml:Assertion MajorVersion="1" MinorVersion="1"
      AssertionID="SAML-xnTew5qgIPuu2kkZGcVqhQ22" IssueInstant="2013-07-29T22:26:43Z"
      Issuer="www.oracle.com" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
      <saml:Conditions NotBefore="2013-07-29T22:26:43Z"
        NotOnOrAfter="2013-07-29T22:31:43Z" />
      <saml:AuthenticationStatement
        AuthenticationInstant="2013-07-29T22:26:43Z" AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password">
        <saml:Subject>
          <saml:NameIdentifier
            Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">weblogic</saml:NameIdentifier>
          <saml:SubjectConfirmation>
            <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:sender-vouches
            </saml:ConfirmationMethod>
          </saml:SubjectConfirmation>
        </saml:Subject>
      </saml:AuthenticationStatement>
    </saml:Assertion>
  </wsse:Security>
</soap:Header>
<soapenv:Body>
  <urn:sayHello xmlns:urn="urn:examples:helloservice">
    <firstName xsi:type="xs:string" .>test</firstName>
  </urn:sayHello>
</soapenv:Body>
</soapenv:Envelope>

In case anyone wonders how I captured the SAML payload, I used TCPMon tool to trap the business outbound call. With OSB 11.1.1.6 version, you can also see similar payload using the OSB business service test console.

The main take away in this experiment is that OSB business service calls JavaEE with SAML assertion and user name, but no password is provided. JavaEE service is configured with a plain SAML token policy that blindly trusts anyone with a well formatted SAML token, as long as the user name exists in “Domain 2”. The web service request will be accepted.

Well, if you find this behavior questionable, then you are correct. Oracle doesn’t recommend using this “plain” dumb SAML policy. In next section, we’ll test with a SAML policy that requires “message protection”. i.e. payload will be encrypted with key (certificate), so the service requests (from the client) can be trusted with assurance by the web service (server).

SAML policy with message protection (encryption)
We’ll use wss11_saml20_token_with_message_protection_client_policy for our experiment.
This SAML policy requires the payload be encrypted (message protection). As far as OWSM policy configurations are concerned, there is not much difference between this policy and the plain SAML policy we discussed earlier. However, there are major configuration steps involved to setup the domains for the encryption/decryption to work.

Setting up certificate trusts between the two domains

If you never worked with OWSM key stores, it can be a daunting task to go through the steps in this section for the first time. You can reference this previous post to get familiar with the OWSM key stores.

By default, the key store is here {domain_home}/config/fmwconfig/default-keystore.jks. The actual key store location is specified in this file {domain_home}/config/fmwconfig/jps-config.xml

In order for message encryption/decryption to work, the two domains need to exchange their keys. “Domain 2” will import the public key of “Domain 1”. So when “domain 1” sends an encrypted message (encrypted with Domain 1’s private key), “domain 2” will be able to decrypt the message with “domain 1’s” public key. Vice versa, “domain 1” needs to import the public key of “domain 1”.

Here are the keytool commands to generate keys for each domain, and to import the public keys to each other’s key store.
 
  #### generate default keypair for domain1, valid for 10 years
  keytool -genkeypair -alias d1key  -keyalg RSA -keypass d1pass -keystore domain1-keystore.jks  -storepass welcome1 -validity 3650
  #### generate default keys for domain2, valid for 10 years
  keytool -genkeypair -alias d2key  -keyalg RSA -keypass d2pass -keystore domain2-keystore.jks  -storepass welcome2 -validity 3650

  #### list key stores
  keytool -list -v -storepass welcome1 -keystore domain1-keystore.jks
  keytool -list -v -storepass welcome2 -keystore domain2-keystore.jks

  #### export domain1 public key
  keytool -exportcert -alias d1key -storepass welcome1 -keystore domain1-keystore.jks -file domain1-pubkey.cer
  #### export domain1 public key
  keytool -exportcert -alias d2key -storepass welcome2 -keystore domain2-keystore.jks -file domain2-pubkey.cer

  #### import domain2 public key into domain 1 as "d2impkey"
  keytool -importcert -alias d2impkey -storepass welcome1 -keystore domain1-keystore.jks  -file domain2-pubkey.cer
  #### import domain1 public key into domain 2 as "d1impkey"
  keytool -importcert -alias d1impkey -storepass welcome2 -keystore domain2-keystore.jks  -file domain1-pubkey.cer

  ### if you need to remove a key
  keytool -delete  -alias yourkey  -keystore keystore.jks -storepass welcome1

The screen below shows how to configure the key store for domain 1. Pay close attention to each field in this screenshot and notice how the field values correspond to the values we used in the key commands above. You will need to bounce the servers if you change key store files. Remember the default is default-keystore.jks, which is specified in jps-config.xml.

This screen shows how to configure the key store for domain 1.

Once the OWSM key stores are configured for both domains. You will detach the plain SAML policies from javaEESvc on domain 2 and the business service on domain 1. Then reattach wss10_saml20_token_with_message_protection_service_policy to JavaEESvc, and wss10_saml20_token_with_message_protection_client_policy to the OSB business service.

You also need to configure the security property on the business service as shown below.The recipient alias is the domain 2 public key. We imported into domain 1 with the alias "d2impkey". This key will be actually used to decrypt the response message from domain 2, which is encrypted with domain's private key.

This screen shows how to configure the business service “security” tab:

You will need to configure domain 2 OWSM key store similarly.

Test in OSB console

If everything is configured correctly, you can test the business service from the OSB console (works for OSB 11.1.1.6, may not work for earlier version)
The test console shows data is encrypted:

Test with SoapUI
The SoapUI test will show the same thing either SAML policy is encrypted or not encrypted.
Final notes:
I used OSB and JavaEE service for the experiments. You can also use SOA composite to test the SAML policies. It is recommended to use JDeveloper to attach policies to SOA composite, but you can also use "em" console to the same.

Friday, August 9, 2013

Weblogic log files and log4j tricks

I have relied on weblogic log files for trouble shooting for many years, but never given a serious thought where they are configured and how to customize them. Well, I still don't know the whole story, but I managed to learn a couple of interesting things lately.

1. By default, WebLogic uses Java logging API which is based on log4j, but it is not exactly log4j. Therefore, if you install an out of box domain, there is no direct reference to log4j.

2. Your application can choose to use log4j for logging, then you can control how things are logged and where to log to etc.

3. If you want to system to dynamically pick up log4j.xml changes during run time, you can use https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/DOMConfigurator.html I'll show a full example below.

4. We also need to send certain log messages to sensage, which monitors the syslog. What we need was using log4j "syslogger" to direct log msgs to syslog, then we configured the /etc/syslog.conf to forward the messages to senage like:
# Sensage Configuration
*.notice;mail.none;user.none;auth.none;mark.none;kern.none     @sensage.acme.com
mail.crit;user.crit                                    @sensage.acme.com
kern.info                                              @sensage.acme.com
auth.info                                              @sensage.acme.com
user.info                                               @sensage.acme.com

However, in order to send message to syslog file, we had to modify "syslogd" start up script and add "-r" parameter.

Now, for the sample log4j and DOMConfigurator example. I grabbed some sample file online and modified it with the configurator:

import org.apache.log4j.Logger;
import java.io.*;
import java.sql.SQLException;
import java.util.*;
import org.apache.log4j.xml.DOMConfigurator;
public class logExample{
  static Logger log = Logger.getLogger(logExample.class.getName());
  public static void main(String[] args)
                throws IOException,SQLException{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String file = System.getProperty("Log4jConfigFile");
System.out.println(String.format("Log4jConfigFile=%s",file));
DOMConfigurator.configureAndWatch(file, 3000);

     while (true) {
     log.debug("Hello this is an debug message");
     log.info("Hello this is an info message");
     System.out.println("modify your log4j.xml, then hit enter to continue, monitor the changes take effect.");

String x = null;
try {
x = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
  }
  }
}

Here is my local compile and run script:

javac -cp .;C:\Oracle\Middleware\Oracle_OSB1\lib\external\log4j_1.2.8.jar -d . logExample.java

java -cp .;C:\Oracle\Middleware\Oracle_OSB1\lib\external\log4j_1.2.8.jar -DLog4jConfigFile="c:\proj\poc\log4j\config\log4j.xml" logExample

You can use your own log4j.xml or grab a sample from here http://stackoverflow.com/questions/2619160/sample-xml-configuration-for-log4j-have-a-main-java-application-and-want-to-w

Finally, to integrate this sample into your web application, you need to find your initialization function, such as servlet context initializer, and insert the DCOMConfigurator over there. Of course, remember to put in -DLog4jConfigFile in your server startup script if your server is started with the script, or put it in the weblogic console server startup argument field if you use nodeManager to start your server(s).

Wednesday, July 24, 2013

Managing WSM Key Store and Credential Store

I am working on a series of OWSM related posts. This one talks about the OWSM key store and credential store. Specifically, this post mostly talks about the physical aspects of the stores: where the files are, how to use UI to find and manage them. I will cover how to generate, export/import, work with aliases of keys and certs etc.

OWSM's key store and credential store are always mysterious to me. As i get better understanding them, i'm recording what i have learned.

By default, OWSM key store (also referred to as OWSM agent's key store) is here: "<domain home>/config/fmwconfig/default-keystore.jks". The actual location can be found in this file: <domain home>/config/fmwconfig/jps-config.xml.

You can configure the keystore from the "em" console, expand "Weblogic Domain" on the left, then right click your domain, select "security", then "security provider configuration", and on that page, click "configure" button under the "Keystore" section.


There are many key stores in SOA suite. This one is for OWSM. It uses the certificates in the store for encryption/decryption when processing OWSM security policies. Do not confuse this keystore with the ones for Weblogic server. Those are under Weblogic console->"Environment"->"servers"-> "your server", then under the "Keystores" tab. You will see at least two "identity" and "trust" stores. Those stores contain certificates mostly for the server SSL among other things.

Based on my observation, when a domain is first created, although "em" console, security provider store configuration page shows the file is "./default-keystore.jks", that file doesn't even exist. You need to use java "keytool" command to generate the file. For example,

keytool -genkeypair -alias test_domain_alias  -keyalg RSA -keypass welcome2 -keystore test_domain.jks  -storepass welcome1 -validity 3650

it will prompt you for your name, location etc, then it will generate a test_domain.jks file. This file contains public/private keys that can be used for encryption/decryption. The key alias is "test_domain_alias" (pay attention to this alias, as it is related to the credential stores we'll discuss). The keypass (for test_domain_alias) is "welcome2". The keystore password is "welcome1", which is used to access the store file. 3650 will make key valid for 10 years!

For example, keytool -list -v -storepass welcome1 -keystore test_domain.jks, will display the certificate contained in the key store.

Once the test_domain.jks file is created, you can put it in the default place (or other place as you choose) under "${your domain home}/config/fmwconfig", then you can configure the store as shown below:

Please note that we used test_domain.jks as the keystore path, the password is "welcome1". For Signature and Encryption key alias, we entered "test_domain_alias". We use "welcome2" for both passwords. Please be careful that the editor doesn't validate anything you enter here other than "password and confirm password" are the same. You can enter wrong jks name, wrong passwords, and wrong alias, it would just same them as-is. I guess you'll find out things are not working at run time if you saved incorrect information.

So where is all the information you entered stored? They all went to the credential store. I believe the physical file is cwallet.sso, it's a binary file, you can't see much of it.

What is the credential store then? Well, all I know is that it stores some "credentials", such as user name and password. Each entry has a key. The overall credential store is organized by "maps".

You access the credential store as shown in the first picture above. Here is a sample view:


If you already configured the key store as above, you will see at least one "oracle.wsm.security" map, under the map, you'll see at least 3 keys: sign-csf-key, enc-csf-key, and keystore-csf-key. These are populated when you configured the keystore. If you change them here (for example, edit sign-csf-key, try change the value from "test_domain_alias" to something else), and you go back to the keystore configuration, you'll notice it is updated over there as well. Again, it's garbage in garbage out, whatever you enter, it would be saved as-is. It's your responsibility to enter the info correctly. If you go to the extreme and delete the oralce.wsm.security map here, then go the keystore configure, you'll see the data is wiped out over there and you'll have to reconfigure the keystore.

BTW, how does OWSM know to use these 3 special keys to access the store. If you open fmwconfig/jps-config.xml, you'll find them there.

That's quite enough about these 3 keys! Let's look at the other key i created manually, "basic.credentials". Under the key i have user name "weblogic", password "welcome1". This is typically used like a "service account", if a BPEL or OSB business process need to invoke another service and provide a service account, you can simply reference "basic.credentials" under oracle.wsm.security. It just so happened that several OWSM security policy reference this particular key "basic.credentials" by default. For example,if you have oracle/wss_username_token_service_policy on a proxy and run the test console on the proxy, you'll see it uses "basic.credentials" by default.


I think oracle/wss_username_token_client_policy does the same, at least for SOA client, it uses "basic.credentials" for user/pass if you don't explicitly specify them in your code.If your key name is something different, then your process needs to reference the other name.

OWSM Note - When import certs, remember to bring in the chain if neccessary

OWSM has so many aspects, it's impossible to discuss OWSM in a few posts. So I'm splitting things up and add specific notes as I go.
Context: I am running test with SAML policy with msg protection on two hosts. The client server has a key store contains its private key “foo”, and server contains private key “bar”.
BTW, OWSM key store, by default, is under <domain home>/config/fmwconfig/default-keystore.jks. The actual location can be found in <domain home>/config/fmwconfig/jps-config.xml.
After I exchanged the certs of the two servers, the picture looks like:
·        Client key store: client private key “foo”, imported server cert “bar
·        Server key store: server private key “bar”, “bar CA”, imported client cert “foo”
when I ran the test, the client side log file produces this:
<Error> <oracle.wsm.resources.security> <WSM-00138>
The path to the certificate is invalid due to Path does not chain with any of the trust anchors .
Validation failed for certificate "CN=bar, OU=Application Development & Architecture, O=ACME Inc., L=Phoenix, ST=Arizona, C=US" Issuer of certificate is "C=US, ST=Arizona, L=Phoenix, O="ACMEInc.", OU=Application Development & Architecture, CN=bar CACertificates in cert path used for validation are  CN=foo, OU=Application Development & Architecture, O=ACME Inc., L=Phoenix, ST=Arizona, C=US.
Looking at key store picture above, it appears that I have exchanged keys “foo” and “bar” properly. So we do I get cert errors? After closely examine the errors messages and the key stores, I found out the problem is the cert I imported into the client store was not a root CA cert. I need to import the server cert as well as it's CA. Here is the new picture:
·        Client key store: client private key “foo”, imported server cert “bar”, imported server “bar CA” cert
·        Server key store: server private key “bar”, “bar CA”, imported client cert “foo
The revealing parts of the error message are “path to the certificate” and “chain”. That leads me to examine the key stores. I noticed that server cert “bar” shows “Certificate chain length: 2”, and finally I realized importing “bar” alone to the client store without its CA is causing the problem. The solution is to import “bar CA” into the client store as well. That took care of the problem.
Let me list all the keytool commands I use to diagnose and resolve the issue. Keytool is under Java bin directory.
Generate key pair:
keytool -genkeypair -alias foo  -keyalg RSA -keypass welcome1 -keystore client-keystore.jks  -storepass welcome1 -validity 3650
I am getting into the habit to set validity to 10 years (3650 days). I ran into too many times of using the default of 1 year, then my test server stops running after a year, and took me long time to find out why.
Keep in mind, alias “foo” is only a local alias in this key store, when you export “foo”, then re-import it into another store, you can give it a different alias, say for example, call it “client_foo”.
Examine the key store:
keytool -list -v -storepass welcome1 -keystore client-keystore.jks
When I ran this command on the server key store, I saw the server cert has a key length of 2. I also found the CA for the server cert in the same key store.
Export cert:
keytool -exportcert -alias foo -keystore client-keystore.jks -storepass welcome1 -file client_foo.cer
Import cert:
keytool -importcert -alias client_foo -keystore server_keystore.jks -storepass serverKeyPass -file client_foo.cert
Import CA cert:
keytool -importcert -trustcacerts -alias "bar CA" -keystore client-keystore.jks -storepass welcome1 -file bar-ca.cer
If the cert you import is chained to a CA (or more), you need to import CA certs.