You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

从Android迁移到iOS AWS IoT:iOS SDK类似密钥库方法咨询

Handling PEM Certificates/Private Keys in AWS IoT for iOS (Keychain Equivalent to Android's AWSIotKeystoreHelper)

Great question! The AWS IoT SDK for iOS doesn’t include direct equivalents to AWSIotKeystoreHelper.saveCertificateAndPrivateKey() or AWSIotKeystoreHelper.getIotKeystore() from Android, but you can replicate this functionality by manually parsing your PEM files and managing entries in the iOS Keychain. Here’s a step-by-step breakdown:

1. Parse PEM Format Certificate and Private Key

First, convert your PEM strings into native iOS security objects (SecCertificate for certificates, SecKey for private keys):

Parsing the PEM Certificate

func parsePEMCertificate(pemString: String) -> SecCertificate? {
    // Strip PEM headers/footers and clean whitespace
    let cleanedPEM = pemString
        .replacingOccurrences(of: "-----BEGIN CERTIFICATE-----", with: "")
        .replacingOccurrences(of: "-----END CERTIFICATE-----", with: "")
        .trimmingCharacters(in: .whitespacesAndNewlines)
    
    guard let data = Data(base64Encoded: cleanedPEM) else {
        print("Failed to base64 decode certificate PEM")
        return nil
    }
    
    return SecCertificateCreateWithData(nil, data as CFData)
}

Parsing the PEM Private Key

Parsing PEM private keys requires a bit more work since iOS’s Security framework lacks a direct helper. For RSA keys, use this approach:

func parsePEMPrivateKey(pemString: String) -> SecKey? {
    let cleanedPEM = pemString
        .replacingOccurrences(of: "-----BEGIN PRIVATE KEY-----", with: "")
        .replacingOccurrences(of: "-----END PRIVATE KEY-----", with: "")
        .trimmingCharacters(in: .whitespacesAndNewlines)
    
    guard let data = Data(base64Encoded: cleanedPEM) else {
        print("Failed to base64 decode private key PEM")
        return nil
    }
    
    let attributes: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecAttrKeyClass: kSecAttrKeyClassPrivate
    ]
    
    var error: Unmanaged<CFError>?
    guard let secKey = SecKeyCreateWithData(data as CFData, attributes as CFDictionary, &error) else {
        print("Failed to create SecKey: \(error?.takeRetainedValue() ?? "Unknown error")")
        return nil
    }
    
    return secKey
}

Note: For EC (Elliptic Curve) keys, update kSecAttrKeyType to kSecAttrKeyTypeEC.

2. Save Certificate and Private Key to Keychain

Persist these security objects to the Keychain for cross-launch access:

Saving the Certificate

func saveCertificateToKeychain(_ certificate: SecCertificate, withLabel label: String) -> Bool {
    let query: [CFString: Any] = [
        kSecClass: kSecClassCertificate,
        kSecAttrLabel: label,
        kSecValueRef: certificate,
        kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
    ]
    
    // Delete existing entry with the same label first
    SecItemDelete(query as CFDictionary)
    
    let status = SecItemAdd(query as CFDictionary, nil)
    return status == errSecSuccess
}

Saving the Private Key

func savePrivateKeyToKeychain(_ privateKey: SecKey, withLabel label: String) -> Bool {
    let query: [CFString: Any] = [
        kSecClass: kSecClassKey,
        kSecAttrLabel: label,
        kSecValueRef: privateKey,
        kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
        kSecAttrKeyClass: kSecAttrKeyClassPrivate
    ]
    
    // Clean up existing entries
    SecItemDelete(query as CFDictionary)
    
    let status = SecItemAdd(query as CFDictionary, nil)
    return status == errSecSuccess
}

3. Retrieve Keychain Entries for AWS IoT

Fetch stored items when initializing your AWS IoT connection:

Fetching the Certificate

func getCertificateFromKeychain(withLabel label: String) -> SecCertificate? {
    let query: [CFString: Any] = [
        kSecClass: kSecClassCertificate,
        kSecAttrLabel: label,
        kSecReturnRef: kCFBooleanTrue!,
        kSecMatchLimit: kSecMatchLimitOne
    ]
    
    var result: AnyObject?
    let status = SecItemCopyMatching(query as CFDictionary, &result)
    
    guard status == errSecSuccess, let certificate = result as? SecCertificate else {
        print("Failed to fetch certificate from Keychain: \(status)")
        return nil
    }
    
    return certificate
}

Fetching the Private Key

func getPrivateKeyFromKeychain(withLabel label: String) -> SecKey? {
    let query: [CFString: Any] = [
        kSecClass: kSecClassKey,
        kSecAttrLabel: label,
        kSecReturnRef: kCFBooleanTrue!,
        kSecMatchLimit: kSecMatchLimitOne,
        kSecAttrKeyClass: kSecAttrKeyClassPrivate
    ]
    
    var result: AnyObject?
    let status = SecItemCopyMatching(query as CFDictionary, &result)
    
    guard status == errSecSuccess, let privateKey = result as? SecKey else {
        print("Failed to fetch private key from Keychain: \(status)")
        return nil
    }
    
    return privateKey
}

4. Configure AWS IoT Client

Use the retrieved Keychain items to set up your MQTT client:

guard let certificate = getCertificateFromKeychain(withLabel: "MyIoTCertificate"),
      let privateKey = getPrivateKeyFromKeychain(withLabel: "MyIoTPrivateKey") else {
    print("Missing certificate or private key")
    return
}

let configuration = AWSMQTTConfiguration(
    clientId: "your-unique-client-id",
    endpoint: "your-iot-endpoint.amazonaws.com",
    certificate: certificate,
    privateKey: privateKey
)

let mqttClient = AWSMQTTClient(configuration: configuration)
// Proceed with connecting and using the client

Key Notes

  • Accessibility: Choose kSecAttrAccessible values carefully based on your app’s needs. kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly balances security and background access.
  • Error Handling: Expand the basic logging in examples for production to handle cases like Keychain access denial.

内容的提问来源于stack exchange,提问作者Pavle Joksović

火山引擎 最新活动