Skip to main content

Certificate Pinning & Integrity

Defend against MITM attacks with certificate pinning and protect your app against runtime tampering with integrity checks and data sealing.

Certificate Pinning

Configure Pins

let pin = PrismCertificatePin(
    host: "api.example.com",
    publicKeyHash: "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=",
    backupHashes: ["sha256/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC="],
    expiresAt: Date.now.addingTimeInterval(365 * 24 * 3600)
)

Hash a Public Key

let hash = PrismCertificatePin.hash(publicKeyDER: publicKeyData)
// → "abc123..." (SHA-256 base64)

Pinning Policies

PolicyBehavior
.strictReject connection if hash mismatch
.reportOnlyAllow connection, call violation handler
.trustFirstUsePin first-seen hash, reject changes

Validate with URLSession

let validator = PrismPinningValidator(
    pins: [pin],
    policy: .strict,
    onViolation: { result in
        logger.warning("Pin mismatch: \(result.host)")
    }
)

let delegate = PrismCertificatePinningDelegate(validator: validator)
let session = URLSession(
    configuration: .default,
    delegate: delegate,
    delegateQueue: nil
)

// All requests through this session are pinned
let (data, _) = try await session.data(from: url)

Dynamic Pin Management

// Add pin at runtime
await validator.addPin(PrismCertificatePin(
    host: "new-api.example.com",
    publicKeyHash: "sha256/..."
))

// Remove pin
await validator.removePin(forHost: "old-api.example.com")

Manual Validation

let result = await validator.validate(
    publicKeyHash: "sha256/...",
    forHost: "api.example.com"
)

print(result.isValid)       // Bool
print(result.matchedHash)   // String? — which pin matched
print(result.serverHash)    // String — server's actual hash

Integrity Checker

Detect jailbreaks, debuggers, simulators, and reverse engineering tools at runtime.

Full Check

let checker = PrismIntegrityChecker()

let violations = checker.checkAll()
for v in violations {
    print("\(v.kind): \(v.detail)")
}

if checker.isSecure {
    // No violations found
}

Individual Checks

checker.isJailbroken()              // suspicious paths, writable /private
checker.isDebuggerAttached()        // sysctl P_TRACED flag
checker.isSimulator()               // TARGET_OS_SIMULATOR
checker.hasReverseEngineeringTools() // frida, objection

Detection Methods

CheckTechnique
JailbreakSuspicious file paths (/Applications/Cydia.app, /usr/sbin/sshd), writable system directories, URL schemes
Debuggersysctl with CTL_KERN / KERN_PROC checking P_TRACED
SimulatorPreprocessor #if targetEnvironment(simulator)
Reverse EngineeringKnown tool binaries (frida-server, objection), DYLD_INSERT_LIBRARIES

Integrity Policy

// Default — log only
let policy = PrismIntegrityPolicy.default  // actions: [.log]

// Strict — log + wipe + notify
let policy = PrismIntegrityPolicy.strict   // actions: [.log, .wipeSecureStore, .notify]

Data Seal

HMAC-SHA256 tamper-evident sealing for Codable values — proves data hasn’t been modified since sealing.

Seal and Unseal

let key = SymmetricKey(size: .bits256)
let seal = PrismDataSeal(key: key)

struct UserToken: Codable, Sendable, Equatable {
    let value: String
    let expiresAt: Date
}

// Seal
let sealed = try seal.seal(UserToken(value: "abc", expiresAt: .now))

// Unseal (throws if tampered)
let token = try seal.unseal(UserToken.self, from: sealed)

Verify Without Unsealing

let data = Data("important config".utf8)
let sealed = seal.sealData(data)

if seal.verify(sealed) {
    // Integrity confirmed
}

Raw MAC Verification

let mac = Data(HMAC<SHA256>.authenticationCode(for: data, using: key))
let valid = seal.verify(data: data, mac: mac)

File Integrity

Track SHA-256 hashes of critical files — detect unexpected modifications.
let integrity = PrismFileIntegrity()

// Register a file (stores hash in keychain)
try integrity.registerFile(at: configURL)

// Verify integrity
let valid = try integrity.verify(at: configURL)

// Verify all registered files
let results = try integrity.verifyAll()
for (url, isValid) in results {
    if !isValid {
        logger.error("File tampered: \(url.lastPathComponent)")
    }
}

// Update hash after legitimate change
try integrity.updateHash(for: configURL)

// Unregister
integrity.unregister(at: configURL)
Jailbreak detection is not foolproof — sophisticated tools can bypass these checks. Use defense-in-depth: combine integrity checks with certificate pinning, data sealing, and server-side validation.