Integrate contribution from Bahman

[ 1726329 ] Setting a column encrypted should also encrypt its contents
This commit is contained in:
Carlos Ruiz 2008-02-14 03:30:55 +00:00
parent 353c1000c7
commit 8459f86e36
1 changed files with 332 additions and 111 deletions

View File

@ -1,53 +1,79 @@
/****************************************************************************** /**********************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution * * This file is part of Adempiere ERP Bazaar *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * http://www.adempiere.org *
* 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 * * Copyright (C) 1999 - 2006 Compiere Inc. *
* by the Free Software Foundation. This program is distributed in the hope * * Copyright (C) Contributors *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * * *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * This program is free software; you can redistribute it and/or *
* See the GNU General Public License for more details. * * modify it under the terms of the GNU General Public License *
* You should have received a copy of the GNU General Public License along * * as published by the Free Software Foundation; either version 2 *
* with this program; if not, write to the Free Software Foundation, Inc., * * of the License, or (at your option) any later version. *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * *
* For the text or an alternative of this public license, you may reach us * * This program is distributed in the hope that it will be useful, *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * * but WITHOUT ANY WARRANTY; without even the implied warranty of *
* or via info@compiere.org or http://www.compiere.org/license.html * * 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Bahman Movaqar (bmovaqar AT users.sf.net) *
**********************************************************************/
package org.compiere.process; package org.compiere.process;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.logging.*; import java.util.logging.*;
import java.sql.ResultSet;
import org.compiere.model.*; import org.compiere.model.*;
import org.compiere.util.*; import org.compiere.util.*;
/** /**
* Column Encryption Test * Column Encryption Test
* *
* @author Jorg Janke * @author Jorg Janke
* @version $Id: ColumnEncryption.java,v 1.2 2006/07/30 00:51:01 jjanke Exp $ * @version $Id: ColumnEncryption.java,v 1.2 2006/07/30 00:51:01 jjanke Exp $
*/ */
public class ColumnEncryption extends SvrProcess public class ColumnEncryption extends SvrProcess {
{ /** Enable/Disable Encryption */
/** Enable/Disable Encryption */ private boolean p_IsEncrypted = false;
private boolean p_IsEncrypted = false;
/** Change Encryption Settings */ /** Change Encryption Settings */
private boolean p_ChangeSetting = false; private boolean p_ChangeSetting = false;
/** Maximum Length */
private int p_MaxLength = 0; /** Maximum Length */
/** Test Value */ private int p_MaxLength = 0;
private String p_TestValue = null;
/** The Column */ /** Test Value */
private int p_AD_Column_ID = 0; private String p_TestValue = null;
/** The Column */
private int p_AD_Column_ID = 0;
/** /**
* Prepare - e.g., get Parameters. * All the resizing and encrypting database are managed by this
* transaction.
*/ */
protected void prepare() private Trx m_trx;
{
/**
* All the resizing and encrypting database work goes through this
* connection.
*/
private Connection m_conn;
/**
* Prepare - e.g., get Parameters.
*/
protected void prepare() {
ProcessInfoParameter[] para = getParameter(); ProcessInfoParameter[] para = getParameter();
for (int i = 0; i < para.length; i++) for (int i = 0; i < para.length; i++) {
{
String name = para[i].getParameterName(); String name = para[i].getParameterName();
if (para[i].getParameter() == null) if (para[i].getParameter() == null)
; ;
@ -58,87 +84,80 @@ public class ColumnEncryption extends SvrProcess
else if (name.equals("MaxLength")) else if (name.equals("MaxLength"))
p_MaxLength = para[i].getParameterAsInt(); p_MaxLength = para[i].getParameterAsInt();
else if (name.equals("TestValue")) else if (name.equals("TestValue"))
p_TestValue = (String)para[i].getParameter(); p_TestValue = (String) para[i].getParameter();
else else
log.log(Level.SEVERE, "Unknown Parameter: " + name); log.log(Level.SEVERE, "Unknown Parameter: " + name);
} }
p_AD_Column_ID = getRecord_ID(); p_AD_Column_ID = getRecord_ID();
} // prepare } // prepare
/** /**
* Process * Process
* @return info *
* @throws Exception * @return info
* @throws Exception
*/ */
protected String doIt () throws Exception protected String doIt() throws Exception {
{ log.info("AD_Column_ID=" + p_AD_Column_ID + ", IsEncrypted="
log.info("AD_Column_ID=" + p_AD_Column_ID + p_IsEncrypted + ", ChangeSetting=" + p_ChangeSetting
+ ", IsEncrypted=" + p_IsEncrypted + ", MaxLength=" + p_MaxLength);
+ ", ChangeSetting=" + p_ChangeSetting MColumn column = new MColumn(getCtx(), p_AD_Column_ID, get_TrxName());
+ ", MaxLength=" + p_MaxLength);
MColumn column = new MColumn (getCtx(), p_AD_Column_ID, null);
if (column.get_ID() == 0 || column.get_ID() != p_AD_Column_ID) if (column.get_ID() == 0 || column.get_ID() != p_AD_Column_ID)
throw new AdempiereUserError("@NotFound@ @AD_Column_ID@ - " + p_AD_Column_ID); throw new AdempiereUserError("@NotFound@ @AD_Column_ID@ - "
+ p_AD_Column_ID);
// //
String columnName = column.getColumnName(); String columnName = column.getColumnName();
int dt = column.getAD_Reference_ID(); int dt = column.getAD_Reference_ID();
// Can it be enabled? // Can it be enabled?
if (column.isKey() if (column.isKey() || column.isParent() || column.isStandardColumn()
|| column.isParent() || column.isVirtualColumn() || column.isIdentifier()
|| column.isStandardColumn() || column.isTranslated() || DisplayType.isLookup(dt)
|| column.isVirtualColumn() || DisplayType.isLOB(dt)
|| column.isIdentifier() || "DocumentNo".equalsIgnoreCase(column.getColumnName())
|| column.isTranslated() || "Value".equalsIgnoreCase(column.getColumnName())
|| DisplayType.isLookup(dt) || "Name".equalsIgnoreCase(column.getColumnName())) {
|| DisplayType.isLOB(dt) if (column.isEncrypted()) {
|| "DocumentNo".equalsIgnoreCase(column.getColumnName())
|| "Value".equalsIgnoreCase(column.getColumnName())
|| "Name".equalsIgnoreCase(column.getColumnName()))
{
if (column.isEncrypted())
{
column.setIsEncrypted(false); column.setIsEncrypted(false);
column.save(); column.save();
} }
return columnName + ": cannot be encrypted"; return columnName + ": cannot be encrypted";
} }
// Start // Start
addLog(0, null, null, "Encryption Class = " + SecureEngine.getClassName()); addLog(0, null, null, "Encryption Class = "
+ SecureEngine.getClassName());
boolean error = false; boolean error = false;
// Test Value // Test Value
if (p_TestValue != null && p_TestValue.length() > 0) if (p_TestValue != null && p_TestValue.length() > 0) {
{
String encString = SecureEngine.encrypt(p_TestValue); String encString = SecureEngine.encrypt(p_TestValue);
addLog(0, null, null, "Encrypted Test Value=" + encString); addLog(0, null, null, "Encrypted Test Value=" + encString);
String clearString = SecureEngine.decrypt(encString); String clearString = SecureEngine.decrypt(encString);
if (p_TestValue.equals(clearString)) if (p_TestValue.equals(clearString))
addLog(0, null, null, "Decrypted=" + clearString addLog(0, null, null, "Decrypted=" + clearString
+ " (same as test value)"); + " (same as test value)");
else else {
{
addLog(0, null, null, "Decrypted=" + clearString addLog(0, null, null, "Decrypted=" + clearString
+ " (NOT the same as test value - check algorithm)"); + " (NOT the same as test value - check algorithm)");
error = true; error = true;
} }
int encLength = encString.length(); int encLength = encString.length();
addLog(0, null, null, "Test Length=" + p_TestValue.length() + " -> " + encLength); addLog(0, null, null, "Test Length=" + p_TestValue.length()
+ " -> " + encLength);
if (encLength <= column.getFieldLength()) if (encLength <= column.getFieldLength())
addLog(0, null, null, "Encrypted Length (" + encLength addLog(0, null, null, "Encrypted Length (" + encLength
+ ") fits into field (" + column.getFieldLength() + ")"); + ") fits into field (" + column.getFieldLength() + ")");
else else {
{
addLog(0, null, null, "Encrypted Length (" + encLength addLog(0, null, null, "Encrypted Length (" + encLength
+ ") does NOT fit into field (" + column.getFieldLength() + ") - resize field"); + ") does NOT fit into field ("
+ column.getFieldLength() + ") - resize field");
error = true; error = true;
} }
} }
// Length Test // Length Test
if (p_MaxLength != 0) if (p_MaxLength != 0) {
{
String testClear = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; String testClear = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
while (testClear.length() < p_MaxLength) while (testClear.length() < p_MaxLength)
testClear += testClear; testClear += testClear;
@ -147,32 +166,234 @@ public class ColumnEncryption extends SvrProcess
// //
String encString = SecureEngine.encrypt(testClear); String encString = SecureEngine.encrypt(testClear);
int encLength = encString.length(); int encLength = encString.length();
addLog(0, null, null, "Test Max Length=" + testClear.length() + " -> " + encLength); addLog(0, null, null, "Test Max Length=" + testClear.length()
+ " -> " + encLength);
if (encLength <= column.getFieldLength()) if (encLength <= column.getFieldLength())
addLog(0, null, null, "Encrypted Max Length (" + encLength addLog(0, null, null, "Encrypted Max Length (" + encLength
+ ") fits into field (" + column.getFieldLength() + ")"); + ") fits into field (" + column.getFieldLength() + ")");
else else {
{
addLog(0, null, null, "Encrypted Max Length (" + encLength addLog(0, null, null, "Encrypted Max Length (" + encLength
+ ") does NOT fit into field (" + column.getFieldLength() + ") - resize field"); + ") does NOT fit into field ("
+ column.getFieldLength() + ") - resize field");
error = true; error = true;
} }
} }
if (p_IsEncrypted != column.isEncrypted()) // If only user chooses both encrypt the contents and override current
{ // settings resize the physical column and encrypt all its contents.
if (error || !p_ChangeSetting) if (p_IsEncrypted && p_ChangeSetting) {
addLog(0, null, null, "Encryption NOT changed - Encryption=" + column.isEncrypted()); // If the column has already been encrypted, show a warning message
else // and exit.
{ if (column.isEncrypted()) {
column.setIsEncrypted(p_IsEncrypted); log.severe("EncryptError: Column already encrypted.");
if (column.save()) throw new Exception();
addLog(0, null, null, "Encryption CHANGED - Encryption=" + column.isEncrypted()); }
else // Init the transaction and setup the connection.
addLog(0, null, null, "Save Error"); m_trx = Trx.get(get_TrxName(), true);
if ((m_conn = m_trx.getConnection()) == null) {
log.warning("EncryptError: No connections available");
throw new Exception();
}
m_conn.setAutoCommit(false);
int columnID = column.get_ID();
MTable table = MTable.get(getCtx(), column.getAD_Table_ID());
String tableName = table.getTableName();
// Check if the encryption exceeds the current length.
int oldLength = column.getFieldLength();
int newLength = encryptedColumnLength(oldLength);
if (newLength > oldLength)
if (changeFieldLength(columnID, columnName, newLength,
tableName) == -1) {
log.warning("EncryptError [ChangeFieldLength]: "
+ "ColumnID=" + columnID + ", NewLength="
+ newLength);
throw new Exception();
}
// Encrypt column contents.
if (encryptColumnContents(columnName, column.getAD_Table_ID()) == -1) {
log.warning("EncryptError: No records encrypted.");
throw new Exception();
}
if (p_IsEncrypted != column.isEncrypted()) {
if (error || !p_ChangeSetting)
addLog(0, null, null, "Encryption NOT changed - Encryption="
+ column.isEncrypted());
else {
column.setIsEncrypted(p_IsEncrypted);
if (column.save())
addLog(0, null, null, "Encryption CHANGED - Encryption="
+ column.isEncrypted());
else
addLog(0, null, null, "Save Error");
}
} }
} }
return "Encryption=" + column.isEncrypted();
} // doIt
} // EncryptionTest return "Encryption=" + column.isEncrypted();
} // doIt
/**
* Encrypt all the contents of a database column.
*
* @param columnName
* The ID of the column to be encrypted.
* @param tableID
* The ID of the table which owns the column.
* @return The number of rows effected or -1 in case of errors.
* @throws Exception
*/
private int encryptColumnContents(String columnName, int tableID)
throws Exception {
// Find the table name
String tableName = MTable.getTableName(getCtx(), tableID);
return encryptColumnContents(columnName, tableName);
} // encryptColumnContents
/**
* Encrypt all the contents of a database column.
*
* @param columnName
* The ID of the column to be encrypted.
* @param tableName
* The name of the table which owns the column.
* @return The number of rows effected or -1 in case of errors.
*/
private int encryptColumnContents(String columnName, String tableName)
throws Exception {
int recordsEncrypted = 0;
String idColumnName = tableName + "_ID";
StringBuffer selectSql = new StringBuffer();
selectSql.append("SELECT " + idColumnName + "," + columnName);
selectSql.append(" FROM " + tableName);
selectSql.append(" ORDER BY " + idColumnName);
StringBuffer updateSql = new StringBuffer();
updateSql.append("UPDATE " + tableName);
updateSql.append(" SET " + columnName + "=?");
updateSql.append(" WHERE " + idColumnName + "=?");
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();
}
}
rs.close();
selectStmt.close();
updateStmt.close();
return recordsEncrypted;
} // encryptColumnContents
/**
* Determines the length of the encrypted column.
*
* @param currentColSize
* Current column size
* @return The length of the encrypted column.
*/
private int encryptedColumnLength(int colLength) {
String str = "";
for (int i = 0; i < colLength; i++) {
str += "1";
}
str = SecureEngine.encrypt(str);
return str.length();
} // encryptedColumnLength
/**
* Change the column length.
*
* @param columnID
* ID of the column
* @param tableName
* The name of the table which owns the column
* @param length
* New length of the column
* @return The number of rows effected, 1 upon success and -1 for failure.
*/
private int changeFieldLength(int columnID, String columnName, int length,
String tableName) throws Exception {
int rowsEffected = -1;
// Select SQL
StringBuffer selectSql = new StringBuffer();
selectSql.append("SELECT FieldLength");
selectSql.append(" FROM AD_Column");
selectSql.append(" WHERE AD_Column_ID=?");
// Alter SQL
StringBuffer alterSql = new StringBuffer();
alterSql.append("ALTER TABLE " + tableName);
alterSql.append(" MODIFY " + columnName);
alterSql.append(" NVARCHAR2(");
alterSql.append(length + ") ");
// Update SQL
StringBuffer updateSql = new StringBuffer();
updateSql.append("UPDATE AD_Column");
updateSql.append(" SET FieldLength=" + length);
updateSql.append(" WHERE AD_Column_ID=" + 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();
}
}
rs.close();
selectStmt.close();
// Update number of rows effected.
rowsEffected++;
return rowsEffected;
} // changeFieldLength
} // EncryptionTest