netinf.common.security.identity.impl.IdentityManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for netinf.common.security.identity.impl.IdentityManagerImpl.java

Source

/*
 * Copyright (C) 2009-2011 University of Paderborn, Computer Networks Group
 * (Full list of owners see http://www.netinf.org/about-2/license)
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of the University of Paderborn nor the names of its contributors may be used to endorse
 *       or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package netinf.common.security.identity.impl;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import netinf.common.datamodel.DatamodelFactory;
import netinf.common.datamodel.attribute.DefinedAttributeIdentification;
import netinf.common.datamodel.creator.ValidCreator;
import netinf.common.datamodel.identity.IdentityObject;
import netinf.common.exceptions.NetInfCheckedException;
import netinf.common.security.CryptoAlgorithm;
import netinf.common.security.SignatureAlgorithm;
import netinf.common.security.identity.IdentityManager;
import netinf.common.security.impl.IntegrityImpl;
import netinf.common.utils.Utils;

import org.apache.commons.lang.NotImplementedException;
import org.apache.log4j.Logger;

import com.google.inject.Inject;

/**
 * The Class IdentityManagerImpl. {@link IdentityManagerImpl} implements the {@link IdentityManager} interface. It uses local
 * files to store the Private Keys persistently. If not used as Singleton, pay high attention to use different paths for the file
 * used to store Private Keys in ( {@link IdentityManager#setFilePath(String)})
 * 
 * @author PG Augnet 2, University of Paderborn
 */
public class IdentityManagerImpl implements netinf.common.security.identity.IdentityManager {

    private static final Logger LOG = Logger.getLogger(IntegrityImpl.class);

    /**
     * Maps Identity (as a String, e.g. IdO-Identifier/PUBLIC_MASTER_KEY) to according PrivateKey
     */
    private Map<String, PrivateKey> privateKeys;
    /**
     * In case of multiple Identity Stores being managed, this Map maps the UserSession to the related Identity->Private Key Map
     * "privateKeys"
     */
    private final Map<UserSession, Map<String, PrivateKey>> sessions = new HashMap<UserSession, Map<String, PrivateKey>>();

    /**
     * Default Password (symmetric) used to encrypt/decrypt key store file
     */
    private String defaultPassword = "passwordpasswordpassword";
    /**
     * Default path to key store file
     */
    private String defaultFilepath = "../configs/Identities/privateKeyFile.pkf";
    /**
     * Default algorithm used to encrypt Private Keys in key store file
     */
    private final String defaultKeyAlgorithmName = "DESede";

    private final CryptoAlgorithm cryptoAlgorithm;

    @Inject
    public IdentityManagerImpl(DatamodelFactory datamodelFactory, SignatureAlgorithm signatureAlgorithm,
            CryptoAlgorithm cryptoAlgorithm) {
        this.cryptoAlgorithm = cryptoAlgorithm;
        this.privateKeys = new HashMap<String, PrivateKey>();
    }

    /**
     * @see IdentityManager#createNewMasterIdentity()
     */
    @Override
    public IdentityObject createNewMasterIdentity() throws NetInfCheckedException {
        // as soon as a new Master Identity is created, all Identites known to this IdentityManager will be stored to file. Thus,
        // ensure that identities are loaded from file before
        if (this.privateKeys.size() == 0) {
            try {
                loadIdentities();
            } catch (NetInfCheckedException e) {
                LOG.warn("Unable to load key file. " + e.getMessage());
            }
        }

        KeyPairGenerator k;
        try {
            k = KeyPairGenerator.getInstance("RSA");

        } catch (Exception e) {
            LOG.warn(e.getMessage());
            return null;
        }

        k.initialize(1024);
        KeyPair pair = k.generateKeyPair();

        // A new Master Identity implies a new Identity Object. Create it
        IdentityObject newIdentity = ValidCreator.createValidIdentityObject(pair.getPublic());

        // Derive the "Identity-Path"
        String pathToKey = newIdentity.getIdentifier().toString() + IntegrityImpl.PATH_SEPERATOR
                + DefinedAttributeIdentification.PUBLIC_KEY.getURI();

        this.privateKeys.put(pathToKey, pair.getPrivate());

        LOG.info("Private Key: " + Utils.objectToString(pair.getPrivate()));
        LOG.info("Public Key: " + Utils.objectToString(pair.getPublic()));

        // save private keys to file
        writePrivateKeysToFile(this.defaultFilepath, this.defaultKeyAlgorithmName, this.defaultPassword);

        return newIdentity;
    }

    /**
     * @see IdentityManager#createNewSubKey(IdentityObject, String)
     */
    @Override
    public String createNewSubKey(IdentityObject identityObject, String path) {
        throw new NotImplementedException();
    }

