I'm designing a small Java desktop application to interact with my database and this is a very important part of it as a majority of the operations will be involving the SQL Server 2012 database. I am using the SQL Server JDBC driver (v6.0) provided by Microsoft.
I wrote a class to hold credentials used to connect, and another one to provide a connection to be able to interact with the database. I'm using Java 8. Looking for any and all improvements.
ConnectionCredentials
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.MessageDigest;
/**
* Hold and make available credentials to connect to SQL database.
*/
public class ConnectionCredentials {
private String serverName = "";
private String databaseName = "";
private String username = "";
private Key passwordKey;
private byte[] encryptedPassword;
/**
* Constructor
* @param serverName name/address of the server
* @param databaseName name of the database
* @param username user/login name on the database
* @param password user/login password on the database (plain text)
*/
public ConnectionCredentials(String serverName, String databaseName, String username, String password) {
this.serverName = serverName;
this.databaseName = databaseName;
this.username = username;
encryptedPassword = encrypt(password);
}
/**
* Encrypts password prior to storing in a ConnectionCredentials object
* @param password
* @return the encrypted password
*/
private byte[] encrypt(String password) {
// Encryption code based on
// http://stackoverflow.com/a/32583766/3626537
byte[] encrypted = {};
try {
MessageDigest digester = MessageDigest.getInstance("MD5");
digester.update(String.valueOf(password).getBytes("UTF-8"));
byte[] digest = digester.digest();
passwordKey = new SecretKeySpec(digest, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, passwordKey);
encrypted = cipher.doFinal(password.getBytes());
} catch(Exception exc) {
exc.printStackTrace();
}
return encrypted;
}
/**
* Format credentials to a String.
* @return the credentials formatted as a String
*/
@Override
public String toString() {
return String.format(
"serverName: %s | databaseName: %s | username: %s | password: HIDDEN",
getServerName(),
getDatabaseName(),
getUsername()
);
}
/*
* Field getters and setters
*/
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public String getDatabaseName() {
return databaseName;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Key getPasswordKey() {
return passwordKey;
}
public byte[] getEncryptedPassword() {
return encryptedPassword;
}
public void setPassword(String password) {
encryptedPassword = encrypt(password);
}
}
DatabaseConnector
import javax.crypto.Cipher;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* Connect to SQL Server database using ConnectionCredentials object.
*/
public class DatabaseConnector {
private String connectionUrl = "";
private ConnectionCredentials credentials;
/**
* Constructor
* @param credentials ConnectionCredentials object containing the required configuration to connect to the database server instance.
*/
public DatabaseConnector(ConnectionCredentials credentials) {
this.credentials = credentials;
}
/**
* Get a JDBC connection using ConnectionCredentials object.
* @param credentials the ConnectionCredentials object to use to open JDBC connection
* @return the JDBC connection
*/
public Connection getJdbcConnection(ConnectionCredentials credentials) {
Connection jdbcConnection = null;
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
jdbcConnection = DriverManager.getConnection(
getConnectionUrl(credentials),
credentials.getUsername(),
decrypt(credentials.getEncryptedPassword())
);
} catch(Exception exc) {
exc.printStackTrace();
}
return jdbcConnection;
}
/**
* Builds the required connection URL required by DriverManager.
* @param credentials ConnectionCredentials object containing the required configuration to connect to the database server instance.
* @return the connection URL built from the credentials
*/
private String getConnectionUrl(ConnectionCredentials credentials) {
String serverName = credentials.getServerName();
String databaseName = credentials.getDatabaseName();
String username = credentials.getUsername();
connectionUrl = String.format(
"jdbc:sqlserver://%s;databaseName=%s;",
serverName,
databaseName
);
return connectionUrl;
}
/**
* Decrypts data encrypted by ConnectionCredentials, e.g., a password.
* @param encrypted the encrypted data
* @return the decrypted data
*/
private String decrypt(byte[] encrypted) {
String decrypted = "";
StringBuilder stringBuilder = new StringBuilder();
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, credentials.getPasswordKey());
decrypted = new String(cipher.doFinal(encrypted));
} catch(Exception exc) {
exc.printStackTrace();
}
return decrypted;
}
}
Example usage
ConnectionCredentials credentials = new ConnectionCredentials(server, database, username, password); DatabaseConnector connector = new DatabaseConnector(credentials); Connection connection = connector.getJdbcConnection(credentials); try { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("select Id, Name, Organization from PsychoProductions.Persons;"); while(resultSet.next()) { System.out.printf( "Id: %d Name: %s Organization: %s%n", resultSet.getInt("Id"), resultSet.getString("Name"), resultSet.getString("Organization") ); } } catch(Exception exc) { exc.printStackTrace(); }
credentials
and a mandatorycredentials
argument for the methods is confusing. I'd suggest doing one or the other instead of mixing them. \$\endgroup\$ – ferada Jun 22 '16 at 14:04