Index Changes
This is version 5. It is not the current version, and thus it cannot be edited.
[Back to current version]   [Restore this version]

Background

Security often plays a role in interacting with various external systems, protocols, and implementations. Security may take the form of authorization, authentication, or encryption. This initiative seeks to provide a common mechanism for components that need to handle authorization and authentication through the use of user names and passwords.

Requirement

  • Passwords must be encrypted at all times. Clear-text passwords must never be made available to the end user
    • NOTE: A user may make use of other recommended features to fulfill this requirement. For example, passwords can be stored in the WSDL. They will likely be un-encrypted in the WSDL, but if the user makes use of environment variables, then the password will not be stored in the WSDL. To provide encryption capabilities in the tooling requires undesired coupling between tooling and runtime...something that is highly discouraged especially given the other solutions.
  • It is preferred that the encryption methodology be selectable by the user.
  • Passwords should be handled in a manner that is consistent with existing technologies (i.e. don't create an approach that is so radically different from technologies that you're using like the Glassfish Application Server or the Java SDK)
  • Passwords must be changeable without having to redeploy the entire application.

Approach

Several functional areas need to be considered in providing a consistent password handling framework
  1. Presentation
    1. Command-line interface
      • In an interactive form, the password must be masked and the user must be prompted at least once to confirm.
      • In a script form, the password must be stored in an encrypted format (like a password file) so that the password will not be in clear-text
    2. GUI interface
      • The password must be masked and the user must be prompted at least once to confirm
  2. Persistence
    1. We will not mandate a single format for persistence of passwords
    2. Passwords must be encrypted when persisted.
    3. The user should be able to set the mechanism by which passwords are encrypted
    4. Detailed approach
      • Provide a common class that all components can use which hides location of password storage. This common class will store passwords in glassfish storage, other properties local to the component. Components must use this class to store configuration
  3. Runtime use
    1. Passwords will need to be changeable without having to redeploy the entire application.

Risks

  • Often times, we have maintained a separation between tooling and runtime components for OpenESB components. Keeping this architectural principle in mind makes it harder to create a seamless password experience as the user moves from tooling to runtime. Any design will have to accommodate how these two sides will interact.
  • For Java CAPS components, passwords are currently embedded as part of the application using very weak password obfuscation. With the number of legacy components, there are great risks in not being able to provide the consistent password experience without breaking backwards-compatibility.

Overview

Password handling at all stages of the development of an application is extremely important. For obvious security reasons, passwords must be kept safely masked and/or encrypted. This design document seeks to provide a combination of components and best practices that will enable JBI Components to handle passwords in a consistent manner in JCAPS and OpenESB. Areas that will be addressed include
  • Tooling requirements and components in the Netbeans 6.0, Command-Line Interfaces, and Web-based Administration GUIs
  • Runtime requirements and components for handling passwords and storing passwords securely for Glassfish V2 in both a clustered and non-clustered environment.

Definition

While this may seem obvious, we must define what a password is. A password is anything that we want to be masked so that a user cannot view it, either through a GUI or in a persistent data store. Examples of passwords might include
  • External system passwords (i.e. Databases, Applications like SAP, Siebel)

Assumptions

  • Passwords can be embedded as part of the application in application artifacts (i.e. WSDLs)
  • Passwords can also be associated with the component and updated through a component's configuration interface. In most cases, this is some type of Mbean editor.
  • Passwords are entered by users through some type of tooling. The tooling must keep the passwords secure through masking and/or encryption.
  • Passwords are used during the running of the application and component.

Key Requirements

This section provides a brief overview of the key functional requirements of the component or system to be developed.
RequirementDescription
PH 1 Passwords cannot be in clear text in any application artifacts
PH 2 GUI-based tooling cannot reveal plain-text passwords
PH 3 Passwords must be stored in an encrypted fashion
PH 4 The runtime processing of passwords must not allow the password to be revealed in clear-text at any point.

Technical Description

Component Architecture

The core classes will address each of the key requirements in the following manner:
  1. PH1 โ€“ Passwords will not be allowed to be embedded in application artifacts. They will be exported using the environment variable design. This design will not be detailed in this document.
  2. PH2 โ€“ A schema will be provided that details the configuration of the component. This schema will be available to all tools to discern what fields need to be masked. (This strategy may change in the future and has not been completely flushed out.)
  3. PH3, PH4 โ€“ The JBI Framework will provide an implementation of KeyStoreUtil which will allow components to encrypt and decrypt strings. It is up to the component to properly use the utility on all fields that are designated as passwords.

