All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.enterprise.security.store.PasswordAdapter Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.security.store;

import com.sun.enterprise.util.SystemPropertyConstants;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import java.util.Enumeration;

/**
 * This class implements an adapter for password manipulation a JCEKS.
 ** Note that although it uses locks ('synchronized'), it tends to be created
 ** anew with each use, an inefficient and potentially problematic use that
 ** could create more than one instance accessing the same keystore at a time.
 */
public final class PasswordAdapter {
    public static final String PASSWORD_ALIAS_KEYSTORE = "domain-passwords";

    private KeyStore    _pwdStore;
    private final File  _keyFile;
    private char[]      _masterPassword;

    private char[] getMasterPassword() {
        return _masterPassword;
    }

/*
    private static final boolean DEBUG = true;
    private void //debug( final Object o )
    {
        if ( DEBUG )
        {
            System.out.println( "PasswordAdapter.debug: " + o );
        }
    }
        private void
    debugState( final String when)
    {
        if ( DEBUG )
        {
            final String INDENT = "    ";
            try
            {
                String s = "PasswordAdapter Status: " + when + "\n";
                s = s +INDENT + "Master password: " + new String( getMasterPassword() ) +"\n";
                s = s + INDENT + "Key file: " + _keyFile + "\n";
                s = s +INDENT + "Aliases:\n";
                final Enumeration   aliases = getAliases();
                while ( aliases.hasMoreElements() )
                {
                    final String alias  = aliases.nextElement();
                    s = s +INDENT + INDENT + alias + "=" +
                            new String( getPasswordForAlias(alias) ) + "\n";
                }
                //debug(s);
            }
            catch( Throwable t )
            {
                t.printStackTrace();
            }
        }
    }
*/

        private static String
    getDefaultKeyFileName()
    {
        return System.getProperty(SystemPropertyConstants.INSTANCE_ROOT_PROPERTY) +
            File.separator + "config" + File.separator + PASSWORD_ALIAS_KEYSTORE;
    }

    /**
     * Construct a PasswordAdapter with given Shared Master Password,
     * SMP using the default keyfile (domain-passwords.jceks)
     * @param smp master password
     * @throws CertificateException
     * @throws IOException
     * @throws KeyStoreException
     * @throws NoSuchAlgorithmException
     */
    public PasswordAdapter(char[] masterPassword)
            throws CertificateException, IOException,
            KeyStoreException, NoSuchAlgorithmException
    {
       this( getDefaultKeyFileName(), masterPassword );
    }

    /**
     * Construct a PasswordAdapter with given Shared Master Password,
     * SMP.
     * @param keyfileName the jceks key file name
     * @param smp master password
     * @throws CertificateException
     * @throws IOException
     * @throws KeyStoreException
     * @throws NoSuchAlgorithmException
     */
    public PasswordAdapter(
        final String keyStoreFileName,
        final char[] masterPassword)
            throws CertificateException, IOException,
            KeyStoreException, NoSuchAlgorithmException
    {
        final File  keyStoreFile    = new File( keyStoreFileName );

        _pwdStore   = loadKeyStore( keyStoreFile, masterPassword);

        // assign these only once the store is good; no need to keep copies otherwise!
        _keyFile            = keyStoreFile;
        _masterPassword     = masterPassword;

        //debugState( "PasswordAdapter constructor" );
    }

    /**
     * Construct a PasswordAdapter with given Shared Master Password,
     * SMP.
     * @param keyfileName the jceks key file name
     * @param smp the master password
     * @exception CertificateException
     * @exception IOException
     * @exception KeyStoreException
     * @exception NoSuchAlgorithmException
     */
       private static KeyStore
    loadKeyStore( final File keyStoreFile, final char[] masterPassword )
            throws CertificateException, IOException,
            KeyStoreException, NoSuchAlgorithmException
    {
        final KeyStore  keyStore = KeyStore.getInstance("JCEKS");

        if ( keyStoreFile.exists() )
        {
            // don't buffer keystore; it's tiny anyway
            final FileInputStream   input   = new FileInputStream( keyStoreFile );
            try {
                keyStore.load( input, masterPassword );
            }
            finally {
                input.close();
            }
        }
        else
        {
            keyStore.load( null, masterPassword );
        }

        return keyStore;
    }

