Index: security-jboss-sx/jbosssx/src/main/java/org/picketbox/plugins/vault/PicketBoxSecurityVault.java
===================================================================
--- security-jboss-sx/jbosssx/src/main/java/org/picketbox/plugins/vault/PicketBoxSecurityVault.java	(revision 484)
+++ security-jboss-sx/jbosssx/src/main/java/org/picketbox/plugins/vault/PicketBoxSecurityVault.java	(working copy)
@@ -23,6 +23,7 @@
 
 import org.jboss.security.PicketBoxLogger;
 import org.jboss.security.PicketBoxMessages;
+import org.jboss.security.Util;
 import org.jboss.security.plugins.PBEUtils;
 import org.jboss.security.vault.SecurityVault;
 import org.jboss.security.vault.SecurityVaultException;
@@ -62,7 +63,20 @@
  * The following options are expected in the {@link SecurityVault#init(Map)} call:
  * ENC_FILE_DIR: the location where the encoded files will be kept. End with "/" or "\" based on your platform
  * KEYSTORE_URL: location where your keystore is located
- * KEYSTORE_PASSWORD: Masked keystore password.  Has to be prepended with MASK-
+ * KEYSTORE_PASSWORD: keystore password.
+ * 'plain text' masked password (has to be prepended with MASK-)
+ * '{EXT}...' where the '...' is the exact command
+ * '{EXTC[:expiration_in_millis]}...' where the '...' is the exact command
+ * line that will be passed to the Runtime.exec(String) method to execute a
+ * platform command. The first line of the command output is used as the
+ * password.
+ * EXTC variant will cache the passwords for expiration_in_millis milliseconds.
+ * Default cache expiration is 0 = infinity.
+ * '{CLASS}classname[:ctorargs]' where the '[:ctorargs]' is an optional
+ * string delimited by the ':' from the classname that will be passed to the
+ * classname ctor. The ctorargs itself is a comma delimited list of strings.
+ * The password is obtained from classname by invoking a
+ * 'char[] toCharArray()' method if found, otherwise, the 'String toString()'
  * KEYSTORE_ALIAS: Alias where the keypair is located
  * SALT: salt of the masked password. Ensured it is 8 characters in length
  * ITERATION_COUNT: Iteration Count of the masked password.
@@ -113,6 +127,10 @@
    
    public static final String PASS_MASK_PREFIX = "MASK-";
    
+   public static final String PASS_CLASS_PREFIX = "{CLASS}";
+
+   public static final String PASS_EXT_PREFIX = "{EXT";
+
    public static final String PUBLIC_CERT = "PUBLIC_CERT";
    
    public static final String KEY_SIZE = "KEY_SIZE"; 
@@ -147,11 +165,13 @@
       }
       keystoreURL = StringUtil.getSystemPropertyAsString(keystoreURL);
 
-      String maskedPassword = (String) options.get(KEYSTORE_PASSWORD);
-      if(maskedPassword == null)
+      String password = (String) options.get(KEYSTORE_PASSWORD);
+      if(password == null)
          throw new SecurityVaultException(PicketBoxMessages.MESSAGES.invalidNullOrEmptyOptionMessage(KEYSTORE_PASSWORD));
-      if(maskedPassword.startsWith(PASS_MASK_PREFIX) == false)
-         throw new SecurityVaultException(PicketBoxMessages.MESSAGES.invalidUnmaskedKeystorePasswordMessage());
+      if(password.startsWith(PASS_MASK_PREFIX) == false
+              && password.startsWith(PASS_EXT_PREFIX) == false
+              && password.startsWith(PASS_CLASS_PREFIX) == false)
+         throw new SecurityVaultException(PicketBoxMessages.MESSAGES.invalidKeystorePasswordFormatMessage());
 
       String salt = (String) options.get(SALT);
       if(salt == null)
@@ -182,8 +202,7 @@
       keyStoreType = (options.get(KEYSTORE_TYPE) != null ? (String) options.get(KEYSTORE_TYPE) : defaultKeyStoreType);
 
       try {
-         String keystorePass = decode(maskedPassword, salt, iterationCount);
-         keyStorePWD = keystorePass.toCharArray();
+         keyStorePWD = loadKeystorePassword(password, salt, iterationCount);
          keystore = getKeyStore(keystoreURL);
          
          checkAndConvertKeyStoreToJCEKS(keystoreURL);
@@ -301,7 +320,21 @@
 	   }
 	   return true;
 	}