Structural Models

The class role descriptions below describe what each class/interface's responsibilities.

com.sun.jbi.security.KeyStoreUtil The KeyStoreUtil interface provides methods for creating, updating, deleting, and reading keys from a key store. The interface also provides utility methods to encrypt and decrypt strings based on the name of the key. The interface makes no restrictions on where the key store is located. Other than the encrypt and decrypt methods, the interface makes no other restrictions on how the key store will be used

com.sun.jbi.framework.sun.SunASKeyStoreUtil The SunASKeyStoreUtil is an implementation of KeyStoreUtil. It uses the underlying PasswordAdapter class provided by the Glassfish Application Server to generate and store keys. Encryption and decryption capabilities are provided by the javax.crypto.Cipher class.

com.sun.jbi.component.ComponentContext The ComponentContext interface details extensions provided by the JBI Framework. There is now the addition of a getKeyStoreUtil() method which Component developers can now call to get an instance of KeyStoreUtil

com.sun.jbi.framework.ComponentContext The ComponentContext class provides the implementation of com.sun.jbi.component.ComponentContext. With regards to the getKeyStoreUtil() method, this implementation should delegate the work to the PlatformContext.

com.sun.jbi.platform.PlatformContext This PlatformContext interface provides a set of common methods that a particular platform needs to provide for the JBI Framework. With this initiative, we've added a new getKeyStoreUtil() method.

SunASPlatformContext, JSEPlatformContext, WebSpherePlatformContext These three classes are implementations of PlatformContext. They will all include the new getKeyStoreUtil() method.

Behavioral Models

Encrypting a string

Exported Interfaces

Interface Name Proposed Stability Classification Specified Former Stability Classification
KeyStoreUtil Unstable Provide a reference to the actual interface None

Imported Interfaces

Interface Name Proposed Stability Classification Specified Former Stability Classification
PasswordAdapter Unstable Provide a reference to the actual interface None
IdentityManager Unstable Provide a reference to the actual interface None

Implementation Considerations

  • Persistence of Keys
    • Implementations of KeyStoreUtil should obfuscate keys when storing them in a KeyStore
    • Persistence is assumed to occur immediately and atomically with each of the Create, Update, and Delete operations for keys on KeyStoreUtil
  • Clustering synchronization
    • (Need input from Keith Babo)
    • Client considerations when using KeyStoreUtil
    • The KeyStoreUtil interface is very simple and basic. However certain considerations need to be addressed in obtaining this interface and using it. These questions should be considered in providing a client-side library:
      1. If the framework does not provide an implementation of this class, what should the client do? Should the client fail, use another utility, or simply expose the passwords in clear text?
      2. Getting the KeyStoreUtil interface from the framework requires casting to an interface specific to the JBI Framework. This limits portability of our components. Can we fix this in some way?

Limitations of interfaces

The KeyStoreUtil class has methods for encyrpt/decrypt. These methods do not allow the use of additional attributes for further configurability. This was done on purpose to limit flexibility and increase ease of use.

Example usage of interfaces

The Password Handling classes and interfaces are fairly simplistic. The following examples will show how to use them.
  • Obtaining a KeyStoreUtil
public void init(ComponentContext jbiContext) throws JBIException {
            KeyStoreUtil keystoreUtil = null;
            if (mContext instanceof com.sun.jbi.component.ComponentContext) {
                keystoreUtil = ((com.sun.jbi.component.ComponentContext)mContext).getKeyStoreUtil();
            }
}

Environment Variables

