- Bump OPENSSL_VERSION default from 3.0.8 to 3.1.2 - Update SHA256 hash for openssl-3.1.2.tar.gz - Update all compliance checks to validate OpenSSL 3.1.x series - Update docs: README, install.md, CLAUDE.md, test READMEs - Previous 3.0.8 had only FIPS 140-2 (Cert #4282); 3.1.2 is the first OpenSSL with full FIPS 140-3 validation (Cert #4985, valid through March 2030)
5.9 KiB
FIPS SQLCipher — Integration Guide
FIPS 140-3 compliant SQLCipher with OpenSSL 3.1.2 for Android and iOS.
Android
AAR Contents
fips-sqlcipher.aar
├── jni/<abi>/
│ ├── libcrypto.so, libssl.so, libsqlcipher.so
│ ├── libfips.so (FIPS provider — never strip)
│ └── libfips_jni.so (JNI bridge for Kotlin helpers)
├── classes.jar
│ ├── FIPSSQLCipher — one-call FIPS init
│ ├── FipsDatabase — SupportSQLiteDatabase over FIPS SQLCipher
│ └── FipsSQLiteOpenHelperFactory — Room integration
├── assets/
│ ├── fips/<abi>/fipsmodule.cnf (per-ABI HMAC fingerprints)
│ └── native/{include,src}/ (C sources for custom JNI)
└── AndroidManifest.xml
Step 1: Add the AAR
Copy fips-sqlcipher.aar to app/libs/:
// app/build.gradle.kts
dependencies {
implementation(files("libs/fips-sqlcipher.aar"))
}
android {
packaging.jniLibs {
keepDebugSymbols += setOf("**/libfips.so") // stripping breaks FIPS HMAC
}
}
Step 2: Initialize at startup
// Application.onCreate() or Activity.onCreate() — before any crypto or DB operations
FIPSSQLCipher.init(this)
That's it. FIPSSQLCipher.init() handles:
- Extracts per-ABI
fipsmodule.cnffrom assets to internal storage - Generates
openssl.cnfwith absolute.includepaths (required because Android'sAT_SECUREblocksOPENSSL_CONFenv vars) - Extracts
fips.sofrom the APK (renames fromlibfips.so— OpenSSL convention) - Loads native libraries in the correct order:
libcrypto.so→libfips_jni.so→ FIPS init →libsqlcipher.so - Calls
OSSL_PROVIDER_set_default_search_path()+OSSL_LIB_CTX_load_config()+OSSL_PROVIDER_load("fips")programmatically
Step 3: Use with Room (optional)
// AppDatabase.kt
@Database(entities = [MyEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun myDao(): MyDao
companion object {
fun create(context: Context): AppDatabase =
Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
.openHelperFactory(FipsSQLiteOpenHelperFactory("your-encryption-key"))
.build()
}
}
FipsSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory and wraps all database operations through FIPS-validated SQLCipher encryption. Room handles migrations, DAOs, and queries normally — the encryption is transparent.
Direct database access (without Room)
val db = FipsDatabase.open("/path/to/db", "encryption-key")
db.execSQL("CREATE TABLE demo (id INTEGER PRIMARY KEY, msg TEXT)")
db.insert("demo", SQLiteDatabase.CONFLICT_NONE, contentValuesOf("msg" to "hello"))
val cursor = db.query("SELECT * FROM demo")
db.close()
Custom native JNI (advanced)
The AAR bundles C source files in assets/native/ for apps that need custom native integration. Extract and add to your CMakeLists:
add_library(your_jni SHARED
fips_init.c
fips_init_android.c
)
target_include_directories(your_jni PRIVATE path/to/include)
target_compile_definitions(your_jni PRIVATE SQLITE_HAS_CODEC)
target_link_libraries(your_jni PRIVATE crypto sqlcipher log)
SQLITE_HAS_CODEC is required to declare sqlite3_key() / sqlite3_rekey().
16 KB page alignment (Android 15+)
Add to your native CMakeLists for Google Play compatibility:
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
The AAR's bundled libraries are already 16 KB aligned.
iOS
Step 1: Add the XCFramework
Drag FIPSSQLCipher.xcframework into your Xcode project. Set Embed & Sign.
Step 2: Link system frameworks
Build Phases > Link Binary With Libraries:
Security.frameworklibz.tbd
Step 3: Disable stripping (critical)
Build Settings:
STRIP_INSTALLED_PRODUCT = NO
DEAD_CODE_STRIPPING = NO
Stripping invalidates the FIPS provider's HMAC integrity check.
Step 4: Copy fips.dylib into the app bundle
Add a Run Script build phase:
FIPS_FW="${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/FIPSSQLCipher.framework"
FIPS_DST="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/fips"
mkdir -p "$FIPS_DST"
if [ -f "$FIPS_FW/fips.dylib" ]; then
cp "$FIPS_FW/fips.dylib" "$FIPS_DST/"
elif [ -f "$FIPS_FW/Resources/fips/fips.dylib" ]; then
cp "$FIPS_FW/Resources/fips/fips.dylib" "$FIPS_DST/"
fi
Step 5: Add C sources + bridging header
Add fips_init.c and fips_init_ios.c to your Xcode target.
Create a bridging header:
// BridgingHeader.h
#define SQLITE_HAS_CODEC 1
#import "fips_init.h"
#import <sqlite3.h>
Step 6: Initialize at launch
Option A — Use the Swift helper (recommended):
Copy src/ios/FIPSSQLCipher.swift into your project:
// App init or AppDelegate
try FIPSSQLCipher.initialize()
Option B — Call C directly:
let fipsDir = Bundle.main.resourcePath! + "/fips"
let rc = fips_init_ios(fipsDir)
guard rc == FIPS_INIT_OK else {
fatalError("FIPS: \(String(cString: fips_init_status_str(rc)))")
}
Verifying FIPS at runtime
#include "fips_init.h"
assert(fips_provider_is_active()); // provider loaded?
assert(fips_self_test_rerun()); // POST/KAT still passing?
Android (Kotlin):
assert(FIPSSQLCipher.isActive) // provider loaded and active?
assert(FIPSSQLCipher.selfTest()) // POST/KAT re-run passes?
What breaks FIPS
| Action | Effect | Fix |
|---|---|---|
| Stripping libfips.so / fips.dylib | Invalidates HMAC | keepDebugSymbols / STRIP_INSTALLED_PRODUCT=NO |
| UPX / binary compression | Mutates sections | Never compress FIPS modules |
Missing SQLITE_HAS_CODEC define |
sqlite3_key() undeclared |
Add to compile definitions |
Versions
- OpenSSL 3.1.2 (FIPS 140-3 validated, CMVP Cert #4985), SQLCipher v4.6.1
- Android: API 24+, iOS: 17.0+