关于AES256算法java端加密,ios端解密出现无法解密问题的解决方案

来源:互联网 发布:外贸进销存软件 编辑:程序博客网 时间:2024/05/16 07:11
我想关于AES算法大家应该都已经了解了,我就不多介绍了。这是本人第一次写技术博文,如果有不对之处欢迎大家指正,共同讨论,一起学习!

      之前在项目上用到AES256加密解密算法,刚开始在java端加密解密都没有问题,在iOS端加密解密也没有问题。但是奇怪的是在java端加密后的文件在iOS端无法正确解密打开,然后简单测试了一下,发现在java端和iOS端采用相同明文,相同密钥加密后的密文不一样!上网查了资料后发现iOS中AES加密算法采用的填充是PKCS7Padding,而java不支持PKCS7Padding,只支持PKCS5Padding。我们知道加密算法由算法+模式+填充组成,所以这两者不同的填充算法导致相同明文相同密钥加密后出现密文不一致的情况。那么我们需要在java中用PKCS7Padding来填充,这样就可以和iOS端填充算法一致了。

      要实现在java端用PKCS7Padding填充,需要用到bouncycastle组件来实现,下面我会提供该包的下载。啰嗦了一大堆,下面是一个简单的测试,上代码!

?
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package com.encrypt.file;
 
 
import java.io.UnsupportedEncodingException;
import java.security.Key; 
import java.security.Security;
 
import javax.crypto.Cipher; 
import javax.crypto.SecretKey; 
import javax.crypto.spec.SecretKeySpec; 
 
public class AES256Encryption{ 
     
         /**
         * 密钥算法
         * java6支持56位密钥,bouncycastle支持64位
         * */ 
        publicstatic final String KEY_ALGORITHM="AES"
           
        /**
         * 加密/解密算法/工作模式/填充方式
         
         * JAVA6 支持PKCS5PADDING填充方式
         * Bouncy castle支持PKCS7Padding填充方式
         * */ 
        publicstatic final String CIPHER_ALGORITHM="AES/ECB/PKCS7Padding"
           
        /**
         
         * 生成密钥,java6只支持56位密钥,bouncycastle支持64位密钥
         * @return byte[] 二进制密钥
         * */ 
        publicstatic byte[] initkey()throws Exception{ 
               
//          //实例化密钥生成器 
//          Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
//          KeyGenerator kg=KeyGenerator.getInstance(KEY_ALGORITHM, "BC"); 
//          //初始化密钥生成器,AES要求密钥长度为128位、192位、256位 
////            kg.init(256); 
//          kg.init(128);
//          //生成密钥 
//          SecretKey secretKey=kg.generateKey(); 
//          //获取二进制密钥编码形式 
//          return secretKey.getEncoded(); 
            //为了便于测试,这里我把key写死了,如果大家需要自动生成,可用上面注释掉的代码
            returnnew byte[] {0x08, 0x08,0x04, 0x0b,0x02, 0x0f,0x0b, 0x0c,
                    0x01,0x03, 0x09,0x07, 0x0c,0x03, 0x07,0x0a, 0x04,0x0f,
                    0x06,0x0f, 0x0e,0x09, 0x05,0x01, 0x0a,0x0a, 0x01,0x09,
                    0x06,0x07, 0x09,0x0d };
        }
 
        /**
         * 转换密钥
         * @param key 二进制密钥
         * @return Key 密钥
         * */ 
        publicstatic Key toKey(byte[] key)throws Exception{ 
            //实例化DES密钥 
            //生成密钥 
            SecretKey secretKey=newSecretKeySpec(key,KEY_ALGORITHM); 
            returnsecretKey; 
        
           
        /**
         * 加密数据
         * @param data 待加密数据
         * @param key 密钥
         * @return byte[] 加密后的数据
         * */ 
        publicstatic byte[] encrypt(byte[] data,byte[] key) throws Exception{ 
            //还原密钥 
            Key k=toKey(key); 
            /**
             * 实例化
             * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现
             * Cipher.getInstance(CIPHER_ALGORITHM,"BC")
             */ 
            Security.addProvider(neworg.bouncycastle.jce.provider.BouncyCastleProvider());
            Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM,"BC"); 
            //初始化,设置为加密模式 
            cipher.init(Cipher.ENCRYPT_MODE, k); 
            //执行操作 
            returncipher.doFinal(data); 
        
        /**
         * 解密数据
         * @param data 待解密数据
         * @param key 密钥
         * @return byte[] 解密后的数据
         * */ 
        publicstatic byte[] decrypt(byte[] data,byte[] key) throws Exception{ 
            //欢迎密钥 
            Key k =toKey(key); 
            /**
             * 实例化
             * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现
             * Cipher.getInstance(CIPHER_ALGORITHM,"BC")
             */ 
            Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM); 
            //初始化,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, k); 
            //执行操作 
            returncipher.doFinal(data); 
        
        /**
         * @param args
         * @throws UnsupportedEncodingException
         * @throws Exception 
         */ 
        publicstatic void main(String[] args) throws UnsupportedEncodingException{ 
             
            String str="AES"
            System.out.println("原文:"+str); 
 
            //初始化密钥 
            byte[] key;
            try{
                key = AES256Encryption.initkey();
                System.out.print("密钥:"); 
                for(inti = 0;i<key.length;i++){
                    System.out.printf("%x", key[i]);
                }
                System.out.print("\n");
                //加密数据 
                byte[] data=AES256Encryption.encrypt(str.getBytes(), key); 
                System.out.print("加密后:");
                for(inti = 0;i<data.length;i++){
                    System.out.printf("%x", data[i]);
                }
                System.out.print("\n");
                 
                //解密数据 
                data=AES256Encryption.decrypt(data, key); 
                System.out.println("解密后:"+newString(data));
            }catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            
              
        
    }

      运行程序后的结果截图:

      

      上图可以看到密钥和密文,好了,我们来看看iOS端实现AES256加密解密的方法,有点复杂,大家耐心点看就好。

EncryptAndDecrypt.h文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//
//  EncryptAndDecrypt.h
//  AES256EncryptionDemo
//
//  Created by rich sun on 12-12-13.
//  Copyright (c) 2012年 rich sun. All rights reserved.
//
 
#import <Foundation/Foundation.h>
 
@classNSString;
 
@interface NSData (Encryption)
 
- (NSData *)AES256EncryptWithKey:(NSData *)key;  //加密
- (NSData *)AES256DecryptWithKey:(NSData *)key;  //解密
- (NSString *)newStringInBase64FromData;           //追加64编码
+ (NSString*)base64encode:(NSString*)str;          //同上64编码
 
@end
EncryptAndDecrypt.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//
//  EncryptAndDecrypt.m
//  AES256EncryptionDemo
//
//  Created by rich sun on 12-12-13.
//  Copyright (c) 2012年 rich sun. All rights reserved.
//
 
#import "EncryptAndDecrypt.h"
#import <CommonCrypto/CommonCrypto.h>
staticchar base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
@implementation NSData (Encryption)
 
- (NSData *)AES256EncryptWithKey:(NSData *)key  //加密
{
 
    //AES256加密,密钥应该是32位的
    constvoid * keyPtr2 = [key bytes];
    char(*keyPtr)[32] = keyPtr2;
 
    //对于块加密算法,输出大小总是等于或小于输入大小加上一个块的大小
    //所以在下边需要再加上一个块的大小
    NSUInteger dataLength = [self length];
    size_tbufferSize = dataLength + kCCBlockSizeAES128;
    void*buffer = malloc(bufferSize);
 
    size_tnumBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding/*这里就是刚才说到的PKCS7Padding填充了*/| kCCOptionECBMode,
                                          [key bytes], kCCKeySizeAES256,
                                          NULL,/* 初始化向量(可选) */
                                          [self bytes], dataLength,/*输入*/
                                          buffer, bufferSize,/* 输出 */
                                          &numBytesEncrypted);
 
    if(cryptStatus == kCCSuccess) {
        return[NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    free(buffer);//释放buffer
    returnnil;
}
 
 
- (NSData *)AES256DecryptWithKey:(NSData *)key  //解密
{
 
    //同理,解密中,密钥也是32位的
    constvoid * keyPtr2 = [key bytes];
    char(*keyPtr)[32] = keyPtr2;
 
    //对于块加密算法,输出大小总是等于或小于输入大小加上一个块的大小
    //所以在下边需要再加上一个块的大小
    NSUInteger dataLength = [self length];
    size_tbufferSize = dataLength + kCCBlockSizeAES128;
    void*buffer = malloc(bufferSize);
 
    size_tnumBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding/*这里就是刚才说到的PKCS7Padding填充了*/| kCCOptionECBMode,
                                          keyPtr, kCCKeySizeAES256,
                                          NULL,/* 初始化向量(可选) */
                                          [self bytes], dataLength,/* 输入 */
                                          buffer, bufferSize,/* 输出 */
                                          &numBytesDecrypted);
    if(cryptStatus == kCCSuccess) {
        return[NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }
    free(buffer);
    returnnil;
}
 
 
- (NSString *)newStringInBase64FromData           //追加64编码
{
    NSMutableString *dest = [[NSMutableString alloc] initWithString:@""];
    unsignedchar * working = (unsignedchar *)[self bytes];
    intsrcLen = [self length];
    for(int i=0; i<srcLen; i += 3) {
        for(int nib=0; nib<4; nib++) {
            intbyt = (nib == 0)?0:nib-1;
            intix = (nib+1)*2;
            if(i+byt >= srcLen) break;
            unsignedchar curr = ((working[i+byt] << (8-ix)) & 0x3F);
            if(i+nib < srcLen) curr |= ((working[i+nib] >> ix) & 0x3F);
            [dest appendFormat:@"%c", base64[curr]];
        }
    }
    returndest;
}
 
+ (NSString*)base64encode:(NSString*)str
{
    if([str length] == 0)
        return@"";
    constchar *source = [str UTF8String];
    intstrlength  = strlen(source);
    char*characters = malloc(((strlength + 2) / 3) * 4);
    if(characters == NULL)
        returnnil;
    NSUInteger length = 0;
    NSUInteger i = 0;
    while(i < strlength) {
        charbuffer[3] = {0,0,0};
        shortbufferLength = 0;
        while(bufferLength < 3 && i < strlength)
            buffer[bufferLength++] = source[i++];
        characters[length++] = base64[(buffer[0] & 0xFC) >> 2];
        characters[length++] = base64[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)];
        if(bufferLength > 1)
            characters[length++] = base64[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)];
        elsecharacters[length++] = '=';
        if(bufferLength > 2)
            characters[length++] = base64[buffer[2] & 0x3F];
        elsecharacters[length++] = '=';
    }
    NSString *g = [[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES];
    returng;
}
 
@end

      ViewController.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
//
//  ViewController.m
//  AES256EncryptionDemo
//
//  Created by 孙 裔 on 12-12-13.
//  Copyright (c) 2012年 rich sun. All rights reserved.
//
 
#import "ViewController.h"
#import "EncryptAndDecrypt.h"
 
@interface ViewController ()
 
@end
 
@implementation ViewController
@synthesize plainTextField;
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
 
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
//这个函数实现了用户输入完后点击视图背景,关闭键盘
- (IBAction)backgroundTap:(id)sender{
    [plainTextField resignFirstResponder];
}
 
- (IBAction)encrypt:(id)sender {
     
    NSString *plainText = plainTextField.text;//明文
    NSData *plainTextData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
     
    //为了测试,这里先把密钥写死
    Byte keyByte[] = {0x08,0x08,0x04,0x0b,0x02,0x0f,0x0b,0x0c,0x01,0x03,0x09,0x07,0x0c,0x03,
        0x07,0x0a,0x04,0x0f,0x06,0x0f,0x0e,0x09,0x05,0x01,0x0a,0x0a,0x01,0x09,
        0x06,0x07,0x09,0x0d};
    //byte转换为NSData类型,以便下边加密方法的调用
    NSData *keyData = [[NSData alloc] initWithBytes:keyByte length:32];
    //
    NSData *cipherTextData = [plainTextData AES256EncryptWithKey:keyData];
    Byte *plainTextByte = (Byte *)[cipherTextData bytes];
    for(inti=0;i<[cipherTextData length];i++){
        printf("%x",plainTextByte[i]);
    }
 
}
@end

运行程序,这里需要自己创建一个简单的应用程序,简单的布局:


加密后的密文:


      大家可以看到这里的密文和java端的密文是一致的,这样我们就成功完成了。只要密文和密钥是一致的,那么解密应该就不会有什么问题了后面如果有需要解密的可以自己调用解密的那个方法就可以了。

      如有什么不明之处欢迎大家留言,互相学习!很遗憾这里无法上传附件,代码中需要的包只能以链接的方式让各位去下了:

jce_policy-6.zip 下载链接:http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html

下载解压后将里边的两个jar包(local_policy.jar,US_export_policy.jar)替换掉jdk安装路径下security文件夹中的两个包。

bcprov-jdk16-139.jar 下载链接:http://www.bouncycastle.org

      终于写完了。。。第一次写博文,虽然有点费时,但感觉很好!如有转载,请注明出处,谢谢大家!

0 0
原创粉丝点击