Skip to main content

Key Hiding

Protect sensitive AppTrove SDK credentials (SDK Key, Secret ID, Secret Key) by storing them securely using iOS Keychain Services. This prevents keys from being exposed in plain text within your app's source code, reducing the risk of unauthorized access and fraudulent activities.

Purpose

Key hiding is essential for securing your AppTrove SDK integration:

  • Safeguard SDK Key, Secret ID, and Secret Key from reverse engineering
  • Store credentials in iOS Keychain Services for maximum security
  • Automatically retrieve keys during app initialization
  • Provide fallback mechanisms for key management
  • Prevent fraudulent activities by securing SDK credentials
note

This feature works in conjunction with SDK Signing to provide comprehensive security for your Trackier SDK integration.

Prerequisites

  • AppTrove SDK: iOS SDK properly integrated
  • iOS: Version 12.0 or later
  • Xcode: Latest version
  • Trackier Panel: SDK Key, Secret ID, and Secret Key retrieved
  • Permissions: Internet access

Implementation

Follow these steps to implement secure key storage in your iOS app:

Step 1: Add Keychain Services

Import the Security framework in your project:

AppDelegate.swift
import UIKit
import Security
import TrackierSDK

Step 2: Create Secure Storage Service

Create a service to handle secure storage operations using iOS Keychain:

SecureStorageService.swift
import Foundation
import Security

class SecureStorageService {
static let shared = SecureStorageService()

private let serviceName = "com.trackier.sdk"
private let accountName = "trackier_credentials"

private init() {}

// Store the Trackier SDK key securely
func storeTrackierKey(_ key: String) -> Bool {
return store(key: key, for: "trackier_sdk_key")
}

// Retrieve the Trackier SDK key securely
func getTrackierKey() -> String? {
return retrieve(for: "trackier_sdk_key")
}

// Store the Secret ID securely
func storeSecretId(_ secretId: String) -> Bool {
return store(key: secretId, for: "trackier_secret_id")
}

// Retrieve the Secret ID securely
func getSecretId() -> String? {
return retrieve(for: "trackier_secret_id")
}

// Store the Secret Key securely
func storeSecretKey(_ secretKey: String) -> Bool {
return store(key: secretKey, for: "trackier_secret_key")
}

// Retrieve the Secret Key securely
func getSecretKey() -> String? {
return retrieve(for: "trackier_secret_key")
}

// Clear all stored credentials
func clearAllCredentials() {
let keys = ["trackier_sdk_key", "trackier_secret_id", "trackier_secret_key"]
for key in keys {
delete(for: key)
}
}

// Private methods for Keychain operations
private func store(key: String, for identifier: String) -> Bool {
guard let data = key.data(using: .utf8) else { return false }

let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: identifier,
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]

// Delete existing item before adding new one
SecItemDelete(query as CFDictionary)

let status = SecItemAdd(query as CFDictionary, nil)

// Handle specific error cases
switch status {
case errSecSuccess:
return true
case errSecDuplicateItem:
// Try to update existing item
let updateQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: identifier
]
let updateAttributes: [String: Any] = [
kSecValueData as String: data
]
let updateStatus = SecItemUpdate(updateQuery as CFDictionary, updateAttributes as CFDictionary)
return updateStatus == errSecSuccess
default:
print("Keychain store error: \(status)")
return false
}
}

private func retrieve(for identifier: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: identifier,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]

var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)

switch status {
case errSecSuccess:
guard let data = result as? Data,
let string = String(data: data, encoding: .utf8) else {
return nil
}
return string
case errSecItemNotFound:
return nil
default:
print("Keychain retrieve error: \(status)")
return nil
}
}

private func delete(for identifier: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: identifier
]

SecItemDelete(query as CFDictionary)
}
}

Step 3: Create App Configuration

Create a configuration class to manage default values and app settings:

AppConfig.swift
struct AppConfig {
// Replace these with your actual Trackier SDK credentials
// IMPORTANT: Remove these keys after first run - they will be stored securely
static let defaultTrackierKey = "YOUR_TRACKIER_SDK_KEY_HERE"
static let defaultSecretId = "YOUR_SECRET_ID_HERE"
static let defaultSecretKey = "YOUR_SECRET_KEY_HERE"

// Environment configuration - use TrackierSDKConfig constants
static let environment = TrackierSDKConfig.ENV_DEVELOPMENT // or TrackierSDKConfig.ENV_PRODUCTION
}

Step 4: Update App Delegate

Update your AppDelegate.swift to use secure storage for key management:

AppDelegate.swift
import UIKit
import Security
import TrackierSDK

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

initTrackierSDK()
return true
}