-   
+
+   private char[] loadKeystorePassword(String passwordDef, String salt, int iterationCount) throws Exception
+   {
+      final char[] password;
+
+      if( passwordDef.startsWith(PASS_MASK_PREFIX) ){
+         String keystorePass = decode(passwordDef, salt, iterationCount);
+         password = keystorePass.toCharArray();
+      }
+      else
+         password = Util.loadPassword(passwordDef);
+
+      return password;
+   }
+
    private String decode(String maskedString, String salt, int iterationCount) throws Exception
    {
       String pbeAlgo = "PBEwithMD5andDES";
Index: security-jboss-sx/jbosssx/src/test/java/org/jboss/test/SecurityActions.java
===================================================================
--- security-jboss-sx/jbosssx/src/test/java/org/jboss/test/SecurityActions.java	(revision 484)
+++ security-jboss-sx/jbosssx/src/test/java/org/jboss/test/SecurityActions.java	(working copy)
@@ -74,4 +74,49 @@
          }
       });
    }
+
+   interface SystemPropertyAction
+   {
+      SystemPropertyAction PRIVILEGED = new SystemPropertyAction()
+      {
+         public String getProperty(final String name, final String defaultValue)
+         {
+            String prop = AccessController.doPrivileged(
+               new PrivilegedAction<String>()
+               {
+                  public String run()
+                  {
+                     return NON_PRIVILEGED.getProperty(name, defaultValue);
+                  }
+               }
+            );
+            return prop;
+         }
+      };
+      SystemPropertyAction NON_PRIVILEGED = new SystemPropertyAction()
+      {
+         public String getProperty(final String name, final String defaultValue)
+         {
+            final String prop = System.getProperty(name, defaultValue);
+            return prop;
+         }
+      };
+      String getProperty(final String name, final String defaultValue);
+   }
+
+   public static String getProperty(final String name, final String defaultValue)
+   {
+      SecurityManager sm = System.getSecurityManager();
+      final String prop;
+      if( sm != null )
+      {
+         prop = SystemPropertyAction.PRIVILEGED.getProperty(name, defaultValue);
+      }
+      else
+      {
+         prop = SystemPropertyAction.NON_PRIVILEGED.getProperty(name, defaultValue);
+      }
+      return prop;
+   }
+
 }
Index: security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/KeystorePasswordProvider.java
===================================================================
--- security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/KeystorePasswordProvider.java	(revision 0)
+++ security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/KeystorePasswordProvider.java	(working copy)
@@ -0,0 +1,14 @@
+package org.jboss.test.security.vault;
+
+/**
+ * Testing password provider for a Vault keystore.
+ *
+ * @author <a href="mailto:istudens@redhat.com">Ivo Studensky</a>
+ */
+public class KeystorePasswordProvider
+{
+   public char[] toCharArray()
+   {
+      return "vault22".toCharArray();
+   }
+}
Index: security-jboss-sx/jbosssx/src/test/resources/bin/askpass.sh
===================================================================
--- security-jboss-sx/jbosssx/src/test/resources/bin/askpass.sh	(revision 0)
+++ security-jboss-sx/jbosssx/src/test/resources/bin/askpass.sh	(working copy)
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo vault22
\ No newline at end of file

Property changes on: security-jboss-sx/jbosssx/src/test/resources/bin/askpass.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: security-spi/common/src/main/java/org/jboss/security/PicketBoxMessages.java
===================================================================
--- security-spi/common/src/main/java/org/jboss/security/PicketBoxMessages.java	(revision 484)
+++ security-spi/common/src/main/java/org/jboss/security/PicketBoxMessages.java	(working copy)
@@ -456,4 +456,7 @@
 
     @Message(id = 140, value = "Unable to get keystore (%s)")
     RuntimeException unableToGetKeyStore(@Cause Throwable throwable, String file);
+
+    @Message(id = 141, value = "Keystore password should be either masked or prefixed with {EXT} or {CLASS}")
+    String invalidKeystorePasswordFormatMessage();
 }
\ No newline at end of file
Index: security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java
===================================================================
--- security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java	(revision 484)
+++ security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java	(working copy)
@@ -26,6 +26,8 @@
 import org.jboss.security.vault.SecurityVaultException;
 import org.jboss.security.vault.SecurityVaultFactory;
 import org.jboss.security.vault.SecurityVaultUtil;
+import org.jboss.test.SecurityActions;
+import org.junit.Assume;
 import org.junit.Test;
 import org.picketbox.plugins.vault.PicketBoxSecurityVault;
 
