Search this blog ...

Wednesday, June 27, 2012

Decrypt / Dump contents of CWALLET.SSO (Oracle file based credential store)

When using a file-based credential store with Oracle, credentials ultimately get stored in a wallet file (cwallet.sso)

Very little if any info exists on how to dump the contents of the wallet.  At best, most people leverage the trusty orapki command to get an overview of what’s inside as far as the maps and keys, but actual password information is never divulged.

For example:

$MW_HOME/oracle_common/bin/orapki wallet display -wallet ~/cwallet.sso
Oracle PKI Tool : Version 11.1.1.6.0
Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.

Requested Certificates:
User Certificates:
Oracle Secret Store entries:
dip@#3#@cn=odisrv
ODSMMap@#3#@ODSMKey.Wallet
oracle.wsm.security@#3#@enc-csf-key
oracle.wsm.security@#3#@keystore-csf-key
oracle.wsm.security@#3#@sign-csf-key
Trusted Certificates:
Subject:        OU=Class 1 Public Primary Certification Authority,O=VeriSign\, Inc.,C=US
Subject:        OU=Secure Server Certification Authority,O=RSA Data Security\, Inc.,C=US
Subject:        CN=Entrust.net Secure Server Certification Authority,OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS incorp. by ref. (limits liab.),O=Entrust.net,C=US
Subject:        CN=GTE CyberTrust Global Root,OU=GTE CyberTrust Solutions\, Inc.,O=GTE Corporation,C=US
Subject:        OU=Class 3 Public Primary Certification Authority,O=VeriSign\, Inc.,C=US
Subject:        CN=Entrust.net Secure Server Certification Authority,OU=(c) 2000 Entrust.net Limited,OU=www.entrust.net/SSL_CPS incorp. by ref. (limits liab.),O=Entrust.net
Subject:        OU=Class 2 Public Primary Certification Authority,O=VeriSign\, Inc.,C=US
Subject:        CN=Entrust.net Certification Authority (2048),OU=(c) 1999 Entrust.net Limited,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),O=Entrust.net

Until now that is :)

Note - wallet and jps-config-dump file must reside in same location for this sample code to function!!  In the example below, choose either the domain wallet, or the bootstrap wallet.

PATH_TO_WALLET="$DOMAIN_HOME/config/fmwconfig/bootstrap/cwallet.sso"
PATH_TO_JPS="$DOMAIN_HOME/config/fmwconfig/bootstrap/jps-config-dump.xml"

or …

PATH_TO_WALLET="$DOMAIN_HOME/config/fmwconfig/cwallet.sso"
PATH_TO_JPS="$DOMAIN_HOME/config/fmwconfig/jps-config-dump.xml"

cat > "${PATH_TO_JPS}" <<EOF
<?xml version="1.0" encoding="UTF-8" standalone='yes'?>
<jpsConfig xmlns="http://xmlns.oracle.com/oracleas/schema/11/jps-config-11_1.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/oracleas/schema/11/jps-config-11_1.xsd
jps-config-11_1.xsd" schema-major-version="11" schema-minor-version="1">
   <serviceProviders>
      <serviceProvider type="CREDENTIAL_STORE" name="credstoressp" class="oracle.security.jps.internal.credstore.ssp.SspCredentialStoreProvider">
         <description>Credential Store Service Provider</description>
      </serviceProvider>
   </serviceProviders>
   <serviceInstances>
      <serviceInstance provider="credstoressp" name="credstore">
         <property value="file:${PATH_TO_WALLET}" name="location"/>
      </serviceInstance>
   </serviceInstances>
  <jpsContexts default="test">
    <jpsContext name="test">
      <serviceInstanceRef ref="credstore"/>
    </jpsContext>
  </jpsContexts>
</jpsConfig>
EOF

cat > /tmp/DumpWallet.java <<EOF
import java.io.File;

import java.util.Hashtable;

import oracle.security.jps.JpsContext;
import oracle.security.jps.JpsContextFactory;

import oracle.security.jps.service.credstore.Credential;
import oracle.security.jps.service.credstore.CredentialFactory;
import oracle.security.jps.service.credstore.CredentialMap;
import oracle.security.jps.service.credstore.CredentialStore;
import oracle.security.jps.service.credstore.GenericCredential;
import oracle.security.jps.service.credstore.PasswordCredential;

