Overview
Working with an Encrypted Database
Encrypting an Existing Database
Decrypting an Encrypted Database
Custom Cipher Implementations
From version 1.2.0, Xodus supports database encryption.
Xodus supports two algorithms out of the box: Salsa20 and ChaCha20. Salsa20 is one of the two best algorithms (along with Rabbit) submitted to the eSTREAM project in "profile 1" (Stream ciphers for software applications with high throughput requirements).
ChaCha20 is a variant of Salsa20 which tends to be even more secure while achieving the same or slightly better performance. There are several major implementations of ChaCha20, including Google's selection of ChaCha20 as a replacement for RC4 in TLS and its inclusion in OpenSSH.
Both built-in implementations (Salsa20 and ChaCha20) are provided by the Legion of the Bouncy Castle.
You can also plug in an implementation for a custom algorithm as long as you use a symmetric stream cipher.
If you want to use either Salsa20 or ChaCha20, add a dependency for the xodus-crypto jar to your application. Otherwise, you have to provide your own cipher implementation (see Custom Cipher Implementations). When opening or creating a database, your application should configure the cipher ID, cipher key and cipher basic IV (initialization vector). Opening a database can look as follows:
final EnvironmentConfig config = new EnvironmentConfig();
config.setCipherId("jetbrains.exodus.crypto.streamciphers.ChaChaStreamCipherProvider");
config.setCipherKey("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f");
config.setCipherBasicIV(314159262718281828);
try (Environment environment = Environments.newInstance("/Users/me/.myAppData", config)) {
// ...
}
You can also pass the cipher ID, cipher key, and cipher basic IV to the application using the corresponding
system properties exodus.cipherId
, exodus.cipherKey
, and exodus.cipherBasicIV
.
The cipher ID defines the stream cipher type (algorithm). The ID is an arbitrary string,
but it's recommended to use the fully qualified name of the
StreamCipherProvider
implementation. StreamCipherProvider
is used internally to create instances of
StreamCipher.
StreamCipher
instances are initialized with the cipher key and cipher basic IV. The key is expected to be a hex string
representing a byte array which is passed to StreamCipher.init(byte[], long)
. The length of the key
depends on the algorithm (cipher ID). Salsa20 accepts both 128-bit and 256-bit keys, whereas
ChaCha20 accepts only 256-bit keys. The basic IV is expected to be a random (pseudo-random) and unique
long value. Basic IV is used to calculate relative IVs which are passed to
StreamCipher.init(byte[], long)
.
To work with an encrypted Entity Store or
Virtual File System, nothing special is
needed. Just use an instance of Environment
that is opened with cipher parameters as in the sample above
to create an instance of PersistentEntityStore
or VirtualFileSystem
. Blob files will be encrypted
as well as .xd
files.
The cipher parameters cannot be changed during the life of the database. If your application opens a
plain (unencrypted) database using cipher parameters, opens an encrypted database without
cipher parameters, or opens an encrypted database with different cipher parameters, an
InvalidCipherParametersException
exception is thrown. The database is not affected.
You can encrypt or decrypt an existing database using the Scytale
tool. Download the
xodus-tools jar for version 1.2.0 or later
and run:
./java -jar xodus-tools.jar scytale
You see a usage message, which is self-explanatory:
Usage: Scytale [options] source target key basicIV [cipher]
Source can be archive or folder
Cipher can be 'Salsa' or 'ChaCha', 'ChaCha' is default
Options:
-g use gzip compression when opening archive
-z make target an archive
-o overwrite target archive or folder
For example, to encrypt a database located at /Users/me/.myAppData
using the ChaCha20 cipher and the same parameters
as in the sample above, run:
./java -jar xodus-tools.jar scytale /Users/me/.myAppData /Users/me/.myAppData/encrypted 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 314159262718281828
The encrypted database is created in /Users/me/.myAppData/encrypted
.
The Scytale
tool can also be used to decrypt an encrypted database:
./java -jar xodus-tools.jar scytale /Users/me/.myAppData/encrypted /Users/me/.myAppData 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 314159262718281828
The decrypted database is created in /Users/me/.myAppData
. The database would not be decrypted if it is encrypted
with cipher parameters different from specified. In that case, the tool would fail with InvalidCipherParametersException
.
In terms of Service Provider Interfaces,
custom cipher implementations are services. A custom cipher implementation should define
implementations of the StreamCipherProvider
abstract class and the StreamCipher
interface.
StreamCipherProvider
is used to create instances of StreamCipher
initialized with a key
and IV. Any StreamCipherProvider
implementation is discoverable by its ID. This ID can be an
arbitrary string, but it's recommended to use the fully qualified name of the
StreamCipherProvider
implementation as the ID.
To plug in your custom cipher, list the fully-qualified name of the StreamCipherProvider
implementation
in the META-INF/services/jetbrains.exodus.crypto.StreamCipherProvider
file
in your application jar or any jar in its CLASSPATH. For example, the
META-INF/services/jetbrains.exodus.crypto.StreamCipherProvider
file in
xodus-crypto.jar
contains the following references:
jetbrains.exodus.crypto.streamciphers.Salsa20StreamCipherProvider
jetbrains.exodus.crypto.streamciphers.ChaChaStreamCipherProvider
So if your application depends on xodus-crypto.jar
, it will be able to use Salsa20 or
ChaCha20 ciphers for database encryption.