@@ -43,8 +45,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import junit.framework.Assert;
-
 import static org.junit.Assert.*;
 
 /**
@@ -164,7 +164,97 @@
       assertFalse(vault.exists(vaultBlock+"1", attributeName+"2"));
    }
    
-   
+   @Test
+   public void testClassBasedKeystorePassword() throws Exception
+   {
+
+      setInitialVaulConditions("src/test/resources/keystore/vault.jks", "target/vaults/vault2/vault.jks",
+            "src/test/resources/keystore/vault_data", "target/vaults/vault2/vault_data");
+
+      Map<String,Object> options = getVaultOptionsMap(
+            "target/vaults/vault2/vault.jks",
+            "target/vaults/vault2/vault_data",
+            "vault", "12438567", 50, "{CLASS}org.jboss.test.security.vault.KeystorePasswordProvider");
+
+      String vaultBlock = "aBlock";
+      String attributeName = "anAttribute";
+
+      char[] attributeValue = "aValue".toCharArray();
+
+      SecurityVault vault = getNewSecurityVaultInstance();
+
+      vault.init(options);
+      assertTrue(vault.isInitialized());
+
+      Map<String,Object> handshakeOptions = new HashMap<String,Object>();
+
+      byte[] sharedKey = vault.handshake(handshakeOptions);
+      assertNotNull(sharedKey);
+
+      vault.store(vaultBlock, attributeName, attributeValue , sharedKey);
+
+      assertTrue(vault.exists(vaultBlock, attributeName));
+      //Now retrieve
+      assertEquals(new String(attributeValue), new String(vault.retrieve(vaultBlock, attributeName, sharedKey)));
+
+      vault.store(vaultBlock+"1", attributeName+"2", attributeValue, sharedKey);
+      assertEquals(new String(attributeValue), new String(vault.retrieve(vaultBlock+"1", attributeName+"2", sharedKey)));
+
+      System.out.println("Currently storing:" + vault.keyList());
+
+      assertTrue(vault.remove(vaultBlock+"1", attributeName+"2", sharedKey));
+      assertFalse(vault.exists(vaultBlock+"1", attributeName+"2"));
+   }
+
+   @Test
+   public void testExtCmdBasedKeystorePassword() throws Exception
+   {
+      // since this test uses an external BASH script it is valid for Linux systems only
+      String OS_NAME = SecurityActions.getProperty("os.name", null);
+      Assume.assumeTrue(OS_NAME.startsWith("Linux") || OS_NAME.startsWith("LINUX"));
+
+      setInitialVaulConditions("src/test/resources/keystore/vault.jks", "target/vaults/vault2/vault.jks",
+            "src/test/resources/keystore/vault_data", "target/vaults/vault2/vault_data");
+
+      String absolutePathToAskPass = SecurityVaultUnitTestCase.class.getResource("/bin/askpass.sh").getFile();
+      System.out.println("absolutePathToAskPass: " + absolutePathToAskPass);
+
+      // 'Enter passphrase for *' is hard-coded into kwalletaskpass for example
+      Map<String,Object> options = getVaultOptionsMap(
+            "target/vaults/vault2/vault.jks",
+            "target/vaults/vault2/vault_data",
+            "vault", "12438567", 50, "{EXT}/bin/sh " + absolutePathToAskPass + " Enter passphrase for askpass test");
+
+      String vaultBlock = "aBlock";
+      String attributeName = "anAttribute";
+
+      char[] attributeValue = "aValue".toCharArray();
+
+      SecurityVault vault = getNewSecurityVaultInstance();
+
+      vault.init(options);
+      assertTrue(vault.isInitialized());
+
+      Map<String,Object> handshakeOptions = new HashMap<String,Object>();
+
+      byte[] sharedKey = vault.handshake(handshakeOptions);
+      assertNotNull(sharedKey);
+
+      vault.store(vaultBlock, attributeName, attributeValue , sharedKey);
+
+      assertTrue(vault.exists(vaultBlock, attributeName));
+      //Now retrieve
+      assertEquals(new String(attributeValue), new String(vault.retrieve(vaultBlock, attributeName, sharedKey)));
+
+      vault.store(vaultBlock+"1", attributeName+"2", attributeValue, sharedKey);
+      assertEquals(new String(attributeValue), new String(vault.retrieve(vaultBlock+"1", attributeName+"2", sharedKey)));
+
+      System.out.println("Currently storing:" + vault.keyList());
+
+      assertTrue(vault.remove(vaultBlock+"1", attributeName+"2", sharedKey));
+      assertFalse(vault.exists(vaultBlock+"1", attributeName+"2"));
+   }
+
    /**
     * See src/test/resources/vault-v0/readme.txt for initial vault setup (including secured attributes).
     * @throws Exception
@@ -345,6 +435,10 @@
    
    private String getMaskedPassword(String pwd, String salt, int iterationCount) throws Exception
    {
+      if (pwd.startsWith(PicketBoxSecurityVault.PASS_EXT_PREFIX)
+            || pwd.startsWith(PicketBoxSecurityVault.PASS_CLASS_PREFIX))
+         return pwd;
+
       String algo = "PBEwithMD5andDES";
       
       // Create the PBE secret key 
@@ -357,7 +451,7 @@
       
       String maskedPass = PBEUtils.encode64(pwd.getBytes(), algo, cipherKey, cipherSpec);
       
-      return new String(PicketBoxSecurityVault.PASS_MASK_PREFIX) + maskedPass; 
+      return PicketBoxSecurityVault.PASS_MASK_PREFIX + maskedPass;
    }
    
 
