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

net.east301.keyring.windows.WindowsDPAPIBackend Maven / Gradle / Ivy

The newest version!
/**
 * @author  $Author: east301 $
 * @date    $Date: 2012/10/21 11:18:21 $
 * @version $Revision: fb93b8608b64 $
 */

package net.east301.keyring.windows;

import com.sun.jna.Platform;
import com.sun.jna.platform.win32.Crypt32Util;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.east301.keyring.KeyringBackend;
import net.east301.keyring.PasswordRetrievalException;
import net.east301.keyring.PasswordSaveException;
import net.east301.keyring.util.FileBasedLock;
import net.east301.keyring.util.LockException;

/**
 * Keyring backend which uses Windows DPAPI
 */
public class WindowsDPAPIBackend extends KeyringBackend {

    /**
     * Returns true when the backend is supported
     */
    @Override
    public boolean isSupported() {
        return Platform.isWindows();
    }

    /**
     * Returns true if the backend directory uses some file to store passwords
     */
    @Override
    public boolean isKeyStorePathRequired() {
        return true;
    }

    /**
     * Gets password from key store
     *
     * @param service   Service name
     * @param account   Account name
     *
     * @return  Password related to specified service and account
     *
     * @throws PasswordRetrievalException   Thrown when an error happened while getting password
     */
    @Override
    public String getPassword(String service, String account)
            throws LockException, PasswordRetrievalException {

        FileBasedLock fileLock = new FileBasedLock(getLockPath());

        try {
            //
            fileLock.lock();

            //
            PasswordEntry targetEntry = null;

            for (PasswordEntry entry : loadPasswordEntries()) {
                if (entry.getService().equals(service) && entry.getAccount().equals(account)) {
                    targetEntry = entry;
                    break;
                }
            }

            if (targetEntry == null) {
                throw new PasswordRetrievalException(
                        "Password related to the specified service and account is not found");
            }

            //
            byte[] decryptedBytes;

            try {
                decryptedBytes = Crypt32Util.cryptUnprotectData(targetEntry.getPassword());
            } catch (Exception ex) {
                throw new PasswordRetrievalException("Failed to decrypt password");
            }

            //
            try {
                return new String(decryptedBytes, "UTF-8");
            } catch (UnsupportedEncodingException ex) {
                throw new PasswordRetrievalException("Unsupported encoding 'UTF-8' specified");
            }

        } finally {
            try {
                fileLock.release();
            } catch (Exception ex) {
                Logger.getLogger(WindowsDPAPIBackend.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Sets password to key store
     *
     * @param service   Service name
     * @param account   Account name
     * @param password  Password
     *
     * @throws PasswordSaveException    Thrown when an error happened while saving the password
     */
    @Override
    public void setPassword(String service, String account, String password)
            throws LockException, PasswordSaveException {

        FileBasedLock fileLock = new FileBasedLock(getLockPath());

        try {
            //
            fileLock.lock();

            //
            byte[] encryptedBytes;

            try {
                encryptedBytes = Crypt32Util.cryptProtectData(password.getBytes("UTF-8"));
            } catch (UnsupportedEncodingException ex) {
                throw new PasswordSaveException("Unsupported encoding 'UTF-8' specified");
            } catch (Exception ex) {
                throw new PasswordSaveException("Failed to encrypt password");
            }

            //
            ArrayList entries = loadPasswordEntries();
            PasswordEntry targetEntry = null;

            for (PasswordEntry entry : entries) {
                if (entry.getService().equals(service) && entry.getAccount().equals(account)) {
                    targetEntry = entry;
                    break;
                }
            }

            if (targetEntry != null) {
                targetEntry.setPassword(encryptedBytes);
            } else {
                entries.add(new PasswordEntry(service, account, encryptedBytes));
            }

            //
            savePasswordEnetires(entries);
        } finally {
            try {
                fileLock.release();
            } catch (Exception ex) {
                Logger.getLogger(WindowsDPAPIBackend.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Gets backend ID
     */
    @Override
    public String getID() {
        return "WindowsDPAPI";
    }

    /**
     * Returns path to a file for lock
     */
    public String getLockPath() {
        return m_keyStorePath + ".lock";
    }

    /**
     * Loads password entries to a file.
     * This method is not thread/process safe.
     */
    private ArrayList loadPasswordEntries() {
        ArrayList entries = new ArrayList();

        try {
            FileInputStream fin = new FileInputStream(m_keyStorePath);
            ObjectInputStream oin = new ObjectInputStream(fin);

            entries.addAll(Arrays.asList((PasswordEntry[])oin.readObject()));
        } catch (Exception ex) {
            Logger.getLogger(WindowsDPAPIBackend.class.getName()).log(Level.SEVERE, null, ex);
        }

        return entries;
    }

    /**
     * Saves password entries to a file
     * This method is not thread/process safe.
     *
     * @param entries   Password entries to be saved
     *
     * @throws PasswordSaveException    Thrown when an error happened while writing to a file
     */
    private void savePasswordEnetires(ArrayList entries)
            throws PasswordSaveException {

        try {
            FileOutputStream fout = new FileOutputStream(m_keyStorePath);
            ObjectOutputStream oout = new ObjectOutputStream(fout);

            oout.writeObject(entries.toArray(new PasswordEntry[0]));
        } catch (Exception ex) {
            Logger.getLogger(WindowsDPAPIBackend.class.getName()).log(Level.SEVERE, null, ex);
            throw new PasswordSaveException("Failed to save password entries to a file");
        }
    }

} // class WindowsDPAPIBackend




© 2015 - 2024 Weber Informatics LLC | Privacy Policy