Sunday, July 27, 2014

OPSS Trust - Identity Propagation over HTTP/S

When we are interacting with external systems over HTTP/S protocol, some times we need to exchange the identities (valid credentials) over protocol to authorize the requested action. Using OPSS trust between two different Weblogic domains, we can propagate identities across HTTP/S enabled applications by providing and validating tokens. The OPSS trust service uses identity asserter available in Weblogic.

Pre-requisites: Same identity should exists on both the domains.

OPSS Trust configuration steps:

Source Domain Configuration:

1. Create a certificate based on source domain and add it to Key Store
>keytool -genkeypair -alias <domainName> -keypass welcome1-keyalg RSA -dname "<domainName>" -keystore default-keystore.jks -storepass welcome1

2.Export source domain certificate
>keytool -export -alias <domainName> -file <domainName>.cer -keystore default-keystore.jks -storepass welcome1

3. Copy the generated default-keystore.jks to ${domain.home}/config/fmwconfig

4. Check the keystore service configured in the file jps-config.xml points to the generated default-keystore.jks

<serviceInstance name="keystore" provider="keystore.provider" location="./default-keystore.jks">
            <description>Default JPS Keystore Service</description>
            <property name="keystore.provider.type" value="file"/>
            <property name="keystore.file.path" value="./"/>
            <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>

5. Create a map/key pair used to open the keystore and another map/key pair used to issue tokens. The following commands illustrate these operations using the OPSS script createCred:
  • connect("weblogic","welcome1","t3://localhost:7001")
  • createCred(map="oracle.wsm.security", key="keystore-csf-key", user="keystore", password="welcome1", desc="Keystore Password")
  • createCred(map="oracle.wsm.security", key="sign-csf-key", user="orakey", password="welcome1", desc="Signing key")
6. Add a grant like the following to the policy store, which allows the client application to use the trust service API:

<grant>
  <grantee>
    <codesource>    
<url>file:${oracle.deployed.app.dir}/<MyApp>${oracle.deployed.app.ext}</url>
    </codesource>
  </grantee>
  <permissions>
    <permission>          
<class>oracle.security.jps.service.trust.TrustServiceAccessPermission</class>
        <name>appId=*</name>
        <actions>issue</actions>
     </permission>
    </permissions>
</grant>

7. Weblogic Identity Asserter Configuration:
Copy the WebLogic identity asserter JAR jps-wls-trustprovider.jar to the location ${domain.home}/lib/mbeantypes,as illustrated by the following command, and then restart the WebLogic Server.

>cp ${common.components.home}/modules/oracle.jps_11.1.1/jps-wls-trustprovider.jar ${domain.home}/lib/mbeantypes

8. Reboot Weblogic server and Login to WLS console.

Navigate to Security Settings > Security Realms > myrealm > Providers Tab > Authentication, and click New to open the Create a New Authentication Provider dialog.

In that dialog, enter TrustServiceIdentityAsserter in the name box, and select TrustServiceIdentityAsserter from the pull-down in the type box; then click OK.

9. Trust service configuration:
Open $DOMAIN_HOME\config\fmwconfig\jps-config.xml

        <propertySet name="trust.provider.embedded">
            <property name="trust.aliasName" value="<domainName>"/>
            <property name="trust.issuerName" value="<domainName>"/>
            <property name="trust.clockSkew" value="60"/>
            <property name="trust.token.includeCertificate" value="false"/>
            <property name="trust.token.validityPeriod" value="1800"/>
            <property name="trust.provider.className" value="oracle.security.jps.internal.trust.provider.embedded.EmbeddedProviderImpl"/>
        </propertySet>

10. Include $WLS_HOME\oracle_common\modules\oracle.jps_11.1.1\jps-api.jar in application classpath and add following code to application generate token.

public static final String AUTH_TYPE_NAME = "OIT";
String user = "weblogic"; 
URL url = "http://host:port/destinationApp"; 

JpsContextFactory ctxFactory = JpsContextFactory.getContextFactory();
JpsContext jpsCtx = ctxFactory.getContext();
final TrustService trustService = jpsCtx.getServiceInstance(TrustService.class);
final TokenManager tokenMgr = trustService.getTokenManager();
final TokenContext ctx = tokenMgr.createTokenContext(
    TokenConfiguration.PROTOCOL_EMBEDDED);
UsernameToken ut = WSSTokenUtils.createUsernameToken("wsuid", user);
GenericToken gtok = new GenericToken(ut);
ctx.setSecurityToken(gtok);
ctx.setTokenType(SAML2URI.ns_saml);
Map<String, Object> ctxProperties = ctx.getOtherProperties();
ctxProperties.put(TokenConstants.CONFIRMATION_METHOD,
    SAML2URI.confirmation_method_bearer);
