NFCommunicator is an Android app from Alsatian Consulting for offline one-message NFC card communication. The app reads, writes, and clears a single AES-256-GCM encrypted message on an NFC tag using a shared password. There is no account, no cloud sync, and no network access. Tags are compatible with any Android device that can access them through standard NDEF APIs, with additional raw MIFARE Classic support on handsets that expose that tech.
NFCommunicator stores one AES-256-GCM encrypted plain-text message on an NFC tag and reads it back using a shared password. Core operations are:
- Read: hold the tag to the phone on the Read tab. If the tag contains the app's encrypted message, the payload is cached immediately. If a password is already entered it is tried automatically; otherwise you enter the password afterward to decrypt.
- Write: enter the password twice, type a plain-text message, review the estimated encrypted size for both NDEF and raw MIFARE Classic storage, tap
Write to Card, and scan the tag. - Clear Card: available from both tabs; overwrites NDEF tags with an empty NDEF message or zeros the app's raw MIFARE Classic storage region.
NDEF mode: works with any NFC tag Android can access through standard NDEF APIs. Non-NDEF tags can be NDEF-formatted when Android exposes NdefFormatable.
Raw MIFARE Classic mode: preferred automatically when the scanned tag exposes MifareClassic on the device. Intentionally skips sector 0 and all sector trailer blocks to avoid manufacturer data and ACL blocks, giving the app a contiguous storage region for one encrypted message.
- Pending scans lock the active form fields and tabs so the captured password/message cannot drift before the tag is processed.
- The app gives immediate haptic feedback on tag detection plus a success/error toast and vibration when the NFC operation completes.
- Password fields are cleared after each completed read, write, or clear operation.
- The read screen lets you select or copy the decrypted message.
- When the last scanned tag exposes an exact capacity, the write screen shows the maximum unencrypted character count that fits on that backend and keeps a live character count for the message draft.
- Raw MIFARE Classic tag summaries show both the app-usable storage estimate and the card's full reported geometry (such as
MIFARE Classic 1K, sector count, and total bytes). - The selected tab, write draft, and last scanned tag summary survive activity recreation, but passwords and decrypted message text remain memory-only and are not restored from saved state.
- Reader mode stays active while the app is in the foreground, so tags produce passive detection feedback and update the last scanned tag summary even before Read, Write, or Clear is armed.
- On the Read tab, passive detection reads and caches compatible encrypted payloads automatically instead of waiting for a manual scan-arm action.
- If a card is readable but does not contain one of this app's encrypted messages, the read flow reports that directly.
- Failure states include a secondary diagnostic explanation when the app can narrow the cause to handset support limits, common-key authentication failure, payload incompatibility, or a lost tag during I/O.
NFCommunicator does not import or export files. The encrypted payload is written to and read from NFC tags only. No data is stored on the device beyond the transient in-memory state for the current session.
Primary platform/framework: Android 8.0+ / Kotlin / Jetpack Compose / Gradle.
Key components:
MainActivity— single-activity entry point withsingleToplaunch mode; handles NFC foreground dispatch.- Compose UI with two tabs:
ReadandWrite. ViewModel-based state management; tab state, write draft, and last tag summary survive configuration changes.- NFC access via
NfcAdapter,Tag,Ndef,NdefFormatable, andMifareClassicAPIs. - AES-256-GCM encryption with PBKDF2-HMAC-SHA256 key derivation; per-message random salt and nonce.
Dependencies: Jetpack Compose BOM, androidx.compose.material3, androidx.compose.material:material-icons-extended, androidx.lifecycle:lifecycle-viewmodel-compose, androidx.lifecycle:lifecycle-runtime-compose, com.google.android.material.
Minification: R8 enabled in release builds. FLAG_SECURE enabled in release; disabled in the screenshot build type.
- Encryption: AES-256-GCM. Key derivation: PBKDF2-HMAC-SHA256. Per-message random salt and nonce ensure no two ciphertexts are identical.
- The payload format is self-describing: the app encodes salt, nonce, and ciphertext together so that Read can detect compatible payloads and report incompatible ones clearly.
- Storage backend is selected automatically: raw MIFARE Classic when available, NDEF otherwise.
Open the installed NFCommunicator app from the Android launcher.
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apkNot applicable. NFCommunicator is an Android UI app.
- Android 8.0+ (min SDK 26), targeting Android 16 (SDK 36)
- An NFC-capable Android device
- For building from source: Android Studio or Gradle with JDK 17, Android SDK with API 36
Install the pre-built APK from the GitHub releases page:
adb install -r NFCommunicator-1.0-release.apkOr sideload the APK directly on the device. The signed AAB (NFCommunicator-1.0-release.aab) is the Play Store artifact; the APK is for direct installs.
Signed artifacts are available in playstore/release/1.0/. Verify with:
sha256sum -c playstore/release/1.0/SHA256SUMS.txtgit clone https://github.com/AlsatianConsulting/NFCommunicator.git
cd NFCommunicator
# Debug build
./gradlew assembleDebug
# Install on connected device
adb install -r app/build/outputs/apk/debug/app-debug.apk| File | Purpose |
|---|---|
keystore.properties |
Release signing; do not commit. Default path: ~/.config/nfccommunicator/keystore.properties |
local.properties |
Set sdk.dir= to your Android SDK path |
Override the default signing properties path via environment variable:
export NFC_COMMUNICATOR_SIGNING_PROPERTIES=/path/to/nfcommunicator-signing.propertiesNFC_COMMUNICATOR_SIGNING_PROPERTIES=/path/to/nfcommunicator-signing.properties \
./gradlew assembleRelease bundleReleasePlace keystore.properties at ~/.config/nfccommunicator/keystore.properties (or at the path pointed to by NFC_COMMUNICATOR_SIGNING_PROPERTIES) with the keystore next to it.
./gradlew assembleScreenshotNo external network access is required at runtime. Gradle dependencies are fetched from Maven Central at build time.
- Launch NFCommunicator. Two tabs appear:
ReadandWrite. - No permissions prompt is required beyond NFC access (enabled at the system level). Grant notification permission if prompted.
- Reader mode activates immediately in the foreground; passive tag detection begins.
- Read an encrypted tag: on the Read tab, hold the tag to the phone. If the tag contains the app's encrypted message, it is cached. Enter the password and tap Decrypt.
- Write an encrypted message: on the Write tab, enter the password twice, type your message, tap
Write to Card, and scan the tag. - Clear a tag: tap
Clear Cardfrom either tab, then scan the tag to erase the app's payload.
Write tab → enter password → confirm password → type message → tap "Write to Card" → scan tag
Read tab → scan tag → enter password → view decrypted message → copy to clipboard
Either tab → tap "Clear Card" → scan tag → payload erased
| Setting | Location | Description | Default |
|---|---|---|---|
FLAG_SECURE |
build.gradle.kts build type |
Blocks screenshots in production | Enabled in release |
| NFC foreground dispatch | MainActivity |
Keeps reader mode active in foreground | Always on |
- No data is persisted to device storage.
- Passwords and decrypted message text are in-memory only and are not restored after activity recreation.
- Tab selection, write draft, and last scanned tag summary survive configuration changes (device rotation) but are not saved across app restarts.
- Nothing leaves the device.
Screenshots are available in playstore/screenshots/selected/:
NFCommunicator
Version 1.0
(c) Alsatian Consulting, LLC 2026
GitHub: https://github.com/AlsatianConsulting
Website: https://www.alsatian.consulting
Production GitHub repository must be selected before release wiring or code signing.
Run the manual installation steps above. Confirm ANDROID_HOME or sdk.dir in local.properties is set correctly.
- Confirm NFC is enabled on the device (Settings > Connected Devices > NFC).
- Confirm the app is in the foreground when scanning (NFC foreground dispatch is active only while the app is on screen).
- Check Android logcat for NFC dispatch errors:
adb logcat -s NfcDispatch NFCommunicator.
- If a card reads as incompatible, it either has no payload or was written by a different app or format.
- Verify the password used for reading matches the password used for writing.
- If MIFARE Classic authentication fails, the tag may use non-default sector keys; the app uses standard transport keys.
| Permission | Purpose |
|---|---|
android.permission.NFC |
Required for all tag operations |
android.permission.VIBRATE |
Haptic feedback on scan events |
No additional permissions are required. If NFC is unavailable or disabled, the app will not function.
# Unit tests
./gradlew :app:testDebugUnitTest
# Build for screenshot validation
./gradlew assembleScreenshotRelease artifact verification:
sha256sum -c playstore/release/1.0/SHA256SUMS.txt| File | SHA-256 |
|---|---|
NFCommunicator-1.0-release.apk |
b4c76a9480a922b94bfe498c8502c38ddae4a74eb35675fae693760d6e5be9b0 |
NFCommunicator-1.0-release.aab |
c4baf5eb3bc97683048dc2de90cf15b8ddbba479175d4c6b02f04b9aafb0bbc5 |
NFCommunicator/
├── README.md
├── RELEASE.md
├── LICENSE
├── build.gradle.kts — root build
├── settings.gradle.kts
├── gradle.properties
├── keystore.properties.example
├── app/
│ ├── build.gradle.kts — versionCode=1, versionName=1.0, minSdk=26, compileSdk=36
│ ├── lint.xml
│ └── src/main/
│ └── AndroidManifest.xml
└── playstore/
├── metadata/ — Play Store listing text
├── screenshots/ — selected/ and raw/
├── release/1.0/ — signed APK, AAB, SHA256SUMS.txt
├── privacy-policy.html
├── icon-512x512.png
└── feature-graphic-1024x500.png
- One message per card: only one encrypted payload can be stored per tag; there is no multi-message or multi-key mode.
- Offline only: no accounts, sync, or stored passwords. Password sharing between sender and recipient must happen out-of-band.
- MIFARE Classic handset dependency: raw MIFARE Classic support requires the handset to expose the
MifareClassictech. Some devices (particularly those using certain NXP NFC controller firmware) do not expose this API even for compatible tags. - No key management: the app does not store, manage, or recover passwords. A forgotten password means the encrypted payload on the tag is unrecoverable.
- Android 8.0+ required:
minSdk = 26.
- Encryption: AES-256-GCM. Key derivation: PBKDF2-HMAC-SHA256. Per-message random salt and nonce.
- No data stored on device: passwords and decrypted messages are in-memory only and cleared after each operation.
- No network access: no internet permission is declared.
allowBackup="false"anddataExtractionRulesprevent OS-level backup of app data.FLAG_SECUREblocks screenshot and screen-recording capture in production builds.- Nothing leaves the device unless the user copies the decrypted message to another app.
- Initial release.
- AES-256-GCM encryption with PBKDF2-HMAC-SHA256 key derivation.
- NDEF read/write/clear.
- Raw MIFARE Classic read/write/clear on compatible handsets.
- Automatic backend selection (MIFARE Classic preferred when available).
- Dark Material 3 theme built from
#E87722. - Passive foreground tag detection on Read tab.
- Clear diagnostics for unsupported tags, wrong passwords, payload format errors, and tag-loss events.
Latest signed artifact: playstore/release/1.0/NFCommunicator-1.0-release.aab (Play Store) and playstore/release/1.0/NFCommunicator-1.0-release.apk (direct install).
Copyright (c) Alsatian Consulting, LLC.
Licensed under the Apache License, Version 2.0. See LICENSE.