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

我想关于AES算法大家应该都已经了解了,我就不多介绍了。这是本人第一次写技术博文,如果有不对之处欢迎大家指正,共同讨论,一起学习!

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

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

001 package com.encrypt.file;
002  
003  
004 import java.io.UnsupportedEncodingException;
005 import java.security.Key; 
006 import java.security.Security;
007  
008 import javax.crypto.Cipher; 
009 import javax.crypto.SecretKey; 
010 import javax.crypto.spec.SecretKeySpec; 
011  
012 public class AES256Encryption{ 
013      
014          /**
015          * 密钥算法
016          * java6支持56位密钥,bouncycastle支持64位
017          * */ 
018         public static final String KEY_ALGORITHM="AES"
019            
020         /**
021          * 加密/解密算法/工作模式/填充方式
022          
023          * JAVA6 支持PKCS5PADDING填充方式
024          * Bouncy castle支持PKCS7Padding填充方式
025          * */ 
026         public static final String CIPHER_ALGORITHM="AES/ECB/PKCS7Padding"
027            
028         /**
029          
030          * 生成密钥,java6只支持56位密钥,bouncycastle支持64位密钥
031          * @return byte[] 二进制密钥
032          * */ 
033         public static byte[] initkey() throws Exception{ 
034                
035 //          //实例化密钥生成器 
036 //          Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
037 //          KeyGenerator kg=KeyGenerator.getInstance(KEY_ALGORITHM, "BC"); 
038 //          //初始化密钥生成器,AES要求密钥长度为128位、192位、256位 
039 ////            kg.init(256); 
040 //          kg.init(128);
041 //          //生成密钥 
042 //          SecretKey secretKey=kg.generateKey(); 
043 //          //获取二进制密钥编码形式 
044 //          return secretKey.getEncoded(); 
045             //为了便于测试,这里我把key写死了,如果大家需要自动生成,可用上面注释掉的代码
046             return new byte[] { 0x080x080x040x0b0x020x0f0x0b0x0c,
047                     0x010x030x090x070x0c0x030x070x0a0x040x0f,
048                     0x060x0f0x0e0x090x050x010x0a0x0a0x010x09,
049                     0x060x070x090x0d };
050         }
051  
052         /**
053          * 转换密钥
054          * @param key 二进制密钥
055          * @return Key 密钥
056          * */ 
057         public static Key toKey(byte[] key) throws Exception{ 
058             //实例化DES密钥 
059             //生成密钥 
060             SecretKey secretKey=new SecretKeySpec(key,KEY_ALGORITHM); 
061             return secretKey; 
062         
063            
064         /**
065          * 加密数据
066          * @param data 待加密数据
067          * @param key 密钥
068          * @return byte[] 加密后的数据
069          * */ 
070         public static byte[] encrypt(byte[] data,byte[] key) throws Exception{ 
071             //还原密钥 
072             Key k=toKey(key); 
073             /**
074              * 实例化
075              * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现
076              * Cipher.getInstance(CIPHER_ALGORITHM,"BC")
077              */ 
078             Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
079             Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM, "BC"); 
080             //初始化,设置为加密模式 
081             cipher.init(Cipher.ENCRYPT_MODE, k); 
082             //执行操作 
083             return cipher.doFinal(data); 
084         
085         /**
086          * 解密数据
087          * @param data 待解密数据
088          * @param key 密钥
089          * @return byte[] 解密后的数据
090          * */ 
091         public static byte[] decrypt(byte[] data,byte[] key) throws Exception{ 
092             //欢迎密钥 
093             Key k =toKey(key); 
094             /**
095              * 实例化
096              * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现
097              * Cipher.getInstance(CIPHER_ALGORITHM,"BC")
098              */ 
099             Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM); 
100             //初始化,设置为解密模式
101             cipher.init(Cipher.DECRYPT_MODE, k); 
102             //执行操作 
103             return cipher.doFinal(data); 
104         
105         /**
106          * @param args
107          * @throws UnsupportedEncodingException
108          * @throws Exception 
109          */ 
110         public static void main(String[] args) throws UnsupportedEncodingException{ 
111              
112             String str="AES"
113             System.out.println("原文:"+str); 
114  
115             //初始化密钥 
116             byte[] key;
117             try {
118                 key = AES256Encryption.initkey();
119                 System.out.print("密钥:"); 
120                 for(int i = 0;i<key.length;i++){
121                     System.out.printf("%x", key[i]);
122                 }
123                 System.out.print("\n");
124                 //加密数据 
125                 byte[] data=AES256Encryption.encrypt(str.getBytes(), key); 
126                 System.out.print("加密后:");
127                 for(int i = 0;i<data.length;i++){
128                     System.out.printf("%x", data[i]);
129                 }
130                 System.out.print("\n");
131                  
132                 //解密数据 
133                 data=AES256Encryption.decrypt(data, key); 
134                 System.out.println("解密后:"+new String(data));
135             catch (Exception e) {
136                 // TODO Auto-generated catch block
137                 e.printStackTrace();
138             
139               
140         
141     }

      运行程序后的结果截图:

      

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

EncryptAndDecrypt.h文件

01 //
02 //  EncryptAndDecrypt.h
03 //  AES256EncryptionDemo
04 //
05 //  Created by rich sun on 12-12-13.
06 //  Copyright (c) 2012年 rich sun. All rights reserved.
07 //
08  
09 #import <Foundation/Foundation.h>
10  
11 @class NSString;
12  
13 @interface NSData (Encryption)
14  
15 - (NSData *)AES256EncryptWithKey:(NSData *)key;   //加密
16 - (NSData *)AES256DecryptWithKey:(NSData *)key;   //解密
17 - (NSString *)newStringInBase64FromData;            //追加64编码
18 + (NSString*)base64encode:(NSString*)str;           //同上64编码
19  
20 @end

EncryptAndDecrypt.m文件

 

001 //
002 //  EncryptAndDecrypt.m
003 //  AES256EncryptionDemo
004 //
005 //  Created by rich sun on 12-12-13.
006 //  Copyright (c) 2012年 rich sun. All rights reserved.
007 //
008  
009 #import "EncryptAndDecrypt.h"
010 #import <CommonCrypto/CommonCrypto.h>
011 static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
012  
013 @implementation NSData (Encryption)
014  
015 - (NSData *)AES256EncryptWithKey:(NSData *)key   //加密
016 {
017  
018     //AES256加密,密钥应该是32位的
019     const void * keyPtr2 = [key bytes];
020     char (*keyPtr)[32] = keyPtr2;
021  
022     //对于块加密算法,输出大小总是等于或小于输入大小加上一个块的大小
023     //所以在下边需要再加上一个块的大小
024     NSUInteger dataLength = [self length];
025     size_t bufferSize = dataLength + kCCBlockSizeAES128;
026     void *buffer = malloc(bufferSize);
027  
028     size_t numBytesEncrypted = 0;
029     CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
030                                           kCCOptionPKCS7Padding/*这里就是刚才说到的PKCS7Padding填充了*/ | kCCOptionECBMode,
031                                           [key bytes], kCCKeySizeAES256,
032                                           NULL,/* 初始化向量(可选) */
033                                           [self bytes], dataLength,/*输入*/
034                                           buffer, bufferSize,/* 输出 */
035                                           &numBytesEncrypted);
036  
037     if (cryptStatus == kCCSuccess) {
038         return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
039     }
040     free(buffer);//释放buffer
041     return nil;
042 }
043  
044  
045 - (NSData *)AES256DecryptWithKey:(NSData *)key   //解密
046 {
047  
048     //同理,解密中,密钥也是32位的
049     const void * keyPtr2 = [key bytes];
050     char (*keyPtr)[32] = keyPtr2;
051  
052     //对于块加密算法,输出大小总是等于或小于输入大小加上一个块的大小
053     //所以在下边需要再加上一个块的大小
054     NSUInteger dataLength = [self length];
055     size_t bufferSize = dataLength + kCCBlockSizeAES128;
056     void *buffer = malloc(bufferSize);
057  
058     size_t numBytesDecrypted = 0;
059     CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128,
060                                           kCCOptionPKCS7Padding/*这里就是刚才说到的PKCS7Padding填充了*/ | kCCOptionECBMode,
061                                           keyPtr, kCCKeySizeAES256,
062                                           NULL,/* 初始化向量(可选) */
063                                           [self bytes], dataLength,/* 输入 */
064                                           buffer, bufferSize,/* 输出 */
065                                           &numBytesDecrypted);
066     if (cryptStatus == kCCSuccess) {
067         return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
068     }
069     free(buffer);
070     return nil;
071 }
072  
073  
074 - (NSString *)newStringInBase64FromData            //追加64编码
075 {
076     NSMutableString *dest = [[NSMutableString alloc] initWithString:@""];
077     unsigned char * working = (unsigned char *)[self bytes];
078     int srcLen = [self length];
079     for (int i=0; i<srcLen; i += 3) {
080         for (int nib=0; nib<4; nib++) {
081             int byt = (nib == 0)?0:nib-1;
082             int ix = (nib+1)*2;
083             if (i+byt >= srcLen) break;
084             unsigned char curr = ((working[i+byt] << (8-ix)) & 0x3F);
085             if (i+nib < srcLen) curr |= ((working[i+nib] >> ix) & 0x3F);
086             [dest appendFormat:@"%c", base64[curr]];
087         }
088     }
089     return dest;
090 }
091  
092 + (NSString*)base64encode:(NSString*)str
093 {
094     if ([str length] == 0)
095         return @"";
096     const char *source = [str UTF8String];
097     int strlength  = strlen(source);
098     char *characters = malloc(((strlength + 2) / 3) * 4);
099     if (characters == NULL)
100         return nil;
101     NSUInteger length = 0;
102     NSUInteger i = 0;
103     while (i < strlength) {
104         char buffer[3] = {0,0,0};
105         short bufferLength = 0;
106         while (bufferLength < 3 && i < strlength)
107             buffer[bufferLength++] = source[i++];
108         characters[length++] = base64[(buffer[0] & 0xFC) >> 2];
109         characters[length++] = base64[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)];
110         if (bufferLength > 1)
111             characters[length++] = base64[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)];
112         else characters[length++] = '=';
113         if (bufferLength > 2)
114             characters[length++] = base64[buffer[2] & 0x3F];
115         else characters[length++] = '=';
116     }
117     NSString *g = [[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES];
118     return g;
119 }
120  
121 @end

      ViewController.m文件

 

01 //
02 //  ViewController.m
03 //  AES256EncryptionDemo
04 //
05 //  Created by 孙 裔 on 12-12-13.
06 //  Copyright (c) 2012年 rich sun. All rights reserved.
07 //
08  
09 #import "ViewController.h"
10 #import "EncryptAndDecrypt.h"
11  
12 @interface ViewController ()
13  
14 @end
15  
16 @implementation ViewController
17 @synthesize plainTextField;
18 - (void)viewDidLoad
19 {
20     [super viewDidLoad];
21     // Do any additional setup after loading the view, typically from a nib.
22 }
23  
24 - (void)didReceiveMemoryWarning
25 {
26     [super didReceiveMemoryWarning];
27     // Dispose of any resources that can be recreated.
28 }
29 //这个函数实现了用户输入完后点击视图背景,关闭键盘
30 - (IBAction)backgroundTap:(id)sender{
31     [plainTextField resignFirstResponder];
32 }
33  
34 - (IBAction)encrypt:(id)sender {
35      
36     NSString *plainText = plainTextField.text;//明文
37     NSData *plainTextData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
38      
39     //为了测试,这里先把密钥写死
40     Byte keyByte[] = {0x08,0x08,0x04,0x0b,0x02,0x0f,0x0b,0x0c,0x01,0x03,0x09,0x07,0x0c,0x03,
41         0x07,0x0a,0x04,0x0f,0x06,0x0f,0x0e,0x09,0x05,0x01,0x0a,0x0a,0x01,0x09,
42         0x06,0x07,0x09,0x0d};
43     //byte转换为NSData类型,以便下边加密方法的调用
44     NSData *keyData = [[NSData alloc] initWithBytes:keyByte length:32];
45     //
46     NSData *cipherTextData = [plainTextData AES256EncryptWithKey:keyData];
47     Byte *plainTextByte = (Byte *)[cipherTextData bytes];
48     for(int i=0;i<[cipherTextData length];i++){
49         printf("%x",plainTextByte[i]);
50     }
51  
52 }
53 @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

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

 

NSString *str = @"AA21f0c1762a3abc299c013abe7dbcc50001DD"
怎么将里面的字符转换到Byte数组中,如下
Byte buffer[] = { 0xAA, 0x21, 0xf0, 0xc1, 0x76, 0x2a, 0x3a, ... , 0x01, 0xDD }
各位大侠恳请帮帮忙,先谢谢啦
找到答案了,
@interface NSString (NSStringHexToBytes)
-(NSData*) hexToBytes ;
@end
@implementation NSString (NSStringHexToBytes)
-(NSData*) hexToBytes {
    NSMutableData* data = [NSMutableDatadata];
    int idx;
    for (idx = 0; idx+2 <= self.length; idx+=2) {
        NSRange range = NSMakeRange(idx, 2);
        NSString* hexStr = [selfsubstringWithRange:range];
        NSScanner* scanner = [NSScannerscannerWithString:hexStr];
        unsignedint intValue;
        [scanner scanHexInt:&intValue];
        [data appendBytes:&intValue length:1];
    }
    return data;
}
@end

应用:
NSData *data = [str hexToBytes];
 
 
2012-11-06 18:49 提问者采纳
 
NSString *str = @"AA21f0c1762a3abc299c013abe7dbcc50001DD";

NSData* bytes = [str dataUsingEncoding:NSUTF8StringEncoding];
Byte * myByte = (Byte *)[bytes bytes];
NSLog(@"myByte = %s",myByte);

猜你喜欢

转载自834945712.iteye.com/blog/2025242