Developing a customPasswordEncryption Provider

You can develop a customPasswordEncryption provider to have custom authorization decisions for Java™ Platform, Enterprise Edition (J2EE) applications by implementing the com.ibm.wsspi.security.crypto.CustomPasswordEncryption interface that is provided in the Liberty server.

About this task

Note: The following optional steps are for supporting the command line utilities. If the provider intends to support the server runtime environment only, then these steps can be skipped.
The customPasswordEncryption provider consists of following three files:
  • The OSGi Bundle File, which contains the executable.
  • The Feature Manifest File, which defines the user feature of the customPasswordEncryption provider. This file contains the location of the required bundles and attributes of the feature, and the extension manifest file if it is packaged. This information is consumed by the server run time.
  • The Extension Manifest File, which defines the required bundle files and implementation class name of the customPasswordEncryption provider. This file is optional and required for supporting the command line utilities.

Procedure

  1. Create an OSGi component that provides a service that implements the com.ibm.wsspi.security.crypto.CustomPasswordEncryption interface.

    The CustomPasswordEncryption interface defines three methods, decrypt, which the Liberty server run time invokes to decrypt the string, encrypt, which the Liberty server run time invokes to encrypt the string, and initialize, which is reserved for future use.

    The following example uses OSGi declarative services annotations:
    package com.mycompany.custom;
    
    import org.osgi.service.component.ComponentContext;
    import org.osgi.service.component.annotations.Activate;
    import org.osgi.service.component.annotations.Component;
    import org.osgi.service.component.annotations.ConfigurationPolicy;
    import org.osgi.service.component.annotations.Deactivate;
    import org.osgi.service.component.annotations.Modified;
    
    import com.ibm.wsspi.security.crypto.CustomPasswordEncryption;
    import com.ibm.wsspi.security.crypto.EncryptedInfo;
    import com.ibm.wsspi.security.crypto.PasswordDecryptException;
    import com.ibm.wsspi.security.crypto.PasswordEncryptException;
    
    /**
     */
    @Component(service = CustomPasswordEncryption.class,
                    immediate = true,
                    name = "com.mycompany.CustomPasswordEncryptionImpl",
                    configurationPolicy = ConfigurationPolicy.OPTIONAL,
                    property = { "someKey=someValue" })
    public class CustomPasswordEncryptionImpl implements CustomPasswordEncryption {
    
        @Activate
        protected synchronized void activate(ComponentContext cc, Map<String, Object> props) {
        }
    
        @Modified
        protected synchronized void modify(Map<String, Object> props) {
        }
    
        @Deactivate
        protected void deactivate(ComponentContext cc) {
        }
    
        /**
         * The encrypt operation takes a UTF-8 encoded String in the form of a byte[].
         * The byte[] is generated from String.getBytes("UTF-8"). An encrypted byte[]
         * is returned from the implementation in the EncryptedInfo object.
         * Additionally, a logically key alias is returned in EncryptedInfo so which
         * is passed back into the decrypt method to determine which key was used to
         * encrypt this password. The WebSphere Application Server runtime has no
         * knowledge of the algorithm or key used to encrypt the data.
         * 
         * @param decrypted_bytes
         * @return com.ibm.wsspi.security.crypto.EncryptedInfo
         * @throws com.ibm.wsspi.security.crypto.PasswordEncryptException
         **/
        @Override
        public EncryptedInfo encrypt(byte[] input) throws PasswordEncryptException {
            byte[] output = null;
            String key = null;
            try {
                :
                <do some encryption>
                :
                return new EncryptedInfo(output, key);
            } catch (Exception e) {
                throw new PasswordEncryptException("Exception is caught", e);
            }
        }
    
        /**
         * The decrypt operation takes the EncryptedInfo object containing a byte[]
         * and the logical key alias and converts it to the decrypted byte[]. The
         * WebSphere Application Server runtime will convert the byte[] to a String
         * using new String (byte[], "UTF-8");
         * 
         * @param info
         * @return byte[]
         * @throws PasswordEncryptException
         * @throws com.ibm.wsspi.security.crypto.PasswordDecryptException
         **/
        @Override
        public byte[] decrypt(EncryptedInfo info) throws PasswordDecryptException {
            byte[] input = info.getEncryptedBytes();
            String key = info.getKeyAlias();
            byte[] output = null;
            try {
                :
                <do some decryption>
                :
                return output;
            } catch (Exception e) {
                throw new PasswordEncryptException("Exception is caught", e);
            }
        }
    
        /**
         * This is reserved for future use and is currently not called by the
         * WebSphere Application Server runtime.
         * 
         * @param initialization_data
         **/
        @SuppressWarnings("rawtypes")
        @Override
        public void initialize(Map initialization_data) {}
    
    }
  2. Package the component into an OSGi bundle that is part of your user feature. Make sure that the bundle includes the OSGi service manifest.
    The following example shows the contents of OSGi service manifest:
    <?xml version="1.0" encoding="UTF-8"?>
    <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.mycompany.custom.CustomPasswordEncryptionImpl" configuration-policy="optional" immediate="true" activate="activate" deactivate="deactivate" modified="modify">
      <implementation class="com.mycompany.custom.CusomPasswordEncryptionImpl"/>
      <service>
        <provide interface="com.ibm.wsspi.security.crypto.CustomPasswordEncryption"/>
      </service>
      <property name="<someKey>" type="String" value="<someValue>"/>
    </scr:component>
  3. Ensure that your feature manifest file includes the OSGi subsystem content with start-phase:="SERVICE_EARLY". If the optional step is completed, then the location of the extension manifest file is in the Subsystem-Content header.
    For example:
    Manifest-Version: 1.0
    IBM-Feature-Version: 2
    IBM-ShortName: customPasswordEncryption-1.0
    Subsystem-Type: osgi.subsystem.feature
    Subsystem-Version: 1.0.0
    Subsystem-ManifestVersion: 1.0
    Subsystem-SymbolicName: customPasswordEncryption-1.0;visibility:=public
    Subsystem-Content: com.mycompany.custom; version="[1,1.0.100)"; start-phase:="SERVICE_EARLY",
     customEncryption.jar; type=file; location:="bin/tools/extensions/ws-customPasswordEncryption/customEncryption.jar"
    Subsystem-Description: MyCompany custom password encryption
    
  4. After the feature is installed into the user product extension location, configure the server.xml file with the feature name.
    <featureManager>
       ...
       <feature>usr:customPasswordEncryption-1.0</feature>
    </featureManager>

    To verify that the installation was successful for the server runtime environment, verify that CWWKS1850I message is logged in the message.log file.

  5. Optional: Create the extension manifest file, which is required to plug in the customPasswordEncryption provider to the command line utilities such as SecurityUtility. The extension manifest file is a JAR file, which contains the MANIFEST.MF file. The required headers of the extension manifest are:
    • Require-Bundle: Defines the list of bundles, which need to be loaded. The format is the same as the OSGi bundle manifest file.
    • IBM-ImplementationClass: Defines the implementation class, which implements the com.ibm.wsspi.security.crypto.CustomPasswordEncryption interface.

    The following is an example MANIFEST.MF file

    Require-Bundle: com.mycompany.custom; version="[1,1.0.100)"; location="usr/extension/lib"
    IBM-ImplementationClass: com.mycompany.custom.CusomPasswordEncryptionImpl
    Use the jar command to package the extension manifest file. For example:
    jar cfm customEncryption.jar MANIFEST.MF

    Place the JAR file in wlp/bin/tools/extensions/ws-customPasswordEncryption. Make sure that the location of the extension manifest file is in the Subsystem-Content header section of the feature manifest file.

    These files need to be in specific directories. The following depicts the relationship of the files:
    Figure 1. customPasswordEncryption provider files
    A flow chart depicting the relationship of the files and their directories.
  6. Optional: If step 5 is completed, then you can verify that the installation was successful by using the following command line utilities command
    wlp/bin/securityUtility encode --listCustom
    The expected output is the information of the customPasswordEncryption. For Example:
    [{"name":"custom","featurename":"usr:customPasswordEncryption-1.0","description":"MyCompany custom password encryption"}]

    Make sure that all of the required files exist, and the contents of the files are accurate.