简体   繁体   English

BLE中央iOS应用何时应读取加密的特征?

[英]When should a BLE central iOS app read an encrypted characteristic?

I have an iOS app which is a central to a peripheral on which I control the firmware. 我有一个iOS应用程序,它是我控制固件的外围设备的核心。 A similar Android app works fine and is able to connect to the peripheral, discover services, bond explicitly, and read an encrypted characteristic. 一个类似的Android应用程序可以正常工作,并且能够连接到外围设备,发现服务,进行显式绑定并读取加密的特征。

On iOS, however, there is no explicit bonding in the CoreBluetooth API. 但是,在iOS上,CoreBluetooth API中没有显式绑定。 I am working on the basis that if I want to bond, I should just read an encrypted characteristic in order to force bonding to occur. 我的工作基础是,如果要绑定,我应该只读取加密的特征以强制进行绑定。

When is the right time to do that? 什么时候才是合适的时间? If I do it when discovering the characteristic I want to read, in this function... 如果在发现要读取的特征时执行此操作,请使用此功能...

func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!)

... I never get a callback to peripheral:didUpdateValueForCharacteristic. ...我从没有回调到外设:didUpdateValueForCharacteristic。 Reading an unencrypted characteristic at this point does work though, I believe. 我相信,此时读取一个未加密的特征确实可行。

[EDIT] [编辑]

Here's the code for my CBPeripheralDelegate implementation. 这是我的CBPeripheralDelegate实现的代码。

import CoreBluetooth
import CoreLocation

class BluetoothPeripheralController: NSObject, CBPeripheralDelegate {

    private var _peripheral: CBPeripheral!
    var peripheral: CBPeripheral! {
        get {
            return self._peripheral
        }
        set {
            if self._peripheral != nil {
                self._peripheral = nil
            }

            self._peripheral = newValue

            if self._peripheral != nil {
                self.peripheralDidChange(newValue)
            }
        }
    }

    private var notificationCenter: NSNotificationCenter = {
        return NSNotificationCenter.defaultCenter()
    }()

    private var testModeCharacteristic: CBCharacteristic?

    private var voltageCharacteristic: CBCharacteristic?

    private var firmwareLoggingCharacteristic: CBCharacteristic?

    static let voltageDidChangeNotification = "voltageDidChangeNotification"

    static let testModeDidChangeNotification = "testModeDidChangeNotification"

    static let bonded = "bonded"

    static let firmwareLoggingDidChangeNotification = "firmwareLoggingDidChangeNotification"

    func peripheralDidChange(peripheral: CBPeripheral) {
        peripheral.delegate = self
    }

    func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
        if error != nil {
            log.error("Error: \(error)")
            return
        }

        for service in peripheral.services {
            log.verbose("Discovering characteristics for service: \(BleService().lookup(service.UUID))")

            peripheral.discoverCharacteristics(nil, forService: service as! CBService)
        }
    }

    // Hold on to characteristic instances so we can read/write/notify on them later.
    func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
        if let chars = service.characteristics as? [CBCharacteristic] {
            log.verbose("* \(BleService().lookup(service.UUID))")

            for char in chars {
                log.verbose("** \(BleCharacteristic().lookup(char.UUID))")

                if char.UUID.isEqual(BleCharacteristic.voltageCharacteristicUUID) {
                    voltageCharacteristic = char
                }

                if char.UUID.isEqual(BleCharacteristic.testModeCharacteristicUUID) {
                    testModeCharacteristic = char

                    log.verbose("Reading value for test mode.")

                    // Reading any encrypted value here does NOT result in a callback to peripheral:didUpdateValueForCharacteristic.
                    peripheral.readValueForCharacteristic(char)
                }

                if char.UUID.isEqual(BleCharacteristic.nordicNrfUartRxUUID) {
                    firmwareLoggingCharacteristic = char
                }
            }
        }
    }

    func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
        if error != nil {
            log.verbose("Error on value: \(error)")
            return
        }

        let data: NSData = characteristic.value

        log.verbose("Got value \(data) for characterisitc \(BleCharacteristic().lookup(characteristic.UUID))")

        switch characteristic.UUID {
        case BleCharacteristic.voltageCharacteristicUUID:
            var voltage: Float = 0.0

            data.getBytes(&voltage, length: sizeof(Float))

            log.debug("Voltage: \(voltage)")

            // The map view is interested in this.
            self.notificationCenter.postNotificationName(BluetoothPeripheralController.voltageDidChangeNotification, object: self, userInfo: ["voltage": NSNumber(float: voltage)])

        case BleCharacteristic.testModeCharacteristicUUID:
            log.debug("Test mode: \(data). We are now bonded.")

            self.notificationCenter.postNotificationName(BluetoothPeripheralController.bonded, object: self, userInfo: nil)

        default:
            log.debug("Unknown characteristic UUID: \(characteristic.UUID)")
        }
    }

    func peripheral(peripheral: CBPeripheral!, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
        if error != nil {
            log.verbose("Error updating notification state: \(error)")
            return
        }

        switch characteristic.UUID {
        case BleCharacteristic.nordicNrfUartRxUUID:
            log.verbose("Notification state change on firmware logging. Now notifying: \(characteristic.isNotifying)")

        default:
            log.verbose("Unhandled characteristic: \(characteristic.description)")
        }
    }

    // Read/write
    func readTestMode() {
        if testModeCharacteristic == nil {
            log.warning("Trying to read test mode before we have a characterisitc instance.")

            return
        }

        // Calls back to peripheral:didUpdateValueForCharacteristic.
        peripheral.readValueForCharacteristic(testModeCharacteristic)
    }

}

Ensure that the desired characteristic is present in didDiscoverCharacteristicsForService using the service's characteristics property. 使用服务的characteristics属性,确保didDiscoverCharacteristicsForService存在所需的characteristics

It's up to your peripheral to return an authentication error when you try to read the characterisitc prior to bonding. 当您尝试在绑定之前读取特征时,由外设决定是否返回验证错误。 The error will appear in didUpdateValueForCharacteristic if you need to do something with it, but the error alone will trigger the bonding. 如果您需要对错误进行didUpdateValueForCharacteristic则该错误将显示在didUpdateValueForCharacteristic ,但仅此错误将触发绑定。

From Bluetooth Accessory Design Guidelines : 根据蓝牙配件设计指南

If, for security reasons, the accessory requires a bonded relationship with the Central, the Peripheral should reject the ATT request using the Insufficient Authentication error code, as appropriate. 如果出于安全考虑,如果附件需要与中央的绑定关系,则外围设备应使用适当的身份验证错误代码拒绝ATT请求。

Turns out it's OK to read a characteristic value during peripheral:didDiscoverCharacteristicsForService and the only reason I wasn't getting my callback was that iOS had cached the bond from earlier, but the peripheral had since had it's own side of that bond wiped. 事实证明,可以在peripheral:didDiscoverCharacteristicsForService期间读取特征值是可以的,而我没有得到回调的唯一原因是iOS缓存了较早的绑定,但是外设此后就已经将其绑定的一面抹去了。

The solution was either or both of: a) Going to the iOS Settings app and "forgetting" the peripheral and b) Rebooting the phone. 解决方案之一或全部为:a)转到iOS设置应用程序并“忘记”外围设备,并b)重新启动手机。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM