移动开发中遇到的最让人纠结的要属Java、Android和iPhone三个平台加解密不一致的问题。因为手机端后台通常是用JAVA开发的Web Service,Android和iPhone客户端调用同样的Web Service接口,为了数据安全考虑,要对数据进行加密。头疼的问题就来了,很难编写出一套加密程序,在3个平台间加解密的结果一致,总不能为Android和iPhone两个客户端各写一套Web Service接口吧?我相信还会有很多朋友为此困惑,在此分享一套3DES加密程序,能够实现Java、Android和iPhone三个平台加解密一致。
首先是JAVA端的加密工具类,它同样适用于Android端,无需任何修改,即可保证Java与Android端的加解密一致,并且中文不会乱码。
1. package org.liuyq.des3;
2.
3. import java.security.Key;
4.
5. import javax.crypto.Cipher;
6. import javax.crypto.SecretKeyFactory;
7. import javax.crypto.spec.DESedeKeySpec;
8. import javax.crypto.spec.IvParameterSpec;
9.
10.
11. public class Des3 {
12. // 密钥
13. private final static String secretKey = "liuyunqiang@lx100$#365#$";
14. // 向量
15. private final static String iv = "01234567";
16. // 加解密统一使用的编码方式
17. private final static String encoding = "utf-8";
18.
19.
20. public static String encode(String plainText) throws Exception {
21. Key deskey = null;
22. DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes());
23. SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
24. deskey = keyfactory.generateSecret(spec);
25.
26. Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
27. IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
28. cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
29. byte[] encryptData = cipher.doFinal(plainText.getBytes(encoding));
30. return Base64.encode(encryptData);
31. }
32.
33.
34. public static String decode(String encryptText) throws Exception {
35. Key deskey = null;
36. DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes());
37. SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
38. deskey = keyfactory.generateSecret(spec);
39. Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
40. IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
41. cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
42.
43. byte[] decryptData = cipher.doFinal(Base64.decode(encryptText));
44.
45. return new String(decryptData, encoding);
46. }
47. }
上面的加密工具类会使用到Base64这个类,该类的源代码如下:
1. import java.io.ByteArrayOutputStream;
2. import java.io.IOException;
3. import java.io.OutputStream;
4.
5.
6. public class Base64 {
7. private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
8.
9. public static String encode(byte[] data) {
10. int start = 0;
11. int len = data.length;
12. StringBuffer buf = new StringBuffer(data.length * 3 / 2);
13.
14. int end = len - 3;
15. int i = start;
16. int n = 0;
17.
18. while (i <= end) {
19. int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 0x0ff) << 8) | (((int) data[i + 2]) & 0x0ff);
20.
21. buf.append(legalChars[(d >> 18) & 63]);
22. buf.append(legalChars[(d >> 12) & 63]);
23. buf.append(legalChars[(d >> 6) & 63]);
24. buf.append(legalChars[d & 63]);
25.
26. i += 3;
27.
28. if (n++ >= 14) {
29. n = 0;
30. buf.append(" ");
31. }
32. }
33.
34. if (i == start + len - 2) {
35. int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 255) << 8);
36.
37. buf.append(legalChars[(d >> 18) & 63]);
38. buf.append(legalChars[(d >> 12) & 63]);
39. buf.append(legalChars[(d >> 6) & 63]);
40. buf.append("=");
41. } else if (i == start + len - 1) {
42. int d = (((int) data[i]) & 0x0ff) << 16;
43.
44. buf.append(legalChars[(d >> 18) & 63]);
45. buf.append(legalChars[(d >> 12) & 63]);
46. buf.append("==");
47. }
48.
49. return buf.toString();
50. }
51.
52. private static int decode(char c) {
53. if (c >= 'A' && c <= 'Z')
54. return ((int) c) - 65;
55. else if (c >= 'a' && c <= 'z')
56. return ((int) c) - 97 + 26;
57. else if (c >= '0' && c <= '9')
58. return ((int) c) - 48 + 26 + 26;
59. else
60. switch (c) {
61. case '+':
62. return 62;
63. case '/':
64. return 63;
65. case '=':
66. return 0;
67. default:
68. throw new RuntimeException("unexpected code: " + c);
69. }
70. }
71.
72.
73.
74. public static byte[] decode(String s) {
75.
76. ByteArrayOutputStream bos = new ByteArrayOutputStream();
77. try {
78. decode(s, bos);
79. } catch (IOException e) {
80. throw new RuntimeException();
81. }
82. byte[] decodedBytes = bos.toByteArray();
83. try {
84. bos.close();
85. bos = null;
86. } catch (IOException ex) {
87. System.err.println("Error while decoding BASE64: " + ex.toString());
88. }
89. return decodedBytes;
90. }
91.
92. private static void decode(String s, OutputStream os) throws IOException {
93. int i = 0;
94.
95. int len = s.length();
96.
97. while (true) {
98. while (i < len && s.charAt(i) <= ' ')
99. i++;
100.
101. if (i == len)
102. break;
103.
104. int tri = (decode(s.charAt(i)) << 18) + (decode(s.charAt(i + 1)) << 12) + (decode(s.charAt(i + 2)) << 6) + (decode(s.charAt(i + 3)));
105.
106. os.write((tri >> 16) & 255);
107. if (s.charAt(i + 2) == '=')
108. break;
109. os.write((tri >> 8) & 255);
110. if (s.charAt(i + 3) == '=')
111. break;
112. os.write(tri & 255);
113.
114. i += 4;
115. }
116. }
117. }
接下来是iPhone端的加密程序,当然是用Ojbective-C写的3DES加密程序,源代码如下:
1. //
2. // DES3Util.h
3. //
4.
5. #import
6.
7.
8. @interface DES3Util : NSObject {
9.
10. }
11.
12. // 加密方法
13. + (NSString*)encrypt:(NSString*)plainText;
14.
15. // 解密方法
16. + (NSString*)decrypt:(NSString*)encryptText;
17.
18. @end
1. //
2. // DES3Util.m
3. //
4.
5. #import "DES3Util.h"
6. #import
7. #import "GTMBase64.h"
8.
9. gkey @"liuyunqiang@lx100$#365#$"
10. gIv @"01234567"
11.
12. @implementation DES3Util
13.
14. // 加密方法
15. (NSString*)encrypt:(NSString*)plainText {
16. NSData* data = [plainText dataUsingEncoding:NSUTF8StringEncoding];
17. size_t plainTextBufferSize = [data length];
18. const void *vplainText = (const void *)[data bytes];
19.
20. CCCryptorStatus ccStatus;
21. uint8_t *bufferPtr = NULL;
22. size_t bufferPtrSize = 0;
23. size_t movedBytes = 0;
24.
25. bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
26. bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
27. memset((void *)bufferPtr, 0x0, bufferPtrSize);
28.
29. const void *vkey = (const void *) [gkey UTF8String];
30. const void *vinitVec = (const void *) [gIv UTF8String];
31.
32. ccStatus = CCCrypt(kCCEncrypt,
33. kCCAlgorithm3DES,
34. kCCOptionPKCS7Padding,
35. vkey,
36. kCCKeySize3DES,
37. vinitVec,
38. vplainText,
39. plainTextBufferSize,
40. (void *)bufferPtr,
41. bufferPtrSize,
42. &movedBytes);
43.
44. NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
45. NSString *result = [GTMBase64 stringByEncodingData:myData];
46. return result;
47. }
48.
49. // 解密方法
50. (NSString*)decrypt:(NSString*)encryptText {
51. NSData *encryptData = [GTMBase64 decodeData:[encryptText dataUsingEncoding:NSUTF8StringEncoding]];
52. size_t plainTextBufferSize = [encryptData length];
53. const void *vplainText = [encryptData bytes];
54.
55. CCCryptorStatus ccStatus;
56. uint8_t *bufferPtr = NULL;
57. size_t bufferPtrSize = 0;
58. size_t movedBytes = 0;
59.
60. bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
61. bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
62. memset((void *)bufferPtr, 0x0, bufferPtrSize);
63.
64. const void *vkey = (const void *) [gkey UTF8String];
65. const void *vinitVec = (const void *) [gIv UTF8String];
66.
67. ccStatus = CCCrypt(kCCDecrypt,
68. kCCAlgorithm3DES,
69. kCCOptionPKCS7Padding,
70. vkey,
71. kCCKeySize3DES,
72. vinitVec,
73. vplainText,
74. plainTextBufferSize,
75. (void *)bufferPtr,
76. bufferPtrSize,
77. &movedBytes);
78.
79. NSString *result = [[[NSString alloc] initWithData:[NSData dataWithBytes:(const void *)bufferPtr
80. length:(NSUInteger)movedBytes] encoding:NSUTF8StringEncoding] autorelease];
81. return result;
82. }
83.
84. @end
iPhone端的加密工具类中引入了“GTMBase64.h”,这是iOS平台的Base64编码工具类,就不在这里贴出相关代码了,需要的百度一下就能找到。
这样,JAVA,Android和iPhone三个平台的加密不一致问题就可以解决了。其实,对此问题,还有一种更好的实现方式,那就是用C语言写一套加密程序,这样在iOS平台是可以直接使用C程序的,而在Java和Android端通过JNI去调用C语言编写的加密方法,这样也可以实现3个平台调用同一套加密程序。