From b6af6a34890599e1efb4a81c68cc5e49a97a04f5 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Wed, 6 Feb 2013 18:03:42 +0800 Subject: [PATCH] IDEMPIERE-613 Column Encryption Enhancement. --- .../compiere/process/ColumnEncryption.java | 119 ++++++++------- org.adempiere.base/OSGI-INF/keystore.xml | 7 + .../src/org/adempiere/base/Core.java | 8 + .../src/org/adempiere/base/IKeyStore.java | 39 +++++ .../src/org/compiere/model/GridTable.java | 30 ++-- .../src/org/compiere/model/PO.java | 10 +- .../org/compiere/util/DefaultKeyStore.java | 137 ++++++++++++++++++ .../src/org/compiere/util/Ini.java | 6 +- .../src/org/compiere/util/Login.java | 54 +++---- .../src/org/compiere/util/Secure.java | 127 ++++++++-------- .../src/org/compiere/util/SecureEngine.java | 32 ++-- .../org/compiere/util/SecureInterface.java | 23 ++- .../src/org/compiere/util/WebUser.java | 15 +- .../adempiere/webui/window/FindWindow.java | 2 +- 14 files changed, 404 insertions(+), 205 deletions(-) create mode 100644 org.adempiere.base/OSGI-INF/keystore.xml create mode 100644 org.adempiere.base/src/org/adempiere/base/IKeyStore.java create mode 100644 org.adempiere.base/src/org/compiere/util/DefaultKeyStore.java diff --git a/org.adempiere.base.process/src/org/compiere/process/ColumnEncryption.java b/org.adempiere.base.process/src/org/compiere/process/ColumnEncryption.java index 0823509493..b4bd4fa88f 100644 --- a/org.adempiere.base.process/src/org/compiere/process/ColumnEncryption.java +++ b/org.adempiere.base.process/src/org/compiere/process/ColumnEncryption.java @@ -117,7 +117,7 @@ public class ColumnEncryption extends SvrProcess { if (column.isKey() || column.isParent() || column.isStandardColumn() || column.isVirtualColumn() || column.isIdentifier() || column.isTranslated() || DisplayType.isLookup(dt) - || DisplayType.isLOB(dt) + || DisplayType.isLOB(dt) || DisplayType.isDate(dt) || DisplayType.isNumeric(dt) || "DocumentNo".equalsIgnoreCase(column.getColumnName()) || "Value".equalsIgnoreCase(column.getColumnName()) || "Name".equalsIgnoreCase(column.getColumnName())) { @@ -126,7 +126,7 @@ public class ColumnEncryption extends SvrProcess { column.saveEx(); } StringBuilder msgreturn = new StringBuilder().append(columnName).append(": cannot be encrypted"); - return msgreturn.toString(); + throw new Exception(msgreturn.toString()); } // Start @@ -137,10 +137,10 @@ public class ColumnEncryption extends SvrProcess { // Test Value if (p_TestValue != null && p_TestValue.length() > 0) { - String encString = SecureEngine.encrypt(p_TestValue); + String encString = SecureEngine.encrypt(p_TestValue, 0); msglog = new StringBuilder("Encrypted Test Value=").append(encString); addLog(0, null, null, msglog.toString()); - String clearString = SecureEngine.decrypt(encString); + String clearString = SecureEngine.decrypt(encString, 0); if (p_TestValue.equals(clearString)){ msglog = new StringBuilder("Decrypted=").append(clearString) .append(" (same as test value)"); @@ -181,7 +181,7 @@ public class ColumnEncryption extends SvrProcess { .append("Test=").append(testClear.toString()).append(" (").append(p_MaxLength).append(")"); log.config(msglog.toString()); // - String encString = SecureEngine.encrypt(testClear.toString()); + String encString = SecureEngine.encrypt(testClear.toString(), 0); int encLength = encString.length(); msglog = new StringBuilder("Test Max Length=").append(testClear.length()) .append(" -> ").append(encLength); @@ -224,7 +224,7 @@ public class ColumnEncryption extends SvrProcess { // Check if the encryption exceeds the current length. int oldLength = column.getFieldLength(); int newLength = encryptedColumnLength(oldLength); - if (newLength > oldLength) + if (newLength > oldLength) { if (changeFieldLength(columnID, columnName, newLength, tableName) == -1) { log.warning("EncryptError [ChangeFieldLength]: " @@ -232,6 +232,7 @@ public class ColumnEncryption extends SvrProcess { + newLength); throw new Exception(); } + } // Encrypt column contents. if (encryptColumnContents(columnName, column.getAD_Table_ID()) == -1) { @@ -295,7 +296,7 @@ public class ColumnEncryption extends SvrProcess { StringBuilder idColumnName = new StringBuilder(tableName).append("_ID"); StringBuilder selectSql = new StringBuilder(); - selectSql.append("SELECT ").append(idColumnName).append(",").append(columnName); + selectSql.append("SELECT ").append(idColumnName).append(",").append(columnName).append(",AD_Client_ID"); selectSql.append(" FROM ").append(tableName); selectSql.append(" ORDER BY ").append(idColumnName); @@ -306,32 +307,36 @@ public class ColumnEncryption extends SvrProcess { PreparedStatement selectStmt = null; PreparedStatement updateStmt = null; - - selectStmt = m_conn.prepareStatement(selectSql.toString(), - ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); - updateStmt = m_conn.prepareStatement(updateSql.toString()); - - ResultSet rs = selectStmt.executeQuery(); - - for (recordsEncrypted = 0; rs.next(); ++recordsEncrypted) { - // Get the row id and column value - int id = rs.getInt(1); - String value = rs.getString(2); - // Encrypt the value - value = SecureEngine.encrypt(value); - // Update the row - updateStmt.setString(1, value); - updateStmt.setInt(2, id); - if (updateStmt.executeUpdate() != 1) { - log.warning("EncryptError: Table=" + tableName + ", ID=" + id); - throw new Exception(); + ResultSet rs = null; + + try { + selectStmt = m_conn.prepareStatement(selectSql.toString(), + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + updateStmt = m_conn.prepareStatement(updateSql.toString()); + + rs = selectStmt.executeQuery(); + + for (recordsEncrypted = 0; rs.next(); ++recordsEncrypted) { + // Get the row id and column value + int id = rs.getInt(1); + String value = rs.getString(2); + int AD_Client_ID = rs.getInt(3); + // Encrypt the value + value = SecureEngine.encrypt(value, AD_Client_ID); + // Update the row + updateStmt.setString(1, value); + updateStmt.setInt(2, id); + if (updateStmt.executeUpdate() != 1) { + log.severe("EncryptError: Table=" + tableName + ", ID=" + id); + throw new Exception(); + } } + } finally { + DB.close(rs); + DB.close(selectStmt); + DB.close(updateStmt); } - rs.close(); - selectStmt.close(); - updateStmt.close(); - return recordsEncrypted; } // encryptColumnContents @@ -348,7 +353,7 @@ public class ColumnEncryption extends SvrProcess { for (int i = 0; i < colLength; i++) { str.append("1"); } - str = new StringBuilder().append(SecureEngine.encrypt(str.toString())); + str = new StringBuilder().append(SecureEngine.encrypt(str.toString(), 0)); return str.length(); } // encryptedColumnLength @@ -387,34 +392,36 @@ public class ColumnEncryption extends SvrProcess { updateSql.append(" WHERE AD_Column_ID=").append(columnID); PreparedStatement selectStmt = null; - - selectStmt = m_conn.prepareStatement(selectSql.toString(), - ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); - - selectStmt.setInt(1, columnID); - ResultSet rs = selectStmt.executeQuery(); - - if (rs.next()) { - // Change the column size physically. - if (DB.executeUpdate(alterSql.toString(), false, m_trx - .getTrxName()) == -1) { - log.warning("EncryptError [ChangeFieldLength]: ColumnID=" - + columnID + ", NewLength=" + length); - throw new Exception(); - } - - // Change the column size in AD. - if (DB.executeUpdate(updateSql.toString(), false, m_trx - .getTrxName()) == -1) { - log.warning("EncryptError [ChangeFieldLength]: ColumnID=" - + columnID + ", NewLength=" + length); - throw new Exception(); + ResultSet rs = null; + + try { + selectStmt = m_conn.prepareStatement(selectSql.toString(), + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + + selectStmt.setInt(1, columnID); + rs = selectStmt.executeQuery(); + + if (rs.next()) { + // Change the column size physically. + if (DB.executeUpdate(alterSql.toString(), false, m_trx + .getTrxName()) == -1) { + log.severe("EncryptError [ChangeFieldLength]: ColumnID=" + + columnID + ", NewLength=" + length); + throw new Exception(); + } + + // Change the column size in AD. + if (DB.executeUpdate(updateSql.toString(), false, m_trx + .getTrxName()) == -1) { + log.severe("EncryptError [ChangeFieldLength]: ColumnID=" + + columnID + ", NewLength=" + length); + throw new Exception(); + } } + } finally { + DB.close(rs, selectStmt); } - rs.close(); - selectStmt.close(); - // Update number of rows effected. rowsEffected++; diff --git a/org.adempiere.base/OSGI-INF/keystore.xml b/org.adempiere.base/OSGI-INF/keystore.xml new file mode 100644 index 0000000000..eb288e633e --- /dev/null +++ b/org.adempiere.base/OSGI-INF/keystore.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.adempiere.base/src/org/adempiere/base/Core.java b/org.adempiere.base/src/org/adempiere/base/Core.java index b13bf38eb5..14302b9757 100644 --- a/org.adempiere.base/src/org/adempiere/base/Core.java +++ b/org.adempiere.base/src/org/adempiere/base/Core.java @@ -104,6 +104,14 @@ public class Core { return Service.locator().locate(ModelValidator.class, "org.adempiere.base.ModelValidator", serviceId, null).getService(); } + /** + * + * @return keystore + */ + public static IKeyStore getKeyStore(){ + return Service.locator().locate(IKeyStore.class).getService(); + } + /** * Get payment processor instance * @param mbap payment processor model diff --git a/org.adempiere.base/src/org/adempiere/base/IKeyStore.java b/org.adempiere.base/src/org/adempiere/base/IKeyStore.java new file mode 100644 index 0000000000..5c14f484d2 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/base/IKeyStore.java @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (C) 2013 Deepak * + * Copyright (C) 2013 Trek Global * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ +package org.adempiere.base; + +import javax.crypto.SecretKey; + +/** + * + * @author deepak + * + */ +public interface IKeyStore { + + /** + * + * @param AD_Client_ID + * @return secret key + */ + public SecretKey getKey(int AD_Client_ID); + + /** + * @return encryption algorithm id, for e.g AES + */ + public String getAlgorithm(); +} + + + diff --git a/org.adempiere.base/src/org/compiere/model/GridTable.java b/org.adempiere.base/src/org/compiere/model/GridTable.java index b1b557425c..aa95094c85 100644 --- a/org.adempiere.base/src/org/compiere/model/GridTable.java +++ b/org.adempiere.base/src/org/compiere/model/GridTable.java @@ -1814,7 +1814,7 @@ public class GridTable extends AbstractTableModel else iii = new Integer(dd.toString()); if (encrypted) - iii = (Integer)encrypt(iii); + iii = (Integer)encrypt(iii, getAD_Client_ID()); if (manualUpdate) createUpdateSql (columnName, String.valueOf (iii)); else @@ -1834,7 +1834,7 @@ public class GridTable extends AbstractTableModel { BigDecimal bd = (BigDecimal)rowData[col]; if (encrypted) - bd = (BigDecimal)encrypt(bd); + bd = (BigDecimal)encrypt(bd, getAD_Client_ID()); if (manualUpdate) createUpdateSql (columnName, bd.toString ()); else @@ -1846,7 +1846,7 @@ public class GridTable extends AbstractTableModel { Timestamp ts = (Timestamp)rowData[col]; if (encrypted) - ts = (Timestamp)encrypt(ts); + ts = (Timestamp)encrypt(ts, getAD_Client_ID()); if (manualUpdate) createUpdateSql (columnName, DB.TO_DATE (ts, false)); else @@ -1884,7 +1884,7 @@ public class GridTable extends AbstractTableModel { String str = rowData[col].toString (); if (encrypted) - str = (String)encrypt(str); + str = (String)encrypt(str, getAD_Client_ID()); if (manualUpdate) createUpdateSql (columnName, DB.TO_STRING (str)); else @@ -3121,7 +3121,7 @@ public class GridTable extends AbstractTableModel { String str = rs.getString(j+1); if (field.isEncryptedColumn()) - str = (String)decrypt(str); + str = (String)decrypt(str, getAD_Client_ID()); rowData[j] = new Boolean ("Y".equals(str)); // Boolean } // LOB @@ -3152,7 +3152,7 @@ public class GridTable extends AbstractTableModel rowData[j] = rs.getString(j+1); // String // Encrypted if (field.isEncryptedColumn() && displayType != DisplayType.YesNo) - rowData[j] = decrypt(rowData[j]); + rowData[j] = decrypt(rowData[j], getAD_Client_ID()); } } catch (SQLException e) @@ -3167,11 +3167,11 @@ public class GridTable extends AbstractTableModel * @param xx clear data * @return encrypted value */ - private Object encrypt (Object xx) + private Object encrypt (Object xx, int AD_Client_ID) { if (xx == null) return null; - return SecureEngine.encrypt(xx); + return SecureEngine.encrypt(xx, AD_Client_ID); } // encrypt /** @@ -3179,13 +3179,23 @@ public class GridTable extends AbstractTableModel * @param yy encrypted data * @return clear data */ - private Object decrypt (Object yy) + private Object decrypt (Object yy, int AD_Client_ID) { if (yy == null) return null; - return SecureEngine.decrypt(yy); + return SecureEngine.decrypt(yy, AD_Client_ID); } // decrypt + private int getAD_Client_ID() + { + int AD_Client_ID = Env.getAD_Client_ID(Env.getCtx()); + GridField field = getField("AD_Client_ID"); + if (field != null && field.getValue() != null) { + AD_Client_ID = ((Number)field.getValue()).intValue(); + } + return AD_Client_ID; + } + /************************************************************************** * Remove Data Status Listener * @param l listener diff --git a/org.adempiere.base/src/org/compiere/model/PO.java b/org.adempiere.base/src/org/compiere/model/PO.java index 12d56add58..5feaf4f60e 100644 --- a/org.adempiere.base/src/org/compiere/model/PO.java +++ b/org.adempiere.base/src/org/compiere/model/PO.java @@ -3020,8 +3020,9 @@ public abstract class PO { if (xx == null) return null; - if (index != -1 && p_info.isEncrypted(index)) - return SecureEngine.encrypt(xx); + if (index != -1 && p_info.isEncrypted(index)) { + return SecureEngine.encrypt(xx, getAD_Client_ID()); + } return xx; } // encrypt @@ -3035,8 +3036,9 @@ public abstract class PO { if (yy == null) return null; - if (index != -1 && p_info.isEncrypted(index)) - return SecureEngine.decrypt(yy); + if (index != -1 && p_info.isEncrypted(index)) { + return SecureEngine.decrypt(yy, getAD_Client_ID()); + } return yy; } // decrypt diff --git a/org.adempiere.base/src/org/compiere/util/DefaultKeyStore.java b/org.adempiere.base/src/org/compiere/util/DefaultKeyStore.java new file mode 100644 index 0000000000..b2b6536549 --- /dev/null +++ b/org.adempiere.base/src/org/compiere/util/DefaultKeyStore.java @@ -0,0 +1,137 @@ +/****************************************************************************** + * Copyright (C) 2013 Deepak * + * Copyright (C) 2013 Heng Sin Low * + * Copyright (C) 2013 Trek Global * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ +package org.compiere.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.security.KeyStore; +import java.security.KeyStore.PasswordProtection; +import java.security.KeyStore.SecretKeyEntry; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +import org.adempiere.base.IKeyStore; + +/** + * @author deepak + * @author hengsin + * + */ +public class DefaultKeyStore implements IKeyStore { + + private static final String LEGACY_ALGORITHM = "DES"; + + private static final String IDEMPIERE_KEYSTORE_PROPERTIES = "idempiere-ks.properties"; + + private static final String IDEMPIERE_KEYSTORE = "idempiere.ks"; + + /** Logger */ + private static Logger log = Logger.getLogger (DefaultKeyStore.class.getName()); + + /** Adempiere Key */ + private SecretKey m_key = null; + + private KeyStore keyStore; + + private char[] password = null; + + private String algorithm; + + public DefaultKeyStore(){ + File file = new File(Ini.getAdempiereHome(), IDEMPIERE_KEYSTORE_PROPERTIES); + if (file.exists()) { + try{ + Properties p = new Properties(); + p.load(new FileInputStream(file)); + String s = p.getProperty("password"); + String a = p.getProperty("algorithm"); + if (!Util.isEmpty(s) && !Util.isEmpty(a)) { + password = s.toCharArray(); + algorithm = a; + keyStore = KeyStore.getInstance("JCEKS"); + file = new File(Ini.getAdempiereHome(), IDEMPIERE_KEYSTORE); + if (file.exists()) { + FileInputStream stream = new FileInputStream(file); + keyStore.load(stream, password ); + } else { + keyStore.load(null, password ); + } + } else { + createLegacyKey(); + } + } catch (Exception ex) { + log.log(Level.SEVERE, "", ex); + password = null; + createLegacyKey(); + } + } else { + createLegacyKey(); + } + } + + private void createLegacyKey() { + m_key = new javax.crypto.spec.SecretKeySpec + (new byte[] {100,25,28,-122,-26,94,-3,-26}, LEGACY_ALGORITHM); + } + + @Override + public SecretKey getKey(int AD_Client_ID) { + if (password != null) { + try { + PasswordProtection protParam = new PasswordProtection(password); + String alias = "ad_client_"+AD_Client_ID; + SecretKeyEntry entry = (SecretKeyEntry) keyStore.getEntry(alias, protParam); + if (entry == null) { + KeyGenerator generator = KeyGenerator.getInstance(algorithm); + SecretKey key = generator.generateKey(); + entry = new SecretKeyEntry((SecretKey) key); + + keyStore.setEntry(alias, entry, protParam); + File file = new File(IDEMPIERE_KEYSTORE); + FileOutputStream stream = null; + try { + stream = new FileOutputStream(file); + keyStore.store(stream, password); + stream.flush(); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (Exception e) {} + } + } + } + return entry.getSecretKey(); + } catch (Exception ex) { + log.log(Level.SEVERE, "", ex); + } + } + return m_key; + } + + @Override + public String getAlgorithm() { + if (algorithm == null) + return LEGACY_ALGORITHM; + else + return algorithm; + } + +} diff --git a/org.adempiere.base/src/org/compiere/util/Ini.java b/org.adempiere.base/src/org/compiere/util/Ini.java index 9f121f7e69..ecbf13c743 100644 --- a/org.adempiere.base/src/org/compiere/util/Ini.java +++ b/org.adempiere.base/src/org/compiere/util/Ini.java @@ -515,7 +515,7 @@ public final class Ini implements Serializable else if (!isClient()) result = s_prop.getProperty (key, SecureInterface.CLEARVALUE_START + defaultValue + SecureInterface.CLEARVALUE_END); else - result = s_prop.getProperty (key, SecureEngine.encrypt(defaultValue)); + result = s_prop.getProperty (key, SecureEngine.encrypt(defaultValue, 0)); s_prop.setProperty (key, result); return result; } // checkProperty @@ -577,7 +577,7 @@ public final class Ini implements Serializable s_prop.setProperty(key, ""); else { - String eValue = SecureEngine.encrypt(value); + String eValue = SecureEngine.encrypt(value, 0); if (eValue == null) s_prop.setProperty(key, ""); else @@ -619,7 +619,7 @@ public final class Ini implements Serializable if (retStr == null || retStr.length() == 0) return ""; // - String value = SecureEngine.decrypt(retStr); + String value = SecureEngine.decrypt(retStr, 0); // getLogger().finer(key + "=" + value); if (value == null) return ""; diff --git a/org.adempiere.base/src/org/compiere/util/Login.java b/org.adempiere.base/src/org/compiere/util/Login.java index 2a44f785c0..1bcfe90733 100644 --- a/org.adempiere.base/src/org/compiere/util/Login.java +++ b/org.adempiere.base/src/org/compiere/util/Login.java @@ -304,35 +304,27 @@ public class Login if ( user.authenticateHash(app_pwd) ) { authenticated = true; - app_pwd = null; } } else{ - StringBuffer sql = new StringBuffer("SELECT AD_User.AD_User_ID,") - .append(" AD_User.ConnectionProfile ") - .append(" FROM AD_User "); + StringBuffer sql = new StringBuffer("SELECT AD_User.AD_User_ID ").append(" FROM AD_User "); sql.append(" WHERE ").append(userNameCol).append("=?"); sql.append(" AND AD_User.IsActive='Y'").append(" AND EXISTS (SELECT * FROM AD_Client c WHERE AD_User.AD_Client_ID=c.AD_Client_ID AND c.IsActive='Y')"); - if (app_pwd != null) - sql.append(" AND ((AD_User.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='N') " - + "OR (AD_User.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='Y'))"); // #2/3 - PreparedStatement pstmt1=null; ResultSet rs1=null; try{ pstmt1 = DB.prepareStatement(sql.toString(), null); pstmt1.setString(1, app_user); - if (app_pwd != null) - { - pstmt1.setString(2, app_pwd); - pstmt1.setString(3, SecureEngine.encrypt(app_pwd)); - } rs1 = pstmt1.executeQuery(); while(rs1.next()){ - authenticated=true; + MUser user = new MUser(m_ctx, rs1.getInt(1), null); + if (user.getPassword() != null && user.getPassword().equals(app_pwd)) { + authenticated=true; + } + } }catch (Exception ex) { @@ -349,9 +341,8 @@ public class Login } if(authenticated){ - StringBuffer sql = new StringBuffer("SELECT AD_User.AD_User_ID, r.AD_Role_ID,r.Name,") - .append(" AD_User.ConnectionProfile ") - .append("FROM AD_User ") + StringBuffer sql = new StringBuffer("SELECT AD_User.AD_User_ID, r.AD_Role_ID,r.Name") + .append(" FROM AD_User ") .append(" INNER JOIN AD_User_Roles ur ON (AD_User.AD_User_ID=ur.AD_User_ID AND ur.IsActive='Y')") .append(" INNER JOIN AD_Role r ON (ur.AD_Role_ID=r.AD_Role_ID AND r.IsActive='Y') "); @@ -359,10 +350,6 @@ public class Login sql.append(" AND AD_User.IsActive='Y'").append(" AND EXISTS (SELECT * FROM AD_Client c WHERE AD_User.AD_Client_ID=c.AD_Client_ID AND c.IsActive='Y')"); - /* if (app_pwd != null && !hash_password) - sql.append(" AND ((AD_User.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='N') " - + "OR (AD_User.Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='Y'))"); // #2/3*/ - sql.append(" ORDER BY r.Name"); PreparedStatement pstmt = null; @@ -372,11 +359,6 @@ public class Login pstmt = DB.prepareStatement(sql.toString(), null); pstmt.setString(1, app_user); - /*if (app_pwd != null && !hash_password) - { - pstmt.setString(2, app_pwd); - pstmt.setString(3, SecureEngine.encrypt(app_pwd)); - }*/ // execute a query rs = pstmt.executeQuery(); @@ -413,13 +395,16 @@ public class Login } do // read all roles - { - int AD_Role_ID = rs.getInt(2); - if (AD_Role_ID == 0) - Env.setContext(m_ctx, "#SysAdmin", "Y"); - String Name = rs.getString(3); - KeyNamePair p = new KeyNamePair(AD_Role_ID, Name); - list.add(p); + { + MUser user = new MUser(m_ctx, rs.getInt(1), null); + if (user.getPassword() != null && user.getPassword().equals(app_pwd)) { + int AD_Role_ID = rs.getInt(2); + if (AD_Role_ID == 0) + Env.setContext(m_ctx, "#SysAdmin", "Y"); + String Name = rs.getString(3); + KeyNamePair p = new KeyNamePair(AD_Role_ID, Name); + list.add(p); + } } while (rs.next()); // @@ -440,6 +425,7 @@ public class Login { DB.close(rs, pstmt); rs = null; pstmt = null; + app_pwd = null; } } //long ms = System.currentTimeMillis () - start; @@ -1333,7 +1319,7 @@ public class Login valid = user.authenticateHash(app_pwd); } else { // password not hashed - valid = user.getPassword().equals(app_pwd); + valid = user.getPassword() != null && user.getPassword().equals(app_pwd); } if (valid ) { if (user.isLocked()) diff --git a/org.adempiere.base/src/org/compiere/util/Secure.java b/org.adempiere.base/src/org/compiere/util/Secure.java index 0752ab5238..7094458925 100644 --- a/org.adempiere.base/src/org/compiere/util/Secure.java +++ b/org.adempiere.base/src/org/compiere/util/Secure.java @@ -26,7 +26,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.Cipher; -import javax.crypto.SecretKey; + +import org.adempiere.base.Core; +import org.adempiere.base.IKeyStore; /** * Security Services. @@ -128,14 +130,11 @@ public class Secure implements SecureInterface { initCipher(); } // Secure - - /** Adempiere Cipher */ - private Cipher m_cipher = null; - /** Adempiere Key */ - private SecretKey m_key = null; + /** Message Digest */ private MessageDigest m_md = null; + private IKeyStore m_keyStore = null; /** Logger */ private static Logger log = Logger.getLogger (Secure.class.getName()); @@ -144,34 +143,10 @@ public class Secure implements SecureInterface */ private synchronized void initCipher() { - if (m_cipher != null) - return; - Cipher cc = null; - try - { - cc = Cipher.getInstance("DES/ECB/PKCS5Padding"); - // Key - /*if (false) - { - KeyGenerator keygen = KeyGenerator.getInstance("DES"); - m_key = keygen.generateKey(); - byte[] key = m_key.getEncoded(); - StringBuffer sb = new StringBuffer ("Key ") - .append(m_key.getAlgorithm()) - .append("(").append(key.length).append(")= "); - for (int i = 0; i < key.length; i++) - sb.append(key[i]).append(","); - log.info(sb.toString()); - } - else*/ - m_key = new javax.crypto.spec.SecretKeySpec - (new byte[] {100,25,28,-122,-26,94,-3,-26}, "DES"); + if(m_keyStore==null){ + m_keyStore = getKeyStore(); } - catch (Exception ex) - { - log.log(Level.SEVERE, "", ex); - } - m_cipher = cc; + } // initCipher @@ -179,35 +154,35 @@ public class Secure implements SecureInterface /** * Encryption. * @param value clear value + * @param AD_Client_ID * @return encrypted String */ - public String encrypt (String value) - { + public String encrypt (String value,int AD_Client_ID) + { String clearText = value; if (clearText == null) clearText = ""; - // Init - if (m_cipher == null) + // Init + if (m_keyStore == null) initCipher(); - // Encrypt - if (m_cipher != null) - { - try - { - m_cipher.init(Cipher.ENCRYPT_MODE, m_key); - byte[] encBytes = m_cipher.doFinal(clearText.getBytes("UTF8")); - String encString = convertToHexString(encBytes); - // globalqss - [ 1577737 ] Security Breach - show database password - // log.log (Level.ALL, value + " => " + encString); - return encString; - } - catch (Exception ex) - { - // log.log(Level.INFO, value, ex); - log.log(Level.INFO, "Problem encrypting string", ex); - } + + // Encrypt + try { + Cipher cipher = Cipher.getInstance(m_keyStore.getAlgorithm()); + + cipher.init(Cipher.ENCRYPT_MODE, m_keyStore.getKey(AD_Client_ID)); + byte[] encBytes = cipher.doFinal(clearText.getBytes("UTF8")); + + String encString = convertToHexString(encBytes); + // globalqss - [ 1577737 ] Security Breach - show database password + // log.log (Level.ALL, value + " => " + encString); + return encString; + } catch (Exception ex) { + // log.log(Level.INFO, value, ex); + log.log(Level.INFO, "Problem encrypting string", ex); } - // Fallback + + // Fallback return CLEARVALUE_START + value + CLEARVALUE_END; } // encrypt @@ -215,9 +190,10 @@ public class Secure implements SecureInterface * Decryption. * The methods must recognize clear text values * @param value encrypted value + * @param AD_Client_ID * @return decrypted String */ - public String decrypt (String value) + public String decrypt (String value,int AD_Client_ID) { if (value == null || value.length() == 0) return value; @@ -238,17 +214,18 @@ public class Secure implements SecureInterface return value; } // Init - if (m_cipher == null) + if (m_keyStore == null) initCipher(); // Encrypt - if (m_cipher != null && value != null && value.length() > 0) + if (value != null && value.length() > 0) { try { - AlgorithmParameters ap = m_cipher.getParameters(); - m_cipher.init(Cipher.DECRYPT_MODE, m_key, ap); - byte[] out = m_cipher.doFinal(data); + Cipher cipher = Cipher.getInstance(m_keyStore.getAlgorithm()); + AlgorithmParameters ap = cipher.getParameters(); + cipher.init(Cipher.DECRYPT_MODE, m_keyStore.getKey(AD_Client_ID), ap); + byte[] out = cipher.doFinal(data); String retValue = new String(out, "UTF8"); // globalqss - [ 1577737 ] Security Breach - show database password // log.log (Level.ALL, value + " => " + retValue); @@ -267,9 +244,10 @@ public class Secure implements SecureInterface * Encryption. * The methods must recognize clear text values * @param value clear value + * @param ad_client_id * @return encrypted String */ - public Integer encrypt (Integer value) + public Integer encrypt (Integer value,int ad_client_id) { return value; } // encrypt @@ -280,7 +258,7 @@ public class Secure implements SecureInterface * @param value encrypted value * @return decrypted String */ - public Integer decrypt (Integer value) + public Integer decrypt (Integer value,int ad_client_id) { return value; } // decrypt @@ -289,9 +267,10 @@ public class Secure implements SecureInterface * Encryption. * The methods must recognize clear text values * @param value clear value + * @param ad_client_id * @return encrypted String */ - public BigDecimal encrypt (BigDecimal value) + public BigDecimal encrypt (BigDecimal value,int ad_client_id) { return value; } // encrypt @@ -302,7 +281,7 @@ public class Secure implements SecureInterface * @param value encrypted value * @return decrypted String */ - public BigDecimal decrypt (BigDecimal value) + public BigDecimal decrypt (BigDecimal value,int ad_client_id) { return value; } // decrypt @@ -311,9 +290,10 @@ public class Secure implements SecureInterface * Encryption. * The methods must recognize clear text values * @param value clear value + * @param ad_client_id * @return encrypted String */ - public Timestamp encrypt (Timestamp value) + public Timestamp encrypt (Timestamp value,int ad_client_id) { return value; } // encrypt @@ -324,7 +304,7 @@ public class Secure implements SecureInterface * @param value encrypted value * @return decrypted String */ - public Timestamp decrypt (Timestamp value) + public Timestamp decrypt (Timestamp value,int ad_client_id) { return value; } // decrypt @@ -412,9 +392,20 @@ public class Secure implements SecureInterface public String toString () { StringBuilder sb = new StringBuilder ("Secure["); - sb.append(m_cipher) + sb.append(m_keyStore.getAlgorithm()) .append ("]"); return sb.toString (); } // toString + /** + * + * @return keystore + */ + public IKeyStore getKeyStore(){ + IKeyStore keyStore = Core.getKeyStore(); + if(keyStore==null) + keyStore = new DefaultKeyStore(); + + return keyStore; + } } // Secure diff --git a/org.adempiere.base/src/org/compiere/util/SecureEngine.java b/org.adempiere.base/src/org/compiere/util/SecureEngine.java index ef9be7313c..d9fd5cf5da 100644 --- a/org.adempiere.base/src/org/compiere/util/SecureEngine.java +++ b/org.adempiere.base/src/org/compiere/util/SecureEngine.java @@ -106,9 +106,10 @@ public class SecureEngine * Encryption. * The methods must recognize clear text values * @param value clear value + * @param AD_Client_ID * @return encrypted String */ - public static String encrypt (String value) + public static String encrypt (String value,int AD_Client_ID) { if (value == null || value.length() == 0) return value; @@ -119,19 +120,21 @@ public class SecureEngine if (inQuotes) value = value.substring(1, value.length()-1); // - String retValue = s_engine.implementation.encrypt(value); + String retValue = s_engine.implementation.encrypt(value,AD_Client_ID); if (inQuotes) return "'" + retValue + "'"; return retValue; } // encrypt + /** * Decryption. * The methods must recognize clear text values * @param value encrypted value + * @param AD_Client_ID * @return decrypted String */ - public static String decrypt (String value) + public static String decrypt (String value, int AD_Client_ID) { if (value == null) return null; @@ -144,22 +147,23 @@ public class SecureEngine if (value.startsWith(SecureInterface.CLEARVALUE_START) && value.endsWith(SecureInterface.CLEARVALUE_END)) retValue = value.substring(SecureInterface.CLEARVALUE_START.length(), value.length()-SecureInterface.CLEARVALUE_END.length()); else - retValue = s_engine.implementation.decrypt(value); + retValue = s_engine.implementation.decrypt(value,AD_Client_ID); if (inQuotes) return "'" + retValue + "'"; return retValue; } // decrypt - + /** * Encryption. * The methods must recognize clear values * @param value clear value + * @param AD_Client_ID * @return encrypted String */ - public static Object encrypt (Object value) + public static Object encrypt (Object value, int AD_Client_ID) { if (value instanceof String) - return encrypt((String) value); + return encrypt((String) value, AD_Client_ID); return value; } // encrypt @@ -169,10 +173,10 @@ public class SecureEngine * @param value encrypted value * @return decrypted String */ - public static Object decrypt (Object value) + public static Object decrypt (Object value, int AD_Client_ID) { if (value instanceof String) - return decrypt((String) value); + return decrypt((String) value, AD_Client_ID); return value; } // decrypt @@ -204,8 +208,8 @@ public class SecureEngine System.exit(10); } // See if it works - String testE = implementation.encrypt(TEST); - String testC = implementation.decrypt(testE); + String testE = implementation.encrypt(TEST,0); + String testC = implementation.decrypt(testE,0); if (!testC.equals(TEST)) throw new IllegalStateException(realClass + ": " + TEST @@ -269,10 +273,10 @@ public class SecureEngine log.info("Decrypt null =" + test(decrypt(null), null)); log.info("Decrypt test =" + test(decrypt("test"), "test")); **/ - log.info("Decrypt {test} =" + test(decrypt("af2309f390afed74"), "test")); - log.info("Decrypt ~{test}~ =" + test(decrypt(SecureInterface.ENCRYPTEDVALUE_START + "af2309f390afed74" + SecureInterface.ENCRYPTEDVALUE_END), "test")); + log.info("Decrypt {test} =" + test(decrypt("af2309f390afed74", 0), "test")); + log.info("Decrypt ~{test}~ =" + test(decrypt(SecureInterface.ENCRYPTEDVALUE_START + "af2309f390afed74" + SecureInterface.ENCRYPTEDVALUE_END, 0), "test")); - log.info("Encrypt test =" + test(encrypt("test"), "af2309f390afed74")); + log.info("Encrypt test =" + test(encrypt("test", 0), "af2309f390afed74")); diff --git a/org.adempiere.base/src/org/compiere/util/SecureInterface.java b/org.adempiere.base/src/org/compiere/util/SecureInterface.java index c4917f2614..9e5141668f 100644 --- a/org.adempiere.base/src/org/compiere/util/SecureInterface.java +++ b/org.adempiere.base/src/org/compiere/util/SecureInterface.java @@ -52,64 +52,71 @@ public interface SecureInterface /** * Encryption. * @param value clear value + * @param AD_Client_ID * @return encrypted String */ - public String encrypt (String value); + public String encrypt (String value,int AD_Client_ID); /** * Decryption. * @param value encrypted value * @return decrypted String */ - public String decrypt (String value); + public String decrypt (String value,int AD_Client_ID); /** * Encryption. * The methods must recognize clear text values * @param value clear value + * @param AD_Client_ID * @return encrypted String */ - public Integer encrypt (Integer value); + public Integer encrypt (Integer value,int AD_Client_ID); /** * Decryption. * The methods must recognize clear text values * @param value encrypted value + * @param AD_Client_ID * @return decrypted String */ - public Integer decrypt (Integer value); + public Integer decrypt (Integer value,int AD_Client_ID); /** * Encryption. * The methods must recognize clear text values * @param value clear value + * @param AD_Client_ID * @return encrypted String */ - public BigDecimal encrypt (BigDecimal value); + public BigDecimal encrypt (BigDecimal value,int AD_Client_ID); /** * Decryption. * The methods must recognize clear text values * @param value encrypted value + * @param AD_Client_ID * @return decrypted String */ - public BigDecimal decrypt (BigDecimal value); + public BigDecimal decrypt (BigDecimal value,int AD_Client_ID); /** * Encryption. * The methods must recognize clear text values * @param value clear value + * @param AD_Client_ID * @return encrypted String */ - public Timestamp encrypt (Timestamp value); + public Timestamp encrypt (Timestamp value,int AD_Client_ID); /** * Decryption. * The methods must recognize clear text values * @param value encrypted value + * @param AD_Client_ID * @return decrypted String */ - public Timestamp decrypt (Timestamp value); + public Timestamp decrypt (Timestamp value,int AD_Client_ID); /** diff --git a/org.adempiere.base/src/org/compiere/util/WebUser.java b/org.adempiere.base/src/org/compiere/util/WebUser.java index 5f8fea21ca..e4859d6a28 100644 --- a/org.adempiere.base/src/org/compiere/util/WebUser.java +++ b/org.adempiere.base/src/org/compiere/util/WebUser.java @@ -682,8 +682,6 @@ public class WebUser { String sql = "SELECT * FROM AD_User " + "WHERE COALESCE(LDAPUser, Name)=? " // #1 - + " AND ((Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='N') " // #2 - + "OR (Password=? AND (SELECT IsEncrypted FROM AD_Column WHERE AD_Column_ID=417)='Y'))" // #3 + " AND IsActive='Y' " // #4 ; PreparedStatement pstmt = null; @@ -692,14 +690,17 @@ public class WebUser { pstmt = DB.prepareStatement (sql, null); pstmt.setString (1, m_bpc.getName()); - pstmt.setString (2, password); - pstmt.setString (3, SecureEngine.encrypt(password)); rs = pstmt.executeQuery (); if (rs.next ()) { - retValue = true; - if (rs.next()) - log.warning ("More then one user with Name/Password = " + m_bpc.getName()); + do + { + MUser user = new MUser(Env.getCtx(), rs, null); + if (user.getPassword() != null && user.getPassword().equals(password)) { + retValue = true; + break; + } + } while (rs.next()); } else log.fine("No record"); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/FindWindow.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/FindWindow.java index 809036a8db..06c040b196 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/FindWindow.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/FindWindow.java @@ -1697,7 +1697,7 @@ public class FindWindow extends Window implements EventListener, ValueCha GridField field = getTargetMField(ColumnName); // add encryption here if the field is encrypted. if (field.isEncryptedColumn()) { - value = SecureEngine.encrypt(value); + value = SecureEngine.encrypt(value, Env.getAD_Client_ID(Env.getCtx())); } boolean isProductCategoryField = isProductCategoryField(field.getAD_Column_ID());