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

原贴地址:http://my.oschina.net/nicsun/blog/95632 

 我想关于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 importjava.security.Key; 
006 import java.security.Security;
007  
008 importjavax.crypto.Cipher; 
009 importjavax.crypto.SecretKey; 
010 importjavax.crypto.spec.SecretKeySpec; 
011  
012 public classAES256Encryption{ 
013      
014          /**
015          * 密钥算法
016          * java6支持56位密钥,bouncycastle支持64位
017          * */ 
018         public static finalString KEY_ALGORITHM="AES"
019            
020         /**
021          * 加密/解密算法/工作模式/填充方式
022          
023          * JAVA6 支持PKCS5PADDING填充方式
024          * Bouncy castle支持PKCS7Padding填充方式
025          * */ 
026         public static finalString CIPHER_ALGORITHM="AES/ECB/PKCS7Padding"
027            
028         /**
029          
030          * 生成密钥,java6只支持56位密钥,bouncycastle支持64位密钥
031          * @return byte[] 二进制密钥
032          * */ 
033         public static byte[] initkey() throwsException{ 
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) throwsException{ 
058             //实例化DES密钥 
059             //生成密钥 
060             SecretKey secretKey=newSecretKeySpec(key,KEY_ALGORITHM); 
061             returnsecretKey; 
062         
063            
064         /**
065          * 加密数据
066          * @param data 待加密数据
067          * @param key 密钥
068          * @return byte[] 加密后的数据
069          * */ 
070         public static byte[] encrypt(byte[] data,byte[] key) throwsException{ 
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             returncipher.doFinal(data); 
084         
085         /**
086          * 解密数据
087          * @param data 待解密数据
088          * @param key 密钥
089          * @return byte[] 解密后的数据
090          * */ 
091         public static byte[] decrypt(byte[] data,byte[] key) throwsException{ 
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             returncipher.doFinal(data); 
104         
105         /**
106          * @param args
107          * @throws UnsupportedEncodingException
108          * @throws Exception 
109          */ 
110         public static void main(String[] args) throwsUnsupportedEncodingException{ 
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("解密后:"+newString(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