    /**
     * @see IdentityManager#getLocalIdentity()
     */
    @Override
    public String getLocalIdentity() throws NetInfCheckedException {
        if (this.privateKeys.size() == 0) {
            try {
                loadIdentities();
            } catch (NetInfCheckedException e) {
                LOG.warn("Unable to load key file. " + e.getMessage());
            }
        }

        // TODO More sophisticated choice of local identity could be implemented
        if (this.privateKeys.size() > 0) {
            return this.privateKeys.keySet().iterator().next();
        }

        throw new NetInfCheckedException("No local identity present.");
    }

    /**
     * @see IdentityManager#getPrivateKey(String)
     */
    @Override
    public PrivateKey getPrivateKey(String pathToPublicKey) throws NetInfCheckedException {
        return getPrivateKey(pathToPublicKey, null, null);
    }

    /**
     * @see IdentityManager#getPrivateKey(String, String, String)
     */
    @Override
    public PrivateKey getPrivateKey(String pathToPublicKey, String userName, String privateKey)
            throws NetInfCheckedException {
        if (userName != null && privateKey != null) {
            UserSession session = new UserSession(userName, privateKey);
            if (!this.sessions.containsKey(session)) {
                this.sessions.put(session, getPrivateKeysFromFile(userName + ".pkf", privateKey));
            }
            Map<String, PrivateKey> map = this.sessions.get(session);

            PrivateKey privKey = map.get(pathToPublicKey);
            if (privKey == null) {
                throw new NetInfCheckedException("Private Key not found.");
            }

            return privKey;

        } else {
            if (this.privateKeys.size() == 0) {
                try {
                    loadIdentities();
                } catch (NetInfCheckedException e) {
                    LOG.warn("Unable to load key file. " + e.getMessage());
                }
            }

            PrivateKey privKey = this.privateKeys.get(pathToPublicKey);
            if (privKey == null) {
                throw new NetInfCheckedException("Private Key not found.");
            }

            return privKey;
        }
    }

    /**
     * Writes all the Private Keys this IdentityManager knows to a file encrypted by a Secret Key (symmetric)
     * 
     * @param filepath
     *           Path to file where Private Keys shall be stored
     * @param algorithmUsedToEncryptTheKey
     *           Algorithm used to encrypt the Private Key
     * @param password
     *           Password used to encrypt the Private Keys
     * @throws NetInfCheckedException
     */
    private void writePrivateKeysToFile(String filepath, String algorithmUsedToEncryptTheKey, String password)
            throws NetInfCheckedException {
        BufferedWriter bw;
        try {
            bw = new BufferedWriter(new FileWriter(filepath));
        } catch (FileNotFoundException e) {
            LOG.error(e.getMessage());
            throw new NetInfCheckedException(e.getMessage());
        } catch (IOException e) {
            LOG.error(e.getMessage());
            throw new NetInfCheckedException(e.getMessage());
        }

        Iterator<Entry<String, PrivateKey>> iterator = this.privateKeys.entrySet().iterator();
        while (iterator.hasNext()) {
            java.util.Map.Entry<String, PrivateKey> entry = iterator.next();

            try {
                // append alias
                bw.write(entry.getKey());
                bw.newLine();
                // append algorithm used to encrypt the key
                bw.write(algorithmUsedToEncryptTheKey);
                bw.newLine();
                // append algorithm key is used for
                bw.write(entry.getValue().getAlgorithm());
                bw.newLine();
                // append encrypted private key

                String encryptedPrivateKey = this.cryptoAlgorithm.encryptPrivateKey(algorithmUsedToEncryptTheKey,
                        this.cryptoAlgorithm.getSecretKeyFromString(algorithmUsedToEncryptTheKey, password),
                        entry.getValue());
                char[] encryptedPrivateKeyChars = encryptedPrivateKey.toCharArray();

                for (int i = 0; i < encryptedPrivateKeyChars.length; i++) {
                    bw.write("" + ((int) encryptedPrivateKeyChars[i]));
                    bw.newLine();
                }

                bw.write("END OF KEY");
                bw.newLine();

            } catch (IOException e) {
                LOG.warn("PrivateKey couldn't be saved to file. " + e.getMessage());
            } finally {
                try {
                    bw.flush();
                } catch (IOException e) {
                    LOG.warn("Error writing to file. " + e.getMessage());
                    throw new NetInfCheckedException("Error writing to file.");
                }
            }
        }
        try {
            bw.close();
        } catch (IOException e) {
            LOG.warn("Unable to close file. " + e.getMessage());
            throw new NetInfCheckedException("Unable to close file.");
        }
    }

