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}