Skip to content

Commit

Permalink
Provides methods to create kdbx using a key file cternes#68
Browse files Browse the repository at this point in the history
  • Loading branch information
mrozanc committed Sep 15, 2019
1 parent 4a354ae commit 542ecd4
Show file tree
Hide file tree
Showing 3 changed files with 366 additions and 6 deletions.
261 changes: 259 additions & 2 deletions src/main/java/de/slackspace/openkeepass/KeePassDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,136 @@ public static void write(KeePassFile keePassFile, String password, String keePas
}
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given file location.
* <p>
* If the KeePassFile cannot be encrypted an exception will be thrown.
*
* @param keePassFile
* the keePass model which should be written
* @param password
* the password to encrypt the database
* @param stream
* the target stream where the output will be written
* @see KeePassFile
*/
public static void write(KeePassFile keePassFile, String password, OutputStream stream) {
if (stream == null) {
throw new IllegalArgumentException("You must provide a stream to write to.");
}

write(keePassFile, password, (InputStream) null, stream);
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given file location.
* <p>
* If the KeePassFile cannot be encrypted an exception will be thrown.
*
* @param keePassFile
* the keePass model which should be written
* @param password
* the password to encrypt the database
* @param keyFile
* the keyfile to encrypt the database as stream
* @param keePassDatabaseFile
* the target location where the database file will be written
* @see KeePassFile
*/
public static void write(KeePassFile keePassFile, String password, File keyFile, String keePassDatabaseFile) {
if (keePassDatabaseFile == null || keePassDatabaseFile.isEmpty()) {
throw new IllegalArgumentException("You must provide a non-empty path where the database should be written to.");
}

try {
InputStream keyFileStream = null;
try {
keyFileStream = new FileInputStream(keyFile);
write(keePassFile, password, keyFileStream, new FileOutputStream(keePassDatabaseFile));
} finally {
if (keyFileStream != null) {
try {
keyFileStream.close();
} catch (IOException e) {
// Ignore
}
}
}
} catch (FileNotFoundException e) {
throw new KeePassDatabaseUnreadableException("Could not find database file", e);
}
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given file location.
* <p>
* If the KeePassFile cannot be encrypted an exception will be thrown.
*
* @param keePassFile
* the keePass model which should be written
* @param password
* the password to encrypt the database
* @param keyFile
* the keyfile to encrypt the database as stream
* @param stream
* the target stream where the output will be written
* @see KeePassFile
*/
public static void write(KeePassFile keePassFile, String password, File keyFile, OutputStream stream) {
if (stream == null) {
throw new IllegalArgumentException("You must provide a stream to write to.");
}

try {
InputStream keyFileStream = null;
try {
keyFileStream = new FileInputStream(keyFile);
write(keePassFile, password, keyFileStream, stream);
} finally {
if (keyFileStream != null) {
try {
keyFileStream.close();
} catch (IOException e) {
// Ignore
}
}
}
} catch (FileNotFoundException e) {
throw new KeePassDatabaseUnreadableException("Could not find database file", e);
}
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given file location.
* <p>
* If the KeePassFile cannot be encrypted an exception will be thrown.
*
* @param keePassFile
* the keePass model which should be written
* @param password
* the password to encrypt the database
* @param keyFileStream
* the keyfile to encrypt the database as stream
* @param keePassDatabaseFile
* the target location where the database file will be written
* @see KeePassFile
*/
public static void write(KeePassFile keePassFile, String password, InputStream keyFileStream, String keePassDatabaseFile) {
if (keePassDatabaseFile == null || keePassDatabaseFile.isEmpty()) {
throw new IllegalArgumentException("You must provide a non-empty path where the database should be written to.");
}

try {
write(keePassFile, password, keyFileStream, new FileOutputStream(keePassDatabaseFile));
} catch (FileNotFoundException e) {
throw new KeePassDatabaseUnreadableException("Could not find database file", e);
}
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given stream.
Expand All @@ -331,17 +461,144 @@ public static void write(KeePassFile keePassFile, String password, String keePas
* the keePass model which should be written
* @param password
* the password to encrypt the database
* @param keyFileStream
* the keyfile to encrypt the database as stream
* @param stream
* the target stream where the output will be written
* @see KeePassFile
*
*/
public static void write(KeePassFile keePassFile, String password, OutputStream stream) {
public static void write(KeePassFile keePassFile, String password, InputStream keyFileStream, OutputStream stream) {
if (stream == null) {
throw new IllegalArgumentException("You must provide a stream to write to.");
}

new KeePassDatabaseWriter().writeKeePassFile(keePassFile, password, keyFileStream, stream);
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given file location.
* <p>
* If the KeePassFile cannot be encrypted an exception will be thrown.
*
* @param keePassFile
* the keePass model which should be written
* @param keyFile
* the keyfile to encrypt the database as stream
* @param keePassDatabaseFile
* the target location where the database file will be written
* @see KeePassFile
*/
public static void write(KeePassFile keePassFile, File keyFile, String keePassDatabaseFile) {
if (keePassDatabaseFile == null || keePassDatabaseFile.isEmpty()) {
throw new IllegalArgumentException("You must provide a non-empty path where the database should be written to.");
}

try {
InputStream keyFileStream = null;
try {
keyFileStream = new FileInputStream(keyFile);
write(keePassFile, null, keyFileStream, new FileOutputStream(keePassDatabaseFile));
} finally {
if (keyFileStream != null) {
try {
keyFileStream.close();
} catch (IOException e) {
// Ignore
}
}
}
} catch (FileNotFoundException e) {
throw new KeePassDatabaseUnreadableException("Could not find database file", e);
}
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given file location.
* <p>
* If the KeePassFile cannot be encrypted an exception will be thrown.
*
* @param keePassFile
* the keePass model which should be written
* @param keyFile
* the keyfile to encrypt the database as stream
* @param stream
* the target stream where the output will be written
* @see KeePassFile
*/
public static void write(KeePassFile keePassFile, File keyFile, OutputStream stream) {
if (stream == null) {
throw new IllegalArgumentException("You must provide a stream to write to.");
}

try {
InputStream keyFileStream = null;
try {
keyFileStream = new FileInputStream(keyFile);
write(keePassFile, keyFileStream, stream);
} finally {
if (keyFileStream != null) {
try {
keyFileStream.close();
} catch (IOException e) {
// Ignore
}
}
}
} catch (FileNotFoundException e) {
throw new KeePassDatabaseUnreadableException("Could not find database file", e);
}
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given file location.
* <p>
* If the KeePassFile cannot be encrypted an exception will be thrown.
*
* @param keePassFile
* the keePass model which should be written
* @param keyFileStream
* the keyfile to encrypt the database as stream
* @param keePassDatabaseFile
* the target location where the database file will be written
* @see KeePassFile
*/
public static void write(KeePassFile keePassFile, InputStream keyFileStream, String keePassDatabaseFile) {
if (keePassDatabaseFile == null || keePassDatabaseFile.isEmpty()) {
throw new IllegalArgumentException("You must provide a non-empty path where the database should be written to.");
}

try {
write(keePassFile, keyFileStream, new FileOutputStream(keePassDatabaseFile));
} catch (FileNotFoundException e) {
throw new KeePassDatabaseUnreadableException("Could not find database file", e);
}
}

/**
* Encrypts a {@link KeePassFile} with the given password and writes it to
* the given stream.
* <p>
* If the KeePassFile cannot be encrypted an exception will be thrown.
*
* @param keePassFile
* the keePass model which should be written
* @param keyFileStream
* the keyfile to encrypt the database as stream
* @param stream
* the target stream where the output will be written
* @see KeePassFile
*
*/
public static void write(KeePassFile keePassFile, InputStream keyFileStream, OutputStream stream) {
if (stream == null) {
throw new IllegalArgumentException("You must provide a stream to write to.");
}

new KeePassDatabaseWriter().writeKeePassFile(keePassFile, password, stream);
write(keePassFile, null, keyFileStream, stream);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.zip.GZIPOutputStream;
Expand All @@ -21,26 +22,35 @@
import de.slackspace.openkeepass.processor.EncryptionStrategy;
import de.slackspace.openkeepass.processor.ProtectedValueProcessor;
import de.slackspace.openkeepass.stream.HashedBlockOutputStream;
import de.slackspace.openkeepass.util.ByteUtils;

public class KeePassDatabaseWriter {

private static final String UTF_8 = "UTF-8";

public void writeKeePassFile(KeePassFile keePassFile, String password, OutputStream stream) {
writeKeePassFile(keePassFile, password, null, stream);
}

public void writeKeePassFile(KeePassFile keePassFile, InputStream keyStream, OutputStream stream) {
writeKeePassFile(keePassFile, null, keyStream, stream);
}

public void writeKeePassFile(KeePassFile keePassFile, String password, InputStream keyStream, OutputStream stream) {
try {
if (!validateKeePassFile(keePassFile)) {
throw new KeePassDatabaseUnwriteableException(
"The provided keePassFile is not valid. A valid keePassFile must contain of meta and root group and the root group must at least contain one group.");
"The provided keePassFile is not valid. A valid keePassFile must contain of meta and root group and the root group must at least contain one group.");
}

KeePassHeader header = new KeePassHeader(new RandomGenerator());
byte[] hashedPassword = hashPassword(password);
byte[] keyHash = getKeyHash(password, keyStream);

byte[] keePassFilePayload = marshallXml(keePassFile, header);
ByteArrayOutputStream streamToZip = compressStream(keePassFilePayload);
ByteArrayOutputStream streamToHashBlock = hashBlockStream(streamToZip);
ByteArrayOutputStream streamToEncrypt = combineHeaderAndContent(header, streamToHashBlock);
byte[] encryptedDatabase = encryptStream(header, hashedPassword, streamToEncrypt);
byte[] encryptedDatabase = encryptStream(header, keyHash, streamToEncrypt);

// Write database to stream
stream.write(encryptedDatabase);
Expand All @@ -57,14 +67,26 @@ public void writeKeePassFile(KeePassFile keePassFile, String password, OutputStr
}
}

private byte[] getKeyHash(String password, InputStream keyFileStream) throws UnsupportedEncodingException {
byte[] hashedPassword = new byte[0];
if (password != null) {
hashedPassword = hashPassword(password);
}
byte[] protectedBuffer = new byte[0];
if (keyFileStream != null) {
protectedBuffer = new KeyFileReader().readKeyFile(keyFileStream);
}
return ByteUtils.concat(hashedPassword, protectedBuffer);
}

private byte[] hashPassword(String password) throws UnsupportedEncodingException {
byte[] passwordBytes = password.getBytes(UTF_8);
return Sha256.hash(passwordBytes);
}

private byte[] encryptStream(KeePassHeader header, byte[] hashedPassword, ByteArrayOutputStream streamToEncrypt) throws IOException {
CryptoInformation cryptoInformation = new CryptoInformation(KeePassHeader.VERSION_SIGNATURE_LENGTH, header.getMasterSeed(), header.getTransformSeed(),
header.getEncryptionIV(), header.getTransformRounds(), header.getHeaderSize());
header.getEncryptionIV(), header.getTransformRounds(), header.getHeaderSize());

return new Decrypter().encryptDatabase(hashedPassword, cryptoInformation, streamToEncrypt.toByteArray());
}
Expand Down
Loading

0 comments on commit 542ecd4

Please sign in to comment.