private func initTrackierSDK() {
// Get the Trackier key from secure storage
var trackierKey = SecureStorageService.shared.getTrackierKey()
var secretId = SecureStorageService.shared.getSecretId()
var secretKey = SecureStorageService.shared.getSecretKey()

// If no keys are stored, use default keys and store them securely
if trackierKey == nil {
trackierKey = AppConfig.defaultTrackierKey
SecureStorageService.shared.storeTrackierKey(trackierKey!)
print("Using default Trackier key: \(trackierKey!)")
} else {
print("Retrieved Trackier key from secure storage: \(trackierKey!)")
}

if secretId == nil {
secretId = AppConfig.defaultSecretId
SecureStorageService.shared.storeSecretId(secretId!)
print("Using default Secret ID: \(secretId!)")
} else {
print("Retrieved Secret ID from secure storage: \(secretId!)")
}

if secretKey == nil {
secretKey = AppConfig.defaultSecretKey
SecureStorageService.shared.storeSecretKey(secretKey!)
print("Using default Secret Key: \(secretKey!)")
} else {
print("Retrieved Secret Key from secure storage: \(secretKey!)")
}

// Configure SDK with secure credentials
let config = TrackierSDKConfig(appToken: trackierKey!, env: AppConfig.environment)

// Set app secrets for SDK signing
config.setAppSecret(secretId: secretId!, secretKey: secretKey!)

// Initialize SDK
TrackierSDK.initialize(config: config)

print("Trackier SDK initialized successfully with secure credentials")
}
}

Reference: This is reference we add library and add key and show in logs after first launch remove the key it will store locally.

Key Management

Updating Keys

To update your SDK keys:

  1. Update in Trackier Panel: First, update your keys in the Trackier Panel
  2. Clear Stored Keys: Use the clear function to remove old keys:
    SecureStorageService.shared.clearAllCredentials()
  3. Update AppConfig: Temporarily update the default keys in AppConfig.swift:
    static let defaultTrackierKey = "YOUR_NEW_SDK_KEY"
    static let defaultSecretId = "YOUR_NEW_SECRET_ID"
    static let defaultSecretKey = "YOUR_NEW_SECRET_KEY"
  4. Run the App: The new keys will be stored securely on first run
  5. Remove from AppConfig: Once keys are stored, remove them from AppConfig.swift:
    static let defaultTrackierKey = "" // Remove after first run
    static let defaultSecretId = "" // Remove after first run
    static let defaultSecretKey = "" // Remove after first run

Removing Keys from AppConfig

Important: After the first successful run, remove your actual keys from AppConfig.swift and replace them with empty strings or placeholder values:

AppConfig.swift
struct AppConfig {
// Remove actual keys after first run - they will be stored securely
static let defaultTrackierKey = "" // Remove after first run
static let defaultSecretId = "" // Remove after first run
static let defaultSecretKey = "" // Remove after first run

// Environment configuration
static let environment = "development" // or "production"
}

This ensures that your actual keys are never committed to version control and are only stored in the device's secure storage.

Security Features

iOS Keychain Services

The iOS Keychain provides:

  • Hardware Encryption: Uses Secure Enclave when available
  • Access Control: Keys are only accessible when device is unlocked
  • Device Bound: Keys are tied to the specific device
  • Automatic Encryption: All data is automatically encrypted

Key Management

  • Automatic Storage: Keys are automatically stored when first accessed
  • Secure Retrieval: Keys are retrieved from iOS Keychain
  • Fallback Mechanism: Default keys are used if Keychain is empty
  • Clear Function: Ability to clear all stored credentials

Best Practices

  1. Never Hardcode Keys: Always use Keychain instead of hardcoding credentials
  2. Use Environment Variables: Store default keys in environment-specific configuration
  3. Regular Key Rotation: Periodically update your SDK keys in the Trackier Panel
  4. Error Handling: Implement proper error handling for Keychain operations
  5. Access Control: Use appropriate accessibility levels for your use case

Troubleshooting

Common Issues

  1. Keys Not Stored: Ensure Keychain Services are properly configured
  2. Permission Errors: Check that your app has proper entitlements
  3. Initialization Failures: Verify that your SDK keys are correct and valid
  4. Simulator Issues: Keychain behavior may differ in iOS Simulator

Debug Information

Enable debug logging to troubleshoot issues:

// Add this to your initTrackierSDK method for debugging
print("Debug: Checking Keychain...")
let storedKey = SecureStorageService.shared.getTrackierKey()
print("Debug: Stored key exists: \(storedKey != nil)")

Next Steps

After implementing key hiding:

  1. Test the Integration: Verify that events are being tracked correctly
  2. Monitor Dashboard: Check your Trackier Panel for incoming data
  3. Implement Events: Add custom event tracking throughout your app
  4. Security Review: Regularly review and update your security practices

Support

For further assistance with key hiding implementation: