这段时间参与了一款与蓝牙外设交互的项目, 之前没有涉及过数据传输方面的开发, 踩了很多坑, 同时也学到了不少东西. 此时, 项目也即将进入尾声, 有时间把这些记录一二. 本人才疏学浅, 若有错误,大佬轻喷.前端
这方面网上的Demo一大堆, 暂时不作太多的赘述, 只对坑点作一个摘要.git
[self.peripheral readRSSI]
来读取信号值强度, 可是该方法最快1s只能返回一次, 若是须要更快速的获取信号值强度, 执行scanForPeripheralsWithServices
方法设置options参数@{CBCentralManagerScanOptionAllowDuplicatesKey:@(YES)}
, 也许iBeacon会是个不错的选择, 可是这边硬件并不支持,也没有进行实际的测试.kCBAdvDataManufacturerData
这个key
中,在发现外设的回调centralManager: didDiscoverPeripheral: advertisementData: RSSI:
中的advertisementData
参数中获取. (必定要写在对应kCBAdvDataManufacturerData
的字段中, 发现该设备广播包中没有这个key
, 让硬件工程师换一个字段再试试, 各个厂家的蓝牙模块不同, 极可能硬件工程师写错了)首先是平台方面的人定好了数据传输协议, 咱们按协议进行拼接, 而后使用拼接好的数据与外设进行交互. 数据传输协议通常分为包头和包体, 包体中也许还会进行相似的嵌套. 协议中会定义传输 的数据类型, 好比拼接过程当中须要传入包体的长度(无符号双字节整型), 咱们通常会用int
取到长度length, 这时候须要把int
转化为两个Byte.github
// int转两个字节Byte
+ (NSData *)dataFromShort:(short)value {
Byte bytes[2] = {};
for (int i = 0; i < 2; i++) {
int offset = 16 - (i + 1) * 8;
bytes[i] = (Byte) ((value >> offset) & 0xff);
}
NSData *data = [[NSData alloc] initWithBytes:bytes length:2];
return data;
}
// int转四个字节Byte
+ (NSData *)dataFromInt:(int)value {
Byte bytes[4] = {};
for (int i = 0; i < 4; i++) {
bytes[i] = (Byte)(value >> (24 - i * 8));
}
NSData *data= [[NSData alloc] initWithBytes:bytes length:4];
return data;
}
复制代码
通常也会有时间戳, 须要拼接这里提供两种格式的时间戳.算法
// 6个字节的时间戳
+ (NSData *)currentTimeData {
Byte bytes[6];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSUInteger unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:[NSDate date]];
int year =(int) [dateComponent year];
int month = (int) [dateComponent month];
int day = (int) [dateComponent day];
int hour = (int) [dateComponent hour];
int minute = (int) [dateComponent minute];
int second = (int) [dateComponent second];
bytes[0] = year - 2000;
bytes[1] = month;
bytes[2] = day;
bytes[3] = hour;
bytes[4] = minute;
bytes[5] = second;
return [[NSData alloc] initWithBytes:bytes length:6];
}
// 4个字节的时间戳
+ (NSData *)timestampData {
int time = [[NSDate date] timeIntervalSince1970];
return [NSData dataFromInt:time];
}
复制代码
首先来看一下PRF算法,这个以前一直想在网上download
一份, 奈何实在没有找到. 猜想是前端通常不会用到, 安卓同事却是从平台处获得了封装好的jar包可使用. iOS这边只能本身动手实现, 下面先看一下PRF算法的实现原理:bash
实现以下:app
+ (NSData *)tf_prfSecret:(NSData *)secret label:(NSData *)label seed:(NSData *)seed {
// 讲label与seed进行拼接
NSMutableData *seedData = [NSMutableData data];
[seedData appendData:label];
[seedData appendData:seed];
return [self tf_prfSecret:secret seed:seedData];
}
+ (NSData *)tf_prfSecret:(NSData *)secret seed:(NSData *)seed {
NSMutableData *prfData = [NSMutableData data];
NSMutableData *mutableData = [NSMutableData dataWithData:seed];
NSData *AnData = [NSData dataWithData:seed];
// 须要prf算法得出的长度
// kStaticPrfMinimumLength: 根据需求须要写入
while (prfData.length < kStaticPrfMinimumLength) {
AnData = [self hmacSHA256WithSecret:secret content:AnData];
mutableData = [NSMutableData dataWithData:AnData];
[mutableData appendData:seed];
NSData *hmacData = [self hmacSHA256WithSecret:secret content:mutableData];
[prfData appendData:hmacData];
}
return prfData;
}
复制代码
PRF算法中, HMAC_hash算法是可选的, 这边使用的是SHA256, 实现以下:函数
// hmac sha256算法
+ (NSData *)hmacSHA256WithSecret:(NSData *)secret content:(NSData *)content {
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, secret.bytes, secret.length, content.bytes, content.length, cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
// 将data按string输出
// const unsigned char *buffer = (const unsigned char *)[HMACData bytes];
// NSMutableString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];
// for (int i = 0; i < HMACData.length; ++i){
// [HMAC appendFormat:@"%02x", buffer[i]];
return HMACData;
}
复制代码
网上这样的加密真是一大堆,这边由于是与硬件数据传输, 因此对数据进行的加密的密钥与iv
向量也大几率是直接即是Data而不是经常使用的NSString, 这边对两种类型的key
与iv
都作了实现, 按实际情景使用.测试
- (NSData *)tf_encryptAES128WithKey:(NSString *)key iv:(NSString *)iv {
return [self tf_AES128Operation:kCCEncrypt key:key iv:iv];
}
- (NSData *)tf_decryptAES128WithKey:(NSString *)key iv:(NSString *)iv {
return [self tf_AES128Operation:kCCDecrypt key:key iv:iv];
}
- (NSData *)tf_encryptAES128WithKeyData:(NSData *)keyData ivData:(NSData *)ivData {
return [self tf_AES128Operation:kCCEncrypt keyData:keyData ivData:ivData];
}
- (NSData *)tf_decryptAES128WithKeyData:(NSData *)keyData ivData:(NSData *)ivData {
return [self tf_AES128Operation:kCCDecrypt keyData:keyData ivData:ivData];
}
/**
*
* @param operation kCCEncrypt:加密 kCCDecrypt:解密
* @param key 公钥
* @param iv 偏移量
*
* @return 加密或者解密的NSData
*/
- (NSData *)tf_AES128Operation:(CCOperation)operation key:(NSString *)key iv:(NSString *)iv {
char keyBytes[kCCKeySizeAES128 + 1]; //kCCKeySizeAES128是加密位数 能够替换成256位的
// bzero函数:从字符串第一位开始置0, 第二个参数表明置0的位数
// 至关于memset(keyBytes,0x00,sizeof(keyBytes));
bzero(keyBytes, sizeof(keyBytes));
[key getCString:keyBytes maxLength:sizeof(keyBytes) encoding:NSUTF8StringEncoding];
// iv
char ivBytes[kCCBlockSizeAES128 + 1];
bzero(ivBytes, sizeof(ivBytes));
[iv getCString:ivBytes maxLength:sizeof(ivBytes) encoding:NSUTF8StringEncoding];
return [self tf_cryptAES128Operation:operation keyBytes:keyBytes ivBytes:ivBytes];
}
- (NSData *)tf_AES128Operation:(CCOperation)operation keyData:(NSData *)keyData ivData:(NSData *)ivData {
char keyBytes[kCCKeySizeAES128 + 1];
bzero(keyBytes, sizeof(keyBytes));
[keyData getBytes:keyBytes length:sizeof(keyBytes)];
char ivBytes[kCCKeySizeAES128 + 1];
bzero(ivBytes, sizeof(ivBytes));
[ivData getBytes:ivBytes length:sizeof(ivBytes)];
return [self tf_cryptAES128Operation:operation keyBytes:keyBytes ivBytes:ivBytes];
}
- (NSData *)tf_cryptAES128Operation:(CCOperation)operation keyBytes:(void *)keyBytes ivBytes:(void *)ivBytes {
size_t bufferSize = self.length + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesCrypted = 0;
/*
CCOptions 默认为CBC加密
选择ECB加密填: kCCOptionPKCS7Padding | kCCOptionECBMode
ECB模式iv向量填NULL
kCCOptionPKCS7Padding: 7填充
直接填0x0000: 就是No padding填充
*/
CCCryptorStatus cryptorStatus = CCCrypt(operation, kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
keyBytes,
kCCKeySizeAES128,
ivBytes,
self.bytes,
self.length,
buffer,
bufferSize,
&numBytesCrypted);
if(cryptorStatus == kCCSuccess) {
NSLog(@"Crypt Successfully");
NSData *result = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
/* 转16进制字符串
Byte *resultBytes = (Byte *)result.bytes;
NSMutableString *outPut = [[NSMutableString alloc] initWithCapacity:result.length * 2];
for (int i = 0; i < result.length; i++) {
[outPut appendFormat:@"%02x", resultBytes[i]];
}
*/
return result;
} else {
NSLog(@"Crypt Error");
free(buffer);
return nil;
}
}
复制代码
附上 源码Demoui