    /**
     * This methods returns password String for a given alias and SMP.
     * @param alias
     * @return corresponding password or null if the alias does not exist.
     * @exception KeyStoreException
     * @exception NoSuchAlgorithmException
     * @exception UnrecoverableKeyException
     */
    public synchronized String getPasswordForAlias(final String alias)
            throws KeyStoreException, NoSuchAlgorithmException,
            UnrecoverableKeyException
    {
        String passwordString = null;

        final Key key = _pwdStore.getKey( alias, getMasterPassword() );
        if ( key != null )
        {
            passwordString  = new String( key.getEncoded() );
        }

        return passwordString;
    }

    /**
     * This methods returns password SecretKey for a given alias and SMP.
     * @param alias
     * @return corresponding password SecretKey or
     *         null if the alias does not exist.
     * @exception KeyStoreException
     * @exception NoSuchAlgorithmException
     * @exception UnrecoverableKeyException
     */
    public synchronized SecretKey getPasswordSecretKeyForAlias(String alias)
            throws KeyStoreException, NoSuchAlgorithmException,
            UnrecoverableKeyException {

        return (SecretKey)_pwdStore.getKey(alias, getMasterPassword());
    }

    /**
     * See if the given alias exists
     * @param alias the alias name
     * @return true if the alias exists in the keystore
     */
    public synchronized boolean aliasExists( final String alias) throws KeyStoreException
    {
        return _pwdStore.containsAlias(alias);
    }

    /**
     * Remove an alias from the keystore
     * @param alias The name of the alias to remove
     * @throws KeyStoreException
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws CertificateException
     */
    public synchronized void removeAlias( final String alias)
        throws KeyStoreException, IOException,
            NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException
    {
        _pwdStore.deleteEntry(alias);
        writeStore();
    }

    /**
     * Return the aliases from the keystore.
     * @return An enumeration containing all the aliases in the keystore.
     */
    public synchronized Enumeration getAliases()
        throws KeyStoreException
    {
        return _pwdStore.aliases();
    }

    /**
     * Writes the keystore to disk
     * @throws KeyStoreException
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws CertificateException
     */
    public void writeStore() throws KeyStoreException, IOException,
        NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException
    {
        writeKeyStoreSafe( getMasterPassword() );
    }

    /**
     * This methods set alias, secretKey into JCEKS keystore.
     * @param alias
     * @param secretKey
     * @exception CertificateException
     * @exception IOException
     * @exception KeyStoreException
     * @exception NoSuchAlgorithmException
     */
    public synchronized void setPasswordForAlias(
        final String alias,
        final byte[] keyBytes)
        throws CertificateException, IOException, KeyStoreException,
            NoSuchAlgorithmException, UnrecoverableKeyException
    {
        //debugState( "BEFORE setPasswordForAlias" );
        final Key key = new SecretKeySpec(keyBytes, "AES");
        _pwdStore.setKeyEntry( alias, key, getMasterPassword(), null);
        writeStore();
        //debugState( "AFTER setPasswordForAlias" );
    }


    /**
        Make a new in-memory KeyStore with all the keys secured with
        the new master password.
      */
        private KeyStore
    duplicateKeyStore( final char[] newMasterPassword)
        throws KeyStoreException, IOException, NoSuchAlgorithmException,
            CertificateException, UnrecoverableKeyException
    {
        final char[]    oldMasterPassword       = getMasterPassword();

        final KeyStore oldStore = _pwdStore;
        final KeyStore newKeyStore = KeyStore.getInstance("JCEKS", _pwdStore.getProvider() );
        newKeyStore.load( null, newMasterPassword );

        final Enumeration   aliasesEnum = oldStore.aliases();
        while ( aliasesEnum.hasMoreElements() )
        {
            final String alias  = aliasesEnum.nextElement();

            if ( ! oldStore.isKeyEntry( alias ) )
            {
                throw new IllegalArgumentException( "Expecting keys only" );
            }

            final Key key      = oldStore.getKey( alias, oldMasterPassword );
            newKeyStore.setKeyEntry( alias, key, newMasterPassword, null);
        }

        return newKeyStore;
    }