Within the use of Environment Variables, we need to remember that we are providing create, read, update, and delete functionality. When loading environment variables, you'll be providing mostly read functionality. When storing environment variables, you'll be creating, updating, or deleting environment variables from the data store.
When dealing with password fields, they must be encypted when persisting environment variables. When loading the environment variables from a persistent store, the password fields must be decrypted.
    public void persistEnvVariableConfiguration() throws MBeanException {
        // Persist the changed configuration        
        try {
            File envVarPersistFileName = new File(mWorkspaceRoot, PERSIST_ENVVAR_CONFIG_FILE_NAME);
            OutputStream os = new FileOutputStream(envVarPersistFileName);
            for (Iterator iter = mEnvVarMap.keySet().iterator(); iter.hasNext(); ) {
               String key = (String) iter.next();
               String[] metadata = (String[]) mEnvVarMap.get(key);
               String value = metadata[0];
               String type = metadata[1];
               if (type.equals("PASSWORD")) {
                   value = mKeyStoreUtil.encrypt(value);
               }
               String prop = (value != null)? key + "=" + value + "{" + type + "}\n" : key + "={" + type + "}\n";
               os.write(prop.getBytes());
            } 
            os.close();
        } catch (Exception ex) {
            throw new MBeanException(ex, mMessages.getString("RTC_Failed_persist_env_var", 
                                     new Object[] {mWorkspaceRoot, ex.getMessage()}));
        } 
    }

    public Map loadEnvironmentVariableConfig(String workspaceRoot) throws JBIException {
        Properties persistedConfig = new Properties();
        Map envVarMap = new HashMap();
        
        File envVarPersistFileName = new File(workspaceRoot, PERSIST_ENVVAR_CONFIG_FILE_NAME);
        if (!envVarPersistFileName.exists()) {
            return envVarMap;
        }
        
        try {
            InputStream is = new FileInputStream(envVarPersistFileName);
            persistedConfig.load(is);
            is.close();
            
            // load the persisted environment variable configurations in the map
            for (Enumeration e = persistedConfig.propertyNames(); e.hasMoreElements(); ) {
                String name = (String) e.nextElement();
                String metadata = persistedConfig.getProperty(name);
                int startIndex = metadata.indexOf("{");
                String value = (startIndex == 0)? null : metadata.substring(0, startIndex);
                String type = metadata.substring(startIndex + 1, metadata.length() -1);
                if (type.equals("PASSWORD")) {
                    value = mKeyStoreUtil.decrypt(value);
                }
                envVarMap.put(name, new String[] {value, type});
            }
        } catch (Exception ex) {
            throw new JBIException(mMessages.getString("RTC_Failed_loading_env_config", new Object[] {envVarPersistFileName, ex.getMessage()}), ex);
        	     }    
        return envVarMap;
    }

Mbean Classes

Mbeans may have attributes that are passwords. In the following example, we assume that Mbeans must be persisted immediately
    public String getProxyPassword() throws Exception {
        try {
            String base64Encoded = mConfig.getProperty(CONFIG_PROXY_PASSWORD);
            return mKeyStoreUtil.decrypt(base64Encoded);
        } catch (Exception ex) {
            throw new Exception(ex);
        }
    }
    
    public void setProxyPassword(String val) throws MBeanException {
        String newVal = val; 
        
        // Apply the change
        String oldVal = null;
        try {
            oldVal = getProxyPassword();
            String base64Encoded = mKeyStoreUtil.encrypt(val);
            mConfig.put(CONFIG_PROXY_PASSWORD, base64Encoded); 
        } catch (Exception ex) {
            throw new MBeanException(ex);
        }
                
        // Save the change
        persistConfiguration();
        
        // Notify listeners of this change
        long seqNo = 0;
        String msg = "Attribute changed";
        String attrType = String.class.getName();
        Notification notif = new AttributeChangeNotification(this, seqNo, System.currentTimeMillis(), msg, 
                                                             CONFIG_PROXY_PASSWORD, attrType, oldVal, newVal);
        broadcasterSupport.sendNotification(notif);
    }

User Interface

There are multiple clients that have the ability to view and set passwords. These clients include, but are not limited to, the JBI Manager, Enterprise Manager, the WSDL Editor, and the the CLI. The main underlying principle driving the client interface is that component code should not rely on the client to properly mask passwords. To that end, we have established the following criteria:
  1. Make password fields write-only. Do not allow the clients to "get" the password. This will basically allow no client to inadvertently get the password and expose it to the outside world. For the JBI Manager and Enterprise Manager, we can certainly designate the MBean fields as write-only by providing just a setter method. For the WSDL editor, this is much more difficult since the passwords are stored in a WSDL file that is viewable by all. For CLI, the same problem exists since passwords are often stored in Properties files which are viewable by all.
  2. Provide a "proper" password editor. The only time passwords can be exposed, then, is when the user is setting them. To ensure the integrity of the password, we want to provide the typical password editor that will allow the user to enter the password twice and have the option of having it masked when typing it in.

JSPWiki v2.4.100
[RSS]
« Home Index Changes Prefs
This particular version was published on 06-Dec-07 17:08 PM, -0800 by Alexander.Fung