记一次iOS使用XTEA微型加密算法


许多年后有一股寒风,从我自以为火热温暖的从未被寒冷侵入的内心深处阵阵袭来时,我才发现穿再厚的棉衣也没用了。生命本身有一个冬天,它已经来临。

——刘亮程《寒风吹彻》

简介

  • 在密码学中,微型加密算法(Tiny Encryption Algorithm,TEA)是一种易于描述和执行的块密码,通常只需要很少的代码就可实现。其设计者是剑桥大学计算机实验室的大卫·惠勒罗杰·尼达姆。这项技术最初于1994年提交给鲁汶的快速软件加密的研讨会上,并在该研讨会上演讲中首次发表。
  • 在给出的代码中:加密使用的数据为2个32位无符号整数,密钥为4个32位无符号整数即密钥长度为128位

正文

关于加密算法

  • 加密算法虽然我们可能不是非常了解底层原理,但是在现在信息安全的年代,工作中也会经常需要用的,比如:

    • 非对称加密的:RSAECC(移动设备用)、DSA(数字签名用)
    • 对称加密的:AESDES
    • Hash算法的:MD2MD4MD5HAVALSHASHA-1HMACHMAC-MD5HMAC-SHA1
  • 有时候我们可能会遇到这种情况:因为数据长度有限制,需要加密后的数据长度尽可能的短。比如如果给蓝牙的广播数据加一次密做单向的校验。但是在iOS开发中,自定义广播的数据长度是有限的,总共可用的才31个字节,再除去蓝牙字段名称、长度、蓝牙名称的实际值、自定义字段名称及长度等等,实际上可以给我们自定义使用的部分那真的是少的可怜。

  • 但是又不能将全部的广播包数据都加密,因为苹果会先将广播数据按照协议解析完成之后,将里面的少部分数据封装成对象抛给上层供我们使用,如果整个广播包加密会导致我们无法获取到广播数据,所以就只能单独的加密自定义广播部分的数据。
  • 在这里就不能使用RSA了,因为性能比较低、加解密比较慢;AES这样的算法又对数据长度有要求,最低都需要16个字节;Hash算法又不可逆,无法获取原始数据。最终在技术经理的提议下,使用了一种叫XTEA的算法,XTEATEA的升级版,增加了更多的密钥表,移位和异或操作等等。

XTEA加密过程

XTEA

iOS使用

  • 算法实现源码是使用C语言写的,虽然iOS可以直接调用C函数,但是直接调用总感觉很别扭,所以就将该算法直接以分类的方式简单封装一下,需要用到的小伙伴可以直接使用,因为一共就没多少行代码,直接贴代码了:
  • 直接给NSData添加一个XTEA算法的分类,.h文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
加密数据

@param data 需要加密的数据 (8字节)
@param keyData 加密的Key (16字节)
@param rounds 循环次数
@return 加密过的数据 (8字节)
*/
+ (NSData *)encryptData:(NSData *)data key:(NSData *)keyData rounds:(unsigned int)rounds;

/**
解密数据

@param data 需要解密的数据 (8字节)
@param keyData 解密的key (16字节)
@param rounds 循环次数
@return 解密过的数据 (8字节)
*/
+ (NSData *)decryptData:(NSData *)data key:(NSData *)keyData rounds:(unsigned int)rounds;
  • .m文件如下:
    • 注意引入需要的头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// 引入必要的头文件
#include <stdio.h>
#include <stdint.h>

// 声明常量
static NSInteger const kUint32DataCount = 2;
static NSInteger const kUint32KeyCount = 4;
static NSInteger const kSubdataLength = 4;

void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
for (i=0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;

uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0] = v0; v[1] = v1;
}

/**
加密数据

@param data 需要加密的数据 (8字节)
@param keyData 加密的Key (16字节)
@param rounds 循环次数
@return 加密过的数据 (8字节)
*/
+ (NSData *)encryptData:(NSData *)data key:(NSData *)keyData rounds:(unsigned int)rounds
{
NSAssert(data.length == 8, @"encryptData: (data length != 8)");
NSAssert(keyData.length == 16, @"encryptData: (keyData length != 16)");

// 将数据拆分成2个uint32_t数组
uint32_t encryptData[kUint32DataCount];
for (int i = 0; i < kUint32DataCount; i++) {
encryptData[i] = [NSData uint32FromData:[data subdataWithRange:NSMakeRange(i * kSubdataLength, kSubdataLength)]];
}

// 将Key拆成4个uint32_t数组
uint32_t key[kUint32KeyCount];
for (int i = 0; i < kUint32KeyCount; i++) {
key[i] = [NSData uint32FromData:[keyData subdataWithRange:NSMakeRange(i * kSubdataLength, kSubdataLength)]];
}

// 加密
encipher(rounds, encryptData, key);

// 返回结果
return [NSData dataFromUInt32Array:encryptData length:data.length];
}

/**
解密数据

@param data 需要解密的数据 (8字节)
@param keyData 解密的key (16字节)
@param rounds 循环次数
@return 解密过的数据 (8字节)
*/
+ (NSData *)decryptData:(NSData *)data key:(NSData *)keyData rounds:(unsigned int)rounds
{
NSAssert(data.length == 8, @"decryptData: (data length != 8)");
NSAssert(keyData.length == 16, @"decryptData: (keyData length != 16)");

// 将数据拆分成2个uint32_t数组
uint32_t decryptData[kUint32DataCount];
for (int i = 0; i < kUint32DataCount; i++) {
decryptData[i] = [NSData uint32FromData:[data subdataWithRange:NSMakeRange(i * kSubdataLength, kSubdataLength)]];
}

// 将Key拆成4个uint32_t数组
uint32_t key[kUint32KeyCount];
for (int i = 0; i < kUint32KeyCount; i++) {
key[i] = [NSData uint32FromData:[keyData subdataWithRange:NSMakeRange(i * kSubdataLength, kSubdataLength)]];
}

// 解密
decipher(rounds, decryptData, key);

// 返回结果
return [NSData dataFromUInt32Array:decryptData length:data.length];
}
  • 使用到的数据转换
    • NSDatauint32
    • uint32NSData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+ (uint32_t)uint32FromData:(NSData *)hexData
{
NSAssert(hexData.length == 4, @"uint32FromData: (data length != 4)");

Byte dataBytes[4];
[hexData getBytes:&dataBytes length:4];

uint32_t resultValue = *(uint32_t *)dataBytes ;
return resultValue;
}

+ (NSData *)dataFromUInt32Array:(uint32_t *)value length:(NSInteger)length
{
Byte *result = (Byte *)value;
return [NSData dataWithBytes:result length:length];
}

参考

要不要鼓励一下😘