SQLite.Encrypted for JDBC
org.sqlite.encrypted:sqlite-jdbc is based on Xerial SQLite JDBC and packages the official SQLite Encryption Extension native library.
Core rule: Activate SEE before opening an encrypted database, then use a keyed
openoverload or callkeyimmediately after an unkeyed open.
Codec support: SQLite.Encrypted currently supports only
aes256ofb. Pass a null codec to useaes256ofb; any codec argument supplied toopen,key, orrekeymust beaes256ofb.
Install
Add the SQLite.Encrypted Maven repository, then add the dependency:
repositories {
mavenCentral()
maven {
url = uri("https://packages.sourcegear.com/maven/releases")
}
}
dependencies {
implementation("org.sqlite.encrypted:sqlite-jdbc:<version>")
}
The package supports Windows x64, macOS x64 and ARM64, and Linux x64. Other Linux architectures and Android are not included yet.
Open and query an encrypted database
The ordinary JDBC APIs for queries, updates, prepared statements, and transactions are unchanged.
import java.sql.ResultSet;
import java.sql.Statement;
import org.sqlite.SQLiteConnection;
import org.sqlite.SQLiteEncrypted;
SQLiteEncrypted.activateSee(System.getenv("SQLITE_SEE_ACTIVATION_KEY"));
try (SQLiteConnection connection = SQLiteEncrypted.open(
"application.db", System.getenv("DATABASE_KEY"), null);
Statement statement = connection.createStatement()) {
statement.executeUpdate(
"CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY, body TEXT NOT NULL)");
statement.executeUpdate("INSERT INTO notes(body) VALUES ('hello')");
try (ResultSet rows = statement.executeQuery("SELECT id, body FROM notes")) {
while (rows.next()) {
System.out.println(rows.getString("body"));
}
}
}
The keyed open overload calls sqlite3_key_v3, closes the connection when that native call fails, and propagates the failure as SQLException. It does not place the database key in the JDBC URL and does not use a PRAGMA.
Open, then apply a key
Use an explicit key call when application structure requires an ordinary JDBC open. Call it before any SQL that reads the schema or database content.
import java.sql.DriverManager;
import org.sqlite.SQLiteConnection;
SQLiteConnection connection = (SQLiteConnection)
DriverManager.getConnection("jdbc:sqlite:application.db");
try {
connection.key("main", System.getenv("DATABASE_KEY"), null);
// The connection is now ready for ordinary JDBC operations.
} catch (Throwable failure) {
connection.close();
throw failure;
}
For compatibility with existing Xerial configuration, its password property is also routed to the checked key method. The Xerial hexkey_mode variants are not supported; use the byte-array overload for binary keys.
Open with PRAGMA
The PRAGMA flow remains available for existing integrations. Check both that the key statement produced a result set and that its first value is ok.
try (SQLiteConnection connection = (SQLiteConnection)
DriverManager.getConnection("jdbc:sqlite:application.db");
Statement statement = connection.createStatement()) {
statement.execute(
"PRAGMA activate_extensions = 'see-<activation-key>'");
boolean returnedResult = statement.execute(
"PRAGMA key = '<database-key>'");
if (!returnedResult) {
throw new IllegalStateException("SEE did not accept the database key");
}
try (ResultSet result = statement.getResultSet()) {
if (!result.next() || !"ok".equals(result.getString(1))) {
throw new IllegalStateException("SEE did not accept the database key");
}
}
}
If encryption support is unavailable or SEE cannot load the key, the key PRAGMA returns no row. Ignoring that result can leave code using a connection whose key was never applied. Prefer keyed open or explicit key for new integrations because both report the native failure directly.
Text, binary, and null keys
connection.key("main", "initial text key", null);
byte[] binaryKey = new byte[] {0x01, 0x00, 0x7f, (byte) 0xff};
connection.rekey("main", binaryKey, binaryKey.length, null);
connection.rekey("main", "replacement text key", null);
connection.rekey("main", (String) null, null); // decrypt
Strings use UTF-8; byte arrays preserve exact bytes and require a length. A null key decrypts on rekey. Use main and a null codec for the main database and the aes256ofb default.