    /**
     * Retrieves the Private Keys from a local file
     * 
     * @param filepath
     *           Path of file the Private Keys are stored in
     * @param password
     *           Secret Key (symmetric) used to encrypt the Private Keys
     * @return Map of String (Path to Identity, e.g. IdO-Identifier/PublicMasterKey) and according Private Key
     * @throws NetInfCheckedException
     */
    private Map<String, PrivateKey> getPrivateKeysFromFile(String filepath, String password)
            throws NetInfCheckedException {
        BufferedReader br;
        try {
            br = new BufferedReader(new FileReader(filepath));
        } catch (FileNotFoundException e) {
            LOG.error(e.getMessage());
            throw new NetInfCheckedException(e.getMessage());
        }

        try {
            Map<String, PrivateKey> privateKeys = new HashMap<String, PrivateKey>();
            String alias;
            String algorithmUsedToEncryptTheKey;
            String algorithmKeyIsUsedFor;
            String encryptedPrivateKey;

            while (true) {
                alias = br.readLine();
                algorithmUsedToEncryptTheKey = br.readLine();
                algorithmKeyIsUsedFor = br.readLine();
                /*
                 * encryptedPrivateKey = br.readLine(); encryptedPrivateKey =
                 * encryptedPrivateKey.replaceAll(REPLACEMENT_CARRIAGE_RETURN, "\r").replaceAll( REPLACEMENT_NEW_LINE, "\n");
                 */
                encryptedPrivateKey = new String();
                String line = br.readLine();
                while (!line.equals("END OF KEY")) {
                    encryptedPrivateKey += ((char) Integer.parseInt(line));
                    line = br.readLine();
                }

                PrivateKey privKey = this.cryptoAlgorithm.decryptPrivateKey(algorithmUsedToEncryptTheKey,
                        algorithmKeyIsUsedFor,
                        this.cryptoAlgorithm.getSecretKeyFromString(algorithmUsedToEncryptTheKey, password),
                        encryptedPrivateKey);

                privateKeys.put(alias, privKey);

                return privateKeys;
            }
        } catch (Exception e) {
            LOG.error("Unable to read keys from file. " + e.getMessage());
            throw new NetInfCheckedException("Unable to read keys from file. " + e.getMessage());
        }

    }

    /**
     * @see IdentityManager#hasPrivateKey(String)
     */
    @Override
    public boolean hasPrivateKey(String pathToPublicKey) {
        return hasPrivateKey(pathToPublicKey, null, null);
    }

    /**
     * @see IdentityManager#hasPrivateKey(String, String, String)
     */
    @Override
    public boolean hasPrivateKey(String pathToPublicKey, String userName, String privateKey) {
        if (userName != null && privateKey != null) {
            UserSession session = new UserSession(userName, privateKey);
            if (!this.sessions.containsKey(session)) {
                try {
                    this.sessions.put(session, getPrivateKeysFromFile(userName + ".pkf", privateKey));
                } catch (NetInfCheckedException e) {
                    LOG.warn("Unable to load key file. " + e.getMessage());
                    return false;
                }
            }
            Map<String, PrivateKey> map = this.sessions.get(session);

            return map.containsKey(pathToPublicKey);

        } else {
            if (this.privateKeys.size() == 0) {
                try {
                    loadIdentities();
                } catch (NetInfCheckedException e) {
                    LOG.warn("Unable to load key file. " + e.getMessage());
                }
            }

            return this.privateKeys.containsKey(pathToPublicKey);
        }
    }

    /**
     * @see IdentityManager#loadIdentities()
     */
    @Override
    public void loadIdentities() throws NetInfCheckedException {
        this.privateKeys = getPrivateKeysFromFile(this.defaultFilepath, this.defaultPassword);
    }

    /**
     * @see IdentityManager#setFilePath(String)
     */
    @Override
    public void setFilePath(String path) {
        this.defaultFilepath = path;
    }

    /**
     * @see IdentityManager#setPassword(String)
     */
    @Override
    public void setPassword(String password) {
        this.defaultPassword = password;
    }

    /**
     * @see IdentityManager#storeIdentities()
     */
    @Override
    public void storeIdentities() throws NetInfCheckedException {
        writePrivateKeysToFile(this.defaultFilepath, this.defaultKeyAlgorithmName, this.defaultPassword);
    }
}

/**
 * To manage several files containing Private Keys, the {@link UserSession} stores user names and their password to decrypt their
 * Private Key file
 */
class UserSession {
    private final String userName;
    private final String privateKey;

    public UserSession(String userName, String privateKey) {
        this.userName = userName;
        this.privateKey = privateKey;
    }

    public String getUserName() {
        return this.userName;
    }

    public String getPrivateKey() {
        return this.privateKey;
    }

    @Override
    public int hashCode() {
        if (this.userName == null) {
            return 0;
        }

        return this.userName.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof UserSession) {
            UserSession session = (UserSession) o;
            return session.userName.equals(this.userName) && session.privateKey.equals(this.privateKey);
        }
        return false;
    }

}