    /**
        Write the KeyStore to disk.  Calling code should protect against
        overwriting any original file.
        @param keyStore
        @param file
        @param masterPassword
     */
        private static void
    writeKeyStoreToFile(
        final KeyStore  keyStore,
        final File      file,
        final char[]    masterPassword )
           throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException
    {
        final FileOutputStream  out = new FileOutputStream(file);
        try
        {
            keyStore.store( out, masterPassword);
        }
        finally
        {
            out.close();
        }
    }


    /**
        Writes the current KeyStore to disk in a manner that preserves its
        on-disk representation from being destroyed if something goes wrong;
        a temporary file is used.
     */
        private synchronized void
    writeKeyStoreSafe( final char[] masterPassword)
        throws KeyStoreException, IOException, NoSuchAlgorithmException,
            CertificateException, UnrecoverableKeyException
     {
        final boolean keystoreExists = _keyFile.exists();

        // if the KeyStore exists, update it in a manner that doesn't destroy
        // the existing store if a failure occurs.
        if ( keystoreExists )
        {
            final KeyStore newKeyStore = duplicateKeyStore( masterPassword );

            // 'newKeyStore' is now complete; rename the old KeyStore, the write the new one in its place
            final File  saveOld = new File( _keyFile.toString() + ".save" );

            if ( ! _keyFile.renameTo( saveOld ) )
            {
                final String msg    = "Can't rename " + _keyFile + " to " + saveOld;
                throw new IOException( msg );
            }

            try
            {
                //debug( "Writing KeyStore to " + _keyFile + " using master password = " + new String(masterPassword) );
                writeKeyStoreToFile( newKeyStore, _keyFile, masterPassword );
                _pwdStore   = newKeyStore;
                _masterPassword = masterPassword;
                //debug( "KeyStore written successfully" );
            }
            catch( final Throwable t )
            {
                try
                {
                    if(!saveOld.renameTo( _keyFile ))
                        throw new RuntimeException( "Could not write new KeyStore, and " +
                        "cannot restore KeyStore to original state", t );
                }
                catch( final Throwable tt )
                {
                    /* best effort failed */
                    throw new RuntimeException( "Could not write new KeyStore, and " +
                        "cannot restore KeyStore to original state", tt );
                }

                throw new RuntimeException( "Can't write new KeyStore", t );
            }

            try
            {
                //debug( "deleting old keystore " + saveOld );
                if(!saveOld.delete())
                    throw new RuntimeException( "Can't remove old KeyStore \"" +  _keyFile  + "\"");

               // //debug( "done deleting old keystore "  saveOld );
            }
            catch( Throwable t )
            {
                throw new RuntimeException( "Can't remove old KeyStore \"" +  _keyFile  + "\"", t );
            }
        }
        else
        {
            //debug( "Writing new KeyStore to " + _keyFile + " using master password = " + new String(masterPassword) );
            writeKeyStoreToFile( _pwdStore, _keyFile, masterPassword );
        }

        //debugState( "AFTER changing master password" );

        loadKeyStore( _keyFile, getMasterPassword() );

        //debugState( "AFTER forcing reload from file" );
     }


    /**
        Changes the keystore password, including the encoding of the keys within it.
        

There are several error conditions that could occur:

  • Problem extracting existing alias keys with new ones.
  • Problem writing the keystore, including destroying it if an I/O problem occurs.
For these reasons, make a new KeyStore and write it, then swap it with the old one. * @param newpassword the new keystore password * @throws KeyStoreException * @throws IOException * @throws NoSuchAlgorithmException * @throws CertificateException */ public synchronized void changePassword(char[] newMasterPassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { //debug( "Changing master password from " + new String(oldMasterPassword) + " to " + new String(newMasterPassword) ); //debugState( "BEFORE changing master password" ); writeKeyStoreSafe(newMasterPassword); //debugState( "AFTER changing master password" ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy