Rethinking the Database Structure
Changing the Database Structure
Currently, we have main database in which we store the user’s email, hashed password and salt when they register with the program. We were planning to create a database of passwords for each user which would be encrypted and located by their email address. But, if we are going to encrypt each user’s password database, surely we could remove the main database and be left with a password database per user. Each database file would be named by hashing the user’s email (for privacy reasons) and each file would be encrypted using AES-256 encryption with a key generated from the user’s password. AES-256 encryption is military grade encryption so is very hard to break meaning all the user’s passwords would be safe in the database.
Creating and Storing the Database
Firstly, when the user registers, we need to generate a blank .db database file. This file will be stored in \AppData\Roaming\PasswordManager
. The name of the database file is generated by hashing the username (everything before the @) of the user’s email address. The username is hashed using a salt of the domain (everything after the @). The code for this process is shown below.
String[] emailArray = email.split("@");
char[] username = emailArray[0].toCharArray();
byte[] domain = emailArray[1].getBytes();
byte[] hashedEmailBytes = hash(username, domain);
The first line takes the email inputted by the user and splits it according to the delimiter, in our case, @ and returns an array of the two halves of the email address - the username and the domain. Our hashing method takes in the data to be hashed as a character array so we have to convert the username, which is a string, to a character array before we can pass it to the hash() method. This is shown on the second line. And, our hashing method takes the salt as a byte array so we have to convert the domain, which is also a string, to a byte array so we can pass it to the method. This is shown on the third line. Once passed through the hash method, we get a byte array which not suitable for naming a file, so we must convert it to a hex string. The code to do this is shown below.
StringBuilder builder = new StringBuilder();
for(byte b: bytes) {
builder.append(String.format("%02x", b));
}
String hexString = builder.toString();
The StringBuilder object is as the name suggests, it helps to build a string.
We have to iterate through each byte in the byte array, convert the byte into a hexadecimal character and add it to the StringBuilder object. Let’s break down what %02x
means:
0
means the result will be padded with zeros if it doesn’t meet the specified length2
means the result will have a length of 2x
means convert to a lowercase hex character
Creating the passwords Table
We got our database file, but it’s blank so we start by creating a table. We do this by using SQL as mentioned in the previous blog posts.
CREATE TABLE IF NOT EXISTS passwords (id integer PRIMARY KEY,name VARCHAR,username VARCHAR,password VARCHAR)
CREATE TABLE IF NOT EXISTS passwords
does what exactly as it says. It creates a table called passwords if a table doesn’t exist with the same name.id integer PRIMARY KEY
creates a column called id which is an integer and is the unique identifier for each record.name VARCHAR
creates a column called name which can hold a string of variable length.- The same goes for username and password.
Encrypting the Database
We now need to encrypt the database as all the data inside is stored as plain text. Here’s where the AES-256 encryption comes to play as shown in the code below.
byte[] salt = email.getBytes();
byte[] hashedPassword = EncryptionMethods.hash(password, salt);
Here, we are using the user’s email address as the salt to hash their password using our hash() method. The hashed password will be used as the key to encrypt the database with AES-256.
Problems
We have multiple problems with this new database structure. First of all, we’re using sqlite jdbc to work with our database file which works fine when it not encrypted. The library will not be able to communicate with an encrypted database which means that we will have to decrypt the database, store it temporarily, write to it, encrypt it and store it again. This means there are a few seconds in which a hacker could possibly get access to the unencrypted data which is all in plain text.
Another option would be to decrypt the database to main memory, write to it there, then encrypt it and store it on the disk. However, it doesn’t seem like we can extract the data from the database in memory to encrypt and store. We would have to dump the database into a text file and then encrypt it which causes the same issue as the first option.
Another more plausible option would be to encrypt the data using AES-256 encryption before writing it to the database. But, then the hashed user’s password would have to be stored like in our old database structure and this hash cannot be used as the key for the AES encryption. This is because the main database would not be encrypted so the password hash would be readable and the encrypted data in the other database would easily be decrypted. Instead, we must find another way of generating a key to use to encrypt the data. A possibility would be to base the key on the user’s password but use a different hashing method to the one used to store the user’s password.
Conclusion
Changing the database structure to the new one has been quite a thought-provoking process. Although, this new structure didn’t entirely work out, I think we will end up combining both our old and new database structure into a much better one. If anything, it’s been a learning process and will ultimately improve our code.