public class DumpWallet
{
  private static final byte[] HEX = new byte[] {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 
 
  public static void main(String[] args)
  {
    try
    {
      System.setProperty("oracle.security.jps.config", (args.length > 0) ? args[0] : "jps-config.xml");
      // System.setProperty("java.security.debug", "all");
      JpsContextFactory ctxFactory  = JpsContextFactory.getContextFactory();
      JpsContext ctx = ctxFactory.getContext();
      CredentialStore store = ctx.getServiceInstance(CredentialStore.class);
      listAll(store);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
 
  private static void listAll(CredentialStore store) throws Exception
  {
    System.out.println("Dumping store contents ...");
    for (String map : store.getMapNames())
    {
      System.out.println("\n" + "### Map: " + map);
      CredentialMap credMap = store.getCredentialMap(map);
      if (credMap != null)
      {
        int i = 1;
        for (String key : credMap.keySet())
        {
          System.out.println(" " + i++ + ". + Key: " + key);
          
          Credential cred = credMap.getCredential(key);
          System.out.println("  class = " + cred.getClass().getName());
          System.out.println("  desc  = " + cred.getDescription());

          if (cred instanceof PasswordCredential)
          {
            PasswordCredential pc = (PasswordCredential)cred;
            System.out.println("  name  = " + pc.getName());
            System.out.println("  pass  = " + new String(pc.getPassword()));
            System.out.println("  expires   = " + pc.getExpiryTime());
          }
          else if (cred instanceof GenericCredential)
          {
            GenericCredential gc = (GenericCredential)cred;
            Object c = gc.getCredential();
            String type = (! c.getClass().isArray())
              ? c.getClass().getName()
              : ("Array of " + c.getClass().getComponentType().getName());

            System.out.println("  type  = " + type);
            if (c instanceof String)
            {
              System.out.println("  cred  = " + c);
            }
            else if ( c instanceof Hashtable)
            {
              Hashtable ht = (Hashtable)c;
              for (Object htkey : ht.keySet())
              {
                Object htVal = ht.get(htkey);
                if (htVal instanceof char[])
                {
                  System.out.println("  cred  = (" + htkey + ", " + new String((char[])htVal) + ")");
                }
                else
                {
                  System.out.println("  cred  = (" + htkey + ", " + htVal + ")");
                }
              }
            }
            else if (c instanceof javax.crypto.spec.SecretKeySpec)
            {
              javax.crypto.spec.SecretKeySpec secret = (javax.crypto.spec.SecretKeySpec) c;
              System.out.println("  algorith  = " + secret.getAlgorithm());
              System.out.println("  format  = " + secret.getFormat());
              System.out.println("  key material as hex = " + bytesAsHex(secret.getEncoded()));
            }
            else if (c instanceof byte[])
            {
              System.out.println("  byte array as hex = " + bytesAsHex((byte[])c));
            }
            System.out.println("  expires   = " + gc.getExpiryTime());
          }
          else
          {
            System.out.println("  toStr = " + cred.toString());
          }
        }
      }
    }
  }

  public static final String bytesAsHex(byte[] bytes)
  {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < bytes.length; i++)
    {
      sb.append((char)(HEX[(bytes[i] & 0x00F0) >> 4])).append((char)(HEX[bytes[i] & 0x000F])).append(" ");
    }
    return sb.toString();
  }
}
EOF

CP=/tmp
CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_11.1.1/jps-api.jar
CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_11.1.1/jps-common.jar
CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_11.1.1/jps-internal.jar
CP=$CP:$MW_HOME/oracle_common/modules/oracle.idm_11.1.1/identitystore.jar
CP=$CP:$MW_HOME/oracle_common/modules/oracle.osdt_11.1.1/osdt_xmlsec.jar
CP=$CP:$MW_HOME/oracle_common/modules/oracle.pki_11.1.1/oraclepki.jar

$JAVA_HOME/bin/javac -cp $CP /tmp/DumpWallet.java

$JAVA_HOME/bin/java -cp $CP DumpWallet "${PATH_TO_JPS}"

Sample Output :-

Dumping store contents ...

### Map: oracle.wsm.security
1. + Key: sign-csf-key
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = signing key alias/password
  name  = orakey
  pass  = welcome1
  expires   = null
2. + Key: enc-csf-key
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = encryption key alias/password
  name  = orakey
  pass  = welcome1
  expires   = null
3. + Key: keystore-csf-key
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = keystore access password
  name  = n/a
  pass  = welcome1
  expires   = null
4. + Key: test-appid-key
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = null
  name  = weblogic
  pass  = welcome1
  expires   = null

### Map: IDCCS
1. + Key: ldap:1340771431089
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = null
  name  = ldap:1340771431089
  pass  = MjcxN0M2ODREOEQ0RjZERg==
  expires   = null
2. + Key: db:1340771431083
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = null
  name  = db:1340771431083
  pass  = MTlDQTE1N0EzQzE3REY1OA==
  expires   = null
3. + Key: hash:1340771431089
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = null
  name  = hash:1340771431089
  pass  = MTk1MTk1QkQ4OUE2QzJBNw==
  expires   = null
4. + Key: proxy:1340771431089
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = null
  name  = proxy:1340771431089
  pass  = M0IwMUZBQjNGQTUxNzk0OA==
  expires   = null

In a WebLogic domain, you will find that there is also a cwallet.sso found in $DOMAIN_HOME/config/fmwconfig/bootstrap.

Dumping store contents ...

### Map: fks
1. + Key: master.key.0
  class = oracle.security.jps.internal.credstore.GenericCredentialImpl
  desc  = null
  type  = javax.crypto.spec.SecretKeySpec
  algorith  = AES
  format  = RAW
  key material as hex = CB 45 4F B0 F8 26 FF 04 31 9F 48 DD 43 42 69 C7
  expires   = null
2. + Key: current.key
  class = oracle.security.jps.internal.credstore.GenericCredentialImpl
  desc  = null
  type  = java.lang.String
  cred  = master.key.0
  expires   = null

### Map: IntegrityChecker
1. + Key: kss
  class = oracle.security.jps.internal.credstore.GenericCredentialImpl
  desc  = null
  type  = Array of byte
  byte array as hex = AB 18 CD 76 6C 39 FE 46 A0 6D 1C F0 BC 8D 97 3A D1 64 BC 80 2C 33 64 8E AE C9 B1 63 88 BE 23 7C 37 2F 63 9D 55 2B 5E 8F 1E 08 0A 73 F1 A8 15 83 8F 24 3D 19 B8 79 6E 75 B2 1C 7F DB 72 FC AE BA 72 A3 62 62 27 29 EE DE
  expires   = null

If you ever decide to reassociate the credential store with LDAP (e.g. using the reassociateSecurityStore command), you will find that the bootstrap cwallet.sso will contain credentials to access the LDAP store.  Here is what my domain wallet files look like when using an LDAP credential store :-

$DOMAIN_HOME/config/fmwconfig/cwallet.sso :

Dumping store contents ...

### Map: dip
1. + Key: cn=odisrv
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = DIP Password
  name  = cn=odisrv,cn=Registered Instances,cn=Directory Integration Platform,cn=Products,cn=OracleContext
  pass  = YNDMSU1wP1WergcX
  expires   = null

### Map: ODSMMap
1. + Key: ODSMKey.Wallet
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = ODSM Key store password
  name  = ODSM
  pass  = 0000000000
  expires   = null

$DOMAIN_HOME/config/fmwconfig/bootstrap/cwallet.sso :

Dumping store contents ...

### Map: BOOTSTRAP_JPS
1. + Key: bootstrap_q6ShJcm89vO8N2oVoSFqTLuW6Sg=
  class = oracle.security.jps.internal.credstore.PasswordCredentialImpl
  desc  = bootstrap user name and password
  name  = cn=orcladmin
  pass  = welcome1
  expires   = null

### Map: fks
1. + Key: master.key.0
  class = oracle.security.jps.internal.credstore.GenericCredentialImpl
  desc  = null
  type  = javax.crypto.spec.SecretKeySpec
  algorith  = AES
  format  = RAW
  key material as hex = 1C B5 89 2A 45 F2 BA A0 E5 C1 A8 F6 DE 6E FC 5A
  expires   = null
2. + Key: current.key
  class = oracle.security.jps.internal.credstore.GenericCredentialImpl
  desc  = null
  type  = java.lang.String
  cred  = master.key.0
  expires   = null

### Map: IntegrityChecker
1. + Key: kss
  class = oracle.security.jps.internal.credstore.GenericCredentialImpl
  desc  = null
  type  = Array of byte
  byte array as hex = FD 01 1E 54 D5 84 3B 9D AF DA 62 62 22 BB 7E A9 0C DB 08 A3 D9 71 9F A6 03 96 7F DF 29 69 37 55 60 5D 0E 32 EE 3A D0 D6 F2 A9 FD 58 DB 82 87 A0 98 D2 78 6A 47 48 E9 6B 86 3E 68 77 BC 17 01 B6 A0 BD 29 A2 3B E7 B7 73
  expires   = null

 

Have fun!

2 comments:

  1. Note, if when running the class from PS7, you see the error
    Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class oracle.security.jps.runtime.AppSecurityContext

    Add to the classpath the jps-ee.jar executable. For example:

    CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_11.1.1/jps-ee.jar

    ReplyDelete
  2. The classpath will vary slightly for each WebLogic and OPSS runtime release. Here's an update for 12.1.3 ... you can externalise the OPSS_VERSION to make upgrades simpler:

    OPSS_VERSION=12.1.3
    CP=/tmp
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_${OPSS_VERSION}/jps-api.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_${OPSS_VERSION}/jps-common.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_${OPSS_VERSION}/jps-internal.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_${OPSS_VERSION}/jps-ee.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_${OPSS_VERSION}/jps-se.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.jps_${OPSS_VERSION}/jps-unsupported-api.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.idm_${OPSS_VERSION}/identitystore.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.osdt_${OPSS_VERSION}/osdt_xmlsec.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.osdt_${OPSS_VERSION}/osdt_core.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.osdt_${OPSS_VERSION}/osdt_cert.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.pki_${OPSS_VERSION}/oraclepki.jar
    CP=$CP:$MW_HOME/oracle_common/modules/oracle.dms_${OPSS_VERSION}/dms.jar

    ReplyDelete