AccessController.doPrivileged(new PrivilegedAction<String>() {
    public String run() {
        try {
            tokenMgr.issueToken(ctx);
        } catch (Exception e) {        
            e.printStackTrace();
        }
        return null;
    }
});
Token token = ctx.getSecurityToken();
String b64Tok = TokenUtil.encodeToken(token);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.setReadTimeout(10000);
connection.setRequestProperty("Authorization", AUTH_TYPE_NAME + " " + b64Tok);
connection.connect();
BufferedReader rd = new BufferedReader(new InputStreamReader(
    connection.getInputStream()));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = rd.readLine()) != null) {
    sb.append(line);
}
connection.disconnect();
System.out.println(sb.toString());

Destination Domain Configuration:

Note: Here I assumed that, destination domain already have default-keystore configured. Else repeat source domain steps to configure default-keystore and credential store.

11. Repeat step 7 & 8 on destination domain

12. Import source domain certificate into destination domain key store
>keytool -importcert -alias <domainName>-file <domainName>.cer -keystore $DOMAIN_NAME/config/fmwconfig/default-keystore.jks -storepass welome1

13.  Repeat previous Step 9 on destination domain

Now test the integration by accessing source application and make a request to destination.

Credential Store Access from BPEL

Some times we need to pass restricted information like credentials in web service request payload to end point systems. In this case, we have to manage the credentials in source system to pass it in run time. As a best practice, we can store this information in Credential Key store or in database(encrypted). Here I will share the steps to manage it in Credential Key Store.

Create Credential Map and Key in Key Store:
1. Login to Weblogic EM. Go to WLS domain --> Security --> Credentials.
2. Click Create Map. Provide map name.
For Ex: kakarla_map
3. Select newly created map and click on Create Key. Provide Key name and credentials info.
For Ex: Key --> kakarla_key
4. Click on Ok to save changes.

Add jps-manifest.jar file path to BpelClassPath:
1. Login to Weblogic EM. Go to soa-infra --> SOA Administration --> BPEL Engine Properties --> More BPEL Configuration Properties.
2. Add jps-manifest.jar file path to BpelClassPath attribute. Typically jps-manifest.jar file will be located in $WLS_HOME/oracle_common/modules/oracle.jps_11.1.1/jps-manifest.jar
3. Click on apply to save changes

Update system-jazn-data.xml (In case OPSS store refers to file system):
1. Login to host machine and go to domains home.
2. Change to $DOMAIN_HOME/config/fmwconfig. Edit system-jazn-data.xml
3. Add following permission to <jazn-policy> grant section
Note: If you want to restrict the access to specific map & key, replace * with map/key name.
    <permission>
          <class>oracle.security.jps.service.credstore.CredentialAccessPermission</class>
          <name>context=SYSTEM,mapName=kakarla_map,keyName=*</name>
          <actions>read</actions>
   </permission>
4. Reboot weblogic admin & managed servers.

Add Java Embedded activity to BPEL process to retrieve CSF key:
1. Add jps-manifest.jar file from JDeveloper installation to SOA project.
2. Add following imports to BPEL component.
    <bpelx:exec import="oracle.security.jps.service.credstore.*"/>
    <bpelx:exec import="oracle.security.jps.*"/>
    <bpelx:exec import="java.security.PrivilegedAction"/>
    <bpelx:exec import="java.security.AccessController"/>
3. Add following code snippet to Java Embedded activity to access key store.
Note: CSF Map & Key (created in prev steps) hard coded here for example,
try  
{  
    String csfUserName = "";  
    String csfPassword = "";  
    JpsContext ctx = JpsContextFactory.getContextFactory().getContext();  
    final CredentialStore cs = (CredentialStore)ctx.getServiceInstance(CredentialStore.class);  
    CredentialMap cmap = cs.getCredentialMap("kakarla_map");  
    Credential cred = cmap.getCredential("kakarla_key");  
    if ((cred instanceof PasswordCredential)) {  
        PasswordCredential pcred = (PasswordCredential)cred;  
        char[] p = pcred.getPassword();  
        csfUserName = pcred.getName();  
        csfPassword = new String(p);  
        addAuditTrailEntry(csfUserName);  
        addAuditTrailEntry(csfPassword);          
   }  
} catch (Exception e) {  
   addAuditTrailEntry(e.getMessage());  
}
4. Use setVariableData and getVariableData methods in Java Embedded to set and get values in BPEL variables.
5. Deploy BPEL process to SOA server and test.
6. If you are getting any BPEL compilation errors, check BpelClassPath value set in previous steps.