GUARDIAN FREE · PLATFORM-SPECIFIC APIS

Protect the connection.
Bind the device key.

Guardian offers more than local scans. Add native TLS pinning, device identity and signing, and opt-in Android protection helpers without a Sentinel subscription.

✓ No Sentinel account required✓ Native and optional modules✓ Your configuration and backend

Core Android / iOS / Flutter: 3.2.0 · Android OkHttp & Gateway modules: 1.2.0 · Android Integrity module: 3.2.0

NETWORK SECURITY

SSL / TLS public-key pinning.

Pin the SHA-256 digest of a certificate’s SubjectPublicKeyInfo (SPKI). Pinning adds a restriction on top of normal certificate-chain, expiry, and hostname validation; it does not replace them.

Gradle — Android core and optional OkHttp module
// app/build.gradle.kts — use each module’s published version.
implementation("io.github.jimmyleonardo:aegis-guardian:3.2.0")
implementation("io.github.jimmyleonardo:aegis-guardian-okhttp:1.2.0")
Kotlin — dedicated pinned OkHttp client
// Network service: reuse the Guardian configured by your Application.
import com.jimmyleonardo.guardian.AegisGuardian
import com.jimmyleonardo.guardian.network.enableAegisPinning
import com.jimmyleonardo.guardian.network.attachAegis
import okhttp3.OkHttpClient

fun makePinnedClient(
    guardian: AegisGuardian,
    activePin: String,
    backupPin: String
): OkHttpClient {
    require(activePin != backupPin) { "Use a distinct backup key pin" }
    return OkHttpClient.Builder()
        .enableAegisPinning("api.example.com", activePin, backupPin)
        .attachAegis(guardian)
        .build()
}

// Supply real sha256/<Base64 SPKI digest> pins for your own HTTPS API.
// Retain and use the returned client for the requests you intend to pin.
// Handle setup errors; never silently substitute an unpinned client.
// Run requests via enqueue(), or execute() on a worker thread.
Reuse the configured client and Guardian instance.

Use the app-level setup from the free guide. Android attachAegis installs its policy interceptor only when blockIfRooted or blockIfEmulator is enabled; set those flags deliberately. iOS evaluates the supplied Guardian policy before each top-level request. Pinning by itself does not enable Sentinel, Play Integrity, or gateway encryption.

iOS requires two distinct pins per exact ASCII DNS host; wildcard and unconfigured hosts are rejected. Its client rejects HTTP and follows redirects only on the same HTTPS host and port. Android’s helper replaces the builder’s CertificatePinner; for several domains, build a single native pinner with all entries.

YOUR HOST, YOUR KEYS

Configure active and backup pins.

Obtain the certificate through a trusted channel, generate the SPKI digest, and prefix it with sha256/. Ship a distinct backup public-key pin before rotating keys. Example domains and function parameters above must be replaced with your own configuration.

Shell — SHA-256 SPKI pin from a trusted certificate
# Use a certificate obtained through a trusted channel.
openssl x509 -in server.pem -pubkey -noout |
  openssl pkey -pubin -outform DER |
  openssl dgst -sha256 -binary |
  openssl base64 -A

# Prefix the output with sha256/.
# Repeat with a DIFFERENT backup public key's certificate before rotation.

Handle configuration and connection failures explicitly. Keep standard CA and hostname validation enabled. Do not retry a pin failure through an unpinned client. Neither native client automatically pins unrelated clients, third-party traffic, or web views.

DEVICE CONTEXT

Read an app-scoped identifier.

Device ID is available without key enrollment, App Attest, or Sentinel. It is useful for application context, but is not a permanent hardware identifier or an authentication credential.

Read device identity
// On a worker thread, using your Application's shared guardian:
val deviceId = guardian.getDeviceId()

// This is a best-effort app identifier, not an authentication credential.
// Handle failures and avoid putting identifiers in unrestricted logs.

Android’s identifier depends on its Widevine / ANDROID_ID source; iOS uses an app-scoped Keychain ID. Lifetimes differ, and continuity after reinstall, reset, or data loss is not guaranteed. Treat persistent identifiers appropriately in your app’s privacy design.

CRYPTOGRAPHIC REQUEST CONTEXT

Enroll a key, then sign fresh messages.

Use P-256 ECDSA/SHA-256 signatures with your backend. Authenticate enrollment first; the server must verify trust and bind each signed message to its session, purpose, body, fresh challenge, and expiry. A key or signature alone does not establish app integrity.

Device-key enrollment and separate signing operations
import com.jimmyleonardo.guardian.AegisGuardian

// Enrollment operation, on a worker thread, with a fresh backend challenge.
fun enrollDeviceKey(
    guardian: AegisGuardian,
    challenge: ByteArray
): Result<List<ByteArray>> {
    require(challenge.size in 1..128)
    return guardian.createDeviceKeySafely(challenge)
}

// Separate operation after the backend has accepted the enrollment.
fun signBackendChallenge(
    guardian: AegisGuardian,
    serverBoundMessage: ByteArray
): Result<ByteArray> = guardian.signWithDeviceKeySafely(serverBoundMessage)

// Enrollment returns a leaf-first DER certificate chain.
// Signing returns an ECDSA P-256/SHA-256 signature in ASN.1 DER.
// Creating a device key replaces the existing key. Do not do it each launch.
Key enrollment is not application initialization.

Android key creation replaces the current signing key. Do not create it on every startup or request. Verify Android attestation trust, challenge, and identity on your server; a returned certificate chain does not guarantee hardware-backed attestation. iOS Secure Enclave signing is separate from App Attest, and its enrollment returns a public key rather than a certificate chain.

Run native Keychain and Keystore work off the main thread. Flutter key operations use the native worker queues. Production iOS signing requires a supported device by default; software-key mode is an explicit development option, not an automatic fallback.

ANDROID NATIVE HELPERS

Filter obscured touches and clear owned buffers.

Android includes opt-in tapjacking helpers and best-effort ByteArray / CharArray wiping in the core module. The iOS SDK exposes no equivalent helpers, and the Flutter plugin does not wrap them.

Android — full and partial touch-occlusion handling
import android.view.MotionEvent
import com.jimmyleonardo.guardian.ui.enableAegisTapjackingProtection
import com.jimmyleonardo.guardian.ui.isAegisTouchTrusted

// On the UI thread, on the View handling your sensitive action:
val protection = sensitiveView.enableAegisTapjackingProtection()
if (protection.isFailure) {
    sensitiveView.isEnabled = false
}

// In your Activity: also reject partially obscured touches on API 29+.
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
    if (!event.isAegisTouchTrusted()) return true
    return super.dispatchTouchEvent(event)
}

// sensitiveView is your actual View; the helper does not replace listeners.
Android — clear an owned temporary buffer
import com.jimmyleonardo.guardian.crypto.wipe

val temporaryBytes = byteArrayOf(1, 2, 3)
try {
    // Use the owned buffer for your operation.
} finally {
    temporaryBytes.wipe()
}

// CharArray.wipe() is available too.
// This is best-effort; JVM/provider copies may still exist.

View filtering covers full occlusion; the MotionEvent helper adds partial-occlusion handling on API 29+. Memory wiping is best-effort and cannot guarantee that JVM, crypto-provider, Dart, or platform-channel copies are erased. Screen privacy has separate lifecycle requirements in the free guide.

OPTIONAL, NOT AUTOMATIC

Platform evidence and encrypted transport.

Google Play Integrity, Apple App Attest, and the Android encrypted Gateway transport are separate integrations. Their SDK helpers can be used with your own backend; they do not give you hosted Sentinel verification without a subscription.

Android — optional modules and their published versions
// app/build.gradle.kts — optional Android integrations:
implementation("io.github.jimmyleonardo:aegis-guardian-integrity:3.2.0")
implementation("io.github.jimmyleonardo:aegis-guardian-gateway:1.2.0")

// Add only modules you integrate. Installation alone activates nothing.
// Integrity needs your app's Google Play / Cloud configuration and backend.
// Gateway needs device enrollment and a matching HTTPS backend.

Play Integrity needs your app’s Google Play / Cloud setup and server verification. iOS includes App Attest helpers; assertions and enrollment must be verified by your backend. The Gateway module needs enrolled keys and a matching HTTPS backend; it is not a transparent replacement for every request and does not provide unlimited streaming. iOS has no matching Gateway module in this SDK; Flutter does not wrap the Android transport.

For managed Sentinel monitoring and assessments →

INTEGRATION QUESTIONS

Know what each integration enables.

Do TLS pinning and device signing need a Sentinel subscription?

No. The native SDK APIs and optional Android modules can be used independently of Sentinel. They require your own host pins, platform configuration, and backend where applicable. Hosted Sentinel assessments, premium Frida results, server-authorized app identity, and blocklists require a subscription.

Does installing Guardian automatically pin every network request?

No. Android pinning applies to the configured OkHttpClient; iOS pinning applies to AegisPinnedClient. It does not cover other networking clients or web views. The Flutter plugin does not wrap native pinning or automatically protect Dio and Dart http traffic.

Where should Android Guardian configuration be created?

Create the shared configuration in Application.onCreate() or your app dependency-injection container. Reuse it for screens, worker scans, and network services. The Builder creates an independent instance and does not replace the legacy init() singleton. Screen and touch protections need explicit UI integration.

ADD CENTRALIZED CONTROL

Bring the signals into Sentinel.

Connect premium detection, device investigations, and risk policies your backend can enforce.

Return to the free SDK guide →