001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package  org.jpos.security;
020
021import java.io.BufferedInputStream;
022import org.jpos.core.Configurable;
023import org.jpos.core.Configuration;
024import org.jpos.core.ConfigurationException;
025import org.jpos.iso.ISOUtil;
026import org.jpos.util.LogEvent;
027import org.jpos.util.LogSource;
028import org.jpos.util.Logger;
029
030import java.io.File;
031import java.io.FileInputStream;
032import java.io.FileOutputStream;
033import java.io.InputStream;
034import java.util.HashMap;
035import java.util.Map;
036import java.util.Properties;
037
038
039/**
040 * Implements SecureKeyStore using a properties file.
041 * @author Hani S. Kirollos
042 * @version $Revision$ $Date$
043 * @see java.util.Properties
044 */
045public class SimpleKeyFile
046        implements SecureKeyStore, Configurable, LogSource  {
047    Properties props = new Properties();
048    File file;
049    String header = "Key File";
050    protected Logger logger = null;
051    protected String realm = null;
052
053    public SimpleKeyFile () {
054    }
055
056    public SimpleKeyFile (String keyFileName) throws SecureKeyStoreException
057    {
058        init(keyFileName);
059    }
060
061    public void init (String keyFileName) throws SecureKeyStoreException {
062        file = new File(keyFileName);
063        try {
064            if (!file.exists())
065                file.createNewFile();
066            load();
067        } catch (Exception e) {
068            throw  new SecureKeyStoreException(e);
069        }
070    }
071
072    @Override
073    public void setLogger (Logger logger, String realm) {
074        this.logger = logger;
075        this.realm = realm;
076    }
077
078    @Override
079    public Logger getLogger () {
080        return  logger;
081    }
082
083    @Override
084    public String getRealm () {
085        return  realm;
086    }
087
088    /**
089     *
090     * @param cfg configuration object
091     * @throws ConfigurationException
092     */
093    @Override
094    public void setConfiguration (Configuration cfg) throws ConfigurationException {
095        try {
096            init(cfg.get("key-file"));
097            header = cfg.get("file-header", header);
098        } catch (Exception e) {
099            throw  new ConfigurationException(e);
100        }
101    }
102
103    @Override
104    public synchronized SecureKey getKey (String alias) throws SecureKeyStoreException {
105        SecureKey secureKey = null;
106        LogEvent evt = logger != null ? new LogEvent(this, "get-key") : null;
107        if (evt != null)
108            evt.addMessage("alias", alias);
109        try {
110            load();
111            String keyClassName = getProperty(alias, "class");
112            Class c = Class.forName(keyClassName);
113            secureKey = (SecureKey)c.newInstance();
114            if (!(secureKey instanceof SecureDESKey))
115                throw  new SecureKeyStoreException("Unsupported SecureKey class: " +
116                        secureKey.getClass().getName());
117            byte[] keyBytes = ISOUtil.hex2byte(getProperty(alias, "key"));
118            short keyLength = Short.parseShort(getProperty(alias, "length"));
119            String keyType = getProperty(alias, "type");
120            byte[] KeyCheckValue = ISOUtil.hex2byte(getProperty(alias, "checkvalue"));
121            secureKey = new SecureDESKey(keyLength, keyType, keyBytes, KeyCheckValue);
122            if (evt != null)
123                evt.addMessage(secureKey);
124        } catch (Exception e) {
125            if (evt == null) // this is an exception, we want to log it, even if we don't have an assigned logger
126                evt = new LogEvent(this, "get-key-error", alias);
127            evt.addMessage(e);
128            throw  e instanceof SecureKeyStoreException ? (SecureKeyStoreException) e : new SecureKeyStoreException(e);
129        } finally {
130            if (evt != null)
131                Logger.log(evt);
132        }
133        return  secureKey;
134    }
135
136    @Override
137    public synchronized void setKey (String alias, SecureKey secureKey) throws SecureKeyStoreException {
138        LogEvent evt = new LogEvent(this, "set-key");
139        evt.addMessage("alias", alias);
140        evt.addMessage(secureKey);
141        try {
142            if (!(secureKey instanceof SecureDESKey))
143                throw  new SecureKeyStoreException("Unsupported SecureKey class: " +
144                        secureKey.getClass().getName());
145            load();                 // load new changes (possibly made manually on the file)
146            setProperty(alias, "class", secureKey.getClass().getName());
147            setProperty(alias, "key", ISOUtil.hexString(secureKey.getKeyBytes()));
148            setProperty(alias, "length",  Short.toString(secureKey.getKeyLength()));
149            setProperty(alias, "type", secureKey.getKeyType());
150            String keyCheckValueHexString = ISOUtil.hexString(((SecureDESKey)secureKey).getKeyCheckValue());
151            setProperty(alias, "checkvalue", keyCheckValueHexString);
152            store();
153        } catch (Exception e) {
154            evt.addMessage(e);
155            throw  e instanceof SecureKeyStoreException ? (SecureKeyStoreException) e : new SecureKeyStoreException(e);
156        } finally {
157            Logger.log(evt);
158        }
159    }
160
161    void load () throws SecureKeyStoreException {
162        InputStream in;
163        try {
164            if (!file.canRead())
165                throw  new SecureKeyStoreException("Can't read from file: " + file.getCanonicalPath());
166            in = new BufferedInputStream(new FileInputStream(file));
167            try {
168                props.load(in);
169            } finally {
170                in.close();
171            }
172        } catch (Exception e) {
173            throw  new SecureKeyStoreException(e);
174        }
175    }
176
177    void store () throws SecureKeyStoreException {
178        FileOutputStream out;
179        try {
180            if (!file.canWrite())
181                throw  new SecureKeyStoreException("Can't write to file: " + file.getCanonicalPath());
182            out = new FileOutputStream(file);
183            props.store(out, header);
184            out.flush();
185            out.close();
186        } catch (Exception e) {
187            throw  new SecureKeyStoreException(e);
188        }
189    }
190
191    public String getProperty (String alias, String subName) throws SecureKeyStoreException {
192        // key here has nothing to do with cryptographic keys
193        String key = alias + "." + subName;
194        String value = props.getProperty(key);
195        if (value == null)
196            throw  new SecureKeyStoreException("Key can't be retrieved. Can't get property: " + key);
197        return value.trim();
198    }
199
200    public void setProperty (String alias, String subName, String value) {
201        // key here has nothing to do with cryptographic keys
202        String key = alias + "." + subName;
203        props.setProperty(key, value);
204    }
205
206    @Override
207    public Map<String, SecureKey> getKeys() throws SecureKeyStoreException {
208        Map<String, SecureKey> keys = new HashMap<>();
209        for (Object k : props.keySet()) {
210            String keyStr = (String) k;
211            String alias = keyStr.substring(0, keyStr.lastIndexOf('.'));
212            if (!keys.containsKey(alias)) {
213                keys.put(alias, getKey(alias));
214            }
215        }
216        return keys;
217    }
218}