Android与iOS平台实现SHA-1、3DES的后台对接

小费周折后终于在Android、iOS项目中实现了加密算法与后台接口的对接。后台接口要求的是SHA-1与3DES的二次加密,然后还有几次Base64等。

首先当然要科普下这两个算法。

SHA-1:Secure Hash Algorithm,安全散列算法。于1995年被NSA提出,主要用于修正SHA-0算法。SHA-0算法已经在04、05年宣布被破解。SHA-1算法虽然被提出理论上可破解,但还没有真正实现。SHA-1可将一个最大264比特的消息,转换成一串160位的消息摘要。原理类似于MD4、MD5算法,但SHA-1更安全。

3DES(Triple DES):TDEA,Triple Data Encryption Algorithm,三重数据加密算法。相当于是对每个数据块应用三次DES加密算法。加密算法为:密文 = EK3(DK2(EK1(平文))),也就是说,使用K1为密钥进行DES加密,再用K2为密钥进行DES“解密”,最后以K3进行DES加密。解密则为其反过程:平文 = DK1(EK2(DK3(密文))),即以K3解密,以K2“加密”,最后以K1解密。每次加密操作都只处理64位数据,称为一块。

实现开始。

SHA-1,Android:

Android直接就用jdk的加密类。代码如下:

1
2
3
4
5
public static byte[] sha1Digest(byte[] src) throws NoSuchAlgorithmException {
java.security.MessageDigest msg = java.security.MessageDigest.getInstance("SHA-1");
msg.update(src);
return msg.digest();
}

SHA-1,iOS:

iOS也是用的原生,但是用到了一个开源的Base64库(Base64kit GitHub)。

加密代码如下:

1
2
3
4
5
6
7
8
 +(NSString *) sha1_base64:(NSString *)dataStr{
const char *cstr = [dataStr cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:dataStr.length];
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, (int)data.length, digest);
NSData * base64 = [[NSData alloc]initWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
return [base64 base64EncodedString];
}

SHA-1算法实现比较简单,接下来实现高级点的3DES。

在加密前要搞定两个重要的参数。密钥key跟向量值destValue。

DES的密钥长度为8位。3DES就是3次DES加密,密钥为24位。

3DES,Android:

密钥算法如下:

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
public static byte[] HexStringToByteArray(String s) {
byte[] buf = new byte[s.length() / 2];
for (int i = 0; i < buf.length; i++) {
buf[i] = (byte) (chr2hex(s.substring(i * 2, i * 2 + 1)) * 0x10 + chr2hex(s
.substring(i * 2 + 1, i * 2 + 2)));
}
return buf;
}
private static byte chr2hex(String chr) {
if (chr.equals("0")) {
return 0x00;
} else if (chr.equals("1")) {
return 0x01;
} else if (chr.equals("2")) {
return 0x02;
} else if (chr.equals("3")) {
return 0x03;
} else if (chr.equals("4")) {
return 0x04;
} else if (chr.equals("5")) {
return 0x05;
} else if (chr.equals("6")) {
return 0x06;
} else if (chr.equals("7")) {
return 0x07;
} else if (chr.equals("8")) {
return 0x08;
} else if (chr.equals("9")) {
return 0x09;
} else if (chr.equals("A")) {
return 0x0a;
} else if (chr.equals("B")) {
return 0x0b;
} else if (chr.equals("C")) {
return 0x0c;
} else if (chr.equals("D")) {
return 0x0d;
} else if (chr.equals("E")) {
return 0x0e;
} else if (chr.equals("F")) {
return 0x0f;
}
return 0x00;
}

destValue转成byte数组。

最后剩下最重要的就是加密的模式及填充方式了。

因为后台是java的,直接参照后台的加密配置就行了。采用的是CBC/PKCS5Padding。

加密代码如下:

1
2
3
4
5
6
7
8
9
10
11
public static String decode(String encryptText) throws Exception {  
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes());
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(destValue.getBytes());
cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
byte[] decryptData = cipher.doFinal(Base64.decode(encryptText));
return new String(decryptData, encoding);
}

最后就是iOS的实现了。

iOS的密钥key跟向量值destValue都要跟Android的一样转为24位的byte数组跟8位的byte数组。

调试用了比较久时间才发现这两个参数错了,之前一直都传字符串,结果加密一直跟Android的对不上,囧~

然后是填充方式iOS只有kCCOptionPKCS7Padding跟kCCOptionECBMode两个。

而Android及java后台用的是PKCS5Padding。于是查一下资料,有一句关于java的DES加密:“PKCS5Padding明确定义了加密块是8字节,PKCS7Padding加密块可以是1~255之间。但是封装的DES算法默认都是8字节,所以可以认为他们是一样”。

所以iOS可以用kCCOptionPKCS7Padding。

上代码:

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
+ (NSString*) doCipher:(NSString*)plainText enc:(CCOperation)encryptOrDecrypt{
const void *vplainText;
size_t plainTextBufferSize;

if (encryptOrDecrypt == kCCDecrypt)
{
NSData *EncryptData =[NSData dataWithBase64EncodedString:plainText];
plainTextBufferSize = [EncryptData length];
vplainText = [EncryptData bytes];
}
else
{
plainTextBufferSize = [plainText length];
vplainText = (const void *) [plainText UTF8String];
}

CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;

bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);

uint8_t iv[kCCBlockSize3DES];
memset((void *) iv, 0x0, (size_t) sizeof(iv));

ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding ,
key,
kCCKeySize3DES,
destValue ,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);

if (ccStatus == kCCParamError) return @"PARAM ERROR";
else if (ccStatus == kCCBufferTooSmall) return @"BUFFER TOO SMALL";
else if (ccStatus == kCCMemoryFailure) return @"MEMORY FAILURE";
else if (ccStatus == kCCAlignmentError) return @"ALIGNMENT";
else if (ccStatus == kCCDecodeError) return @"DECODE ERROR";
else if (ccStatus == kCCUnimplemented) return @"UNIMPLEMENTED";

NSString *result;

if (encryptOrDecrypt == kCCDecrypt)
{
result = [ [NSString alloc] initWithData: [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes] encoding:NSASCIIStringEncoding];

}
else
{
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
result = [myData base64EncodedString];
}
return result;
}

ok。这样都完成了。

除了这两种加密算法之外还有RSA算法等。有一个规律是:加密算法越复杂,需要消耗的性能就越多,就越慢。

以要按照实际需求来选择加密算法。