/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.apache.directory.studio.connection.core; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStore.SecretKeyEntry; import java.security.KeyStoreException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import org.apache.directory.api.util.FileUtils; /** * A wrapper around {@link KeyStore} for storing passwords. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public class PasswordsKeyStoreManager { /** The default filename */ private static final String KEYSTORE_DEFAULT_FILENAME = "passwords.jks"; /** The keystore file name */ private String filename = KEYSTORE_DEFAULT_FILENAME; /** The master password */ private String masterPassword; /** The keystore */ private KeyStore keystore; /** * Creates a new instance of PasswordsKeyStoreManager. */ public PasswordsKeyStoreManager() { } /** * Creates a new instance of PasswordsKeyStoreManager. * * @param filename the filename */ public PasswordsKeyStoreManager( String filename ) { this.filename = filename; } /** * Indicates if the keystore is loaded. * * @return <code>true</code> if the keystore is loaded, * <code>false</code> if not. */ public boolean isLoaded() { return keystore != null; } public void load( String masterPassword ) throws KeyStoreException { this.masterPassword = masterPassword; try { keystore = KeyStore.getInstance( "JCEKS" ); //$NON-NLS-1$ // Getting the keystore file File keystoreFile = getKeyStoreFile(); // Checking if the keystore file is available on disk if ( keystoreFile.exists() && keystoreFile.isFile() && keystoreFile.canRead() ) { keystore.load( new FileInputStream( keystoreFile ), masterPassword.toCharArray() ); } else { keystore.load( null, null ); } } // Catch for the following exceptions that may be raised while // handling the keystore: // - java.security.KeyStoreException // - java.security.NoSuchAlgorithmException // - java.security.cert.CertificateException catch ( GeneralSecurityException e ) { this.masterPassword = null; this.keystore = null; throw new KeyStoreException( e ); } // Catch for the following exceptions that may be raised while // handling the file: // - java.io.IOException // - java.io.FileNotFoundException catch ( IOException e ) { this.masterPassword = null; this.keystore = null; throw new KeyStoreException( e ); } } /** * Saves the keystore on disk. */ public void save() throws KeyStoreException { if ( isLoaded() && ( masterPassword != null ) ) { try { keystore.store( new FileOutputStream( getKeyStoreFile() ), masterPassword.toCharArray() ); } // Catch for the following exceptions that may be raised while // handling the keystore: // - java.security.KeyStoreException // - java.security.NoSuchAlgorithmException // - java.security.cert.CertificateException catch ( GeneralSecurityException e ) { throw new KeyStoreException( e ); } // Catch for the following exceptions that may be raised while // handling the file: // - java.io.IOException // - java.io.FileNotFoundException catch ( IOException e ) { throw new KeyStoreException( e ); } } } /** * Checks the master password. * * @param masterPassword the master password * @return <code>true</code> if the master password is correct, * <code>false</code> if not. * @throws KeyStoreException if an error occurs */ public boolean checkMasterPassword( String masterPassword ) throws KeyStoreException { // If the keystore is already loaded, we compare the master password directly if ( isLoaded() ) { return ( ( this.masterPassword != null ) && ( this.masterPassword.equals( masterPassword ) ) ); } // The keystore is not loaded yet else { try { // Loading the keystore load( masterPassword ); // Returning the check value return isLoaded(); } catch ( KeyStoreException e ) { throw e; } } } /** * Sets the master password. * * @param masterPassword the master password */ public void setMasterPassword( String masterPassword ) { // Creating a map to store previously stored passwords Map<String, String> passwordsMap = new HashMap<String, String>(); if ( isLoaded() ) { // Getting the connection IDs String[] connectionIds = getConnectionIds(); // Storing the password of each connection in the map for ( String connectionId : connectionIds ) { // Getting the connection password String connectionPassword = getConnectionPassword( connectionId ); // Checking if we got a password if ( connectionPassword != null ) { // Storing the password of the connection in the map passwordsMap.put( connectionId, connectionPassword ); } // Removing the password from the keystore storeConnectionPassword( connectionId, null, false ); } } // Assigning the new master password this.masterPassword = masterPassword; // Storing the previous passwords back in the keystore if ( passwordsMap.size() > 0 ) { Set<String> connectionIds = passwordsMap.keySet(); // Storing the password of each connection in the keystore if ( connectionIds != null ) { for ( String connectionId : connectionIds ) { String connectionPassword = passwordsMap.get( connectionId ); if ( connectionPassword != null ) { // Storing the password of the connection in the keystore storeConnectionPassword( connectionId, connectionPassword, false ); } } } } } /** * Gets the keystore file. * * @return the keystore file */ public File getKeyStoreFile() { return ConnectionCorePlugin.getDefault().getStateLocation().append( filename ).toFile(); } /** * Deletes the keystore. */ public void deleteKeystoreFile() { // Getting the keystore file File keystoreFile = getKeyStoreFile(); // Checking if the keystore file is available on disk if ( keystoreFile.exists() && keystoreFile.isFile() && keystoreFile.canRead() && keystoreFile.canWrite() ) { keystoreFile.delete(); } } /** * Gets the connections IDs contained in the keystore. * * @return the connection IDs contained in the keystore */ public String[] getConnectionIds() { if ( keystore != null ) { try { return Collections.list( keystore.aliases() ).toArray( new String[0] ); } catch ( KeyStoreException e ) { // Silent } } return new String[0]; } /** * Stores a connection password. * * @param connection the connection * @param password the password */ public void storeConnectionPassword( Connection connection, String password ) { if ( connection != null ) { storeConnectionPassword( connection.getId(), password ); } } /** * Stores a connection password. * * @param connection the connection * @param password the password * @param saveKeystore if the keystore needs to be saved */ public void storeConnectionPassword( Connection connection, String password, boolean saveKeystore ) { if ( connection != null ) { storeConnectionPassword( connection.getId(), password, true ); } } /** * Stores a connection password. * * @param connectionId the connection id * @param password the password */ public void storeConnectionPassword( String connectionId, String password ) { storeConnectionPassword( connectionId, password, true ); } /** * Stores a connection password. * * @param connectionId the connection id * @param password the password * @param saveKeystore if the keystore needs to be saved */ public void storeConnectionPassword( String connectionId, String password, boolean saveKeystore ) { if ( isLoaded() && ( connectionId != null ) ) { try { // Checking if the password is null if ( password == null ) { // We need to remove the corresponding entry in the keystore if ( keystore.containsAlias( connectionId ) ) { keystore.deleteEntry( connectionId ); } } else { // Generating a secret key from the password SecretKeyFactory factory = SecretKeyFactory.getInstance( "PBE" ); SecretKey generatedSecret = factory.generateSecret( new PBEKeySpec( password.toCharArray() ) ); // Setting the entry in the keystore keystore.setEntry( connectionId, new KeyStore.SecretKeyEntry( generatedSecret ), new KeyStore.PasswordProtection( masterPassword.toCharArray() ) ); } // Saving if ( saveKeystore ) { save(); } } catch ( Exception e ) { // Silent } } } /** * Gets a connection password. * * @param connection the connection * @return the password for the connection or <code>null</code>. */ public String getConnectionPassword( Connection connection ) { if ( connection != null ) { return getConnectionPassword( connection.getId() ); } return null; } /** * Gets a connection password. * * @param connectionId the connection id * @return the password for the connection id or <code>null</code>. */ public String getConnectionPassword( String connectionId ) { if ( isLoaded() && ( connectionId != null ) ) { try { SecretKeyFactory factory = SecretKeyFactory.getInstance( "PBE" ); SecretKeyEntry ske = ( SecretKeyEntry ) keystore.getEntry( connectionId, new KeyStore.PasswordProtection( masterPassword.toCharArray() ) ); if ( ske != null ) { PBEKeySpec keySpec = ( PBEKeySpec ) factory.getKeySpec( ske.getSecretKey(), PBEKeySpec.class ); if ( keySpec != null ) { char[] password = keySpec.getPassword(); if ( password != null ) { return new String( password ); } } } } catch ( Exception e ) { return null; } } return null; } /** * Resets the keystore manager. */ public void reset() { // Reseting the fields this.keystore = null; this.masterPassword = null; // Getting the keystore file File keystoreFile = getKeyStoreFile(); // If the keystore file exists, we need to remove it if ( keystoreFile.exists() ) { // Deleting the file FileUtils.deleteQuietly( keystoreFile ); } } public void reload( String masterPassword ) throws KeyStoreException { // Reseting the fields this.keystore = null; this.masterPassword = null; load( masterPassword ); } /** * Gets the master password. * * @return the master password */ public String getMasterPassword() { return masterPassword; } }