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

我想关于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位
          * */ 
         public static final String KEY_ALGORITHM= "AES"
           
         /**
          * 加密/解密算法/工作模式/填充方式
         
          * JAVA6 支持PKCS5PADDING填充方式
          * Bouncy castle支持PKCS7Padding填充方式
          * */ 
         public static final String CIPHER_ALGORITHM= "AES/ECB/PKCS7Padding"
           
         /**
         
          * 生成密钥,java6只支持56位密钥,bouncycastle支持64位密钥
          * @return byte[] 二进制密钥
          * */ 
         public static 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写死了,如果大家需要自动生成,可用上面注释掉的代码
             return new 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 密钥
          * */ 
         public static Key toKey( byte [] key) throws Exception{ 
             //实例化DES密钥 
             //生成密钥 
             SecretKey secretKey= new SecretKeySpec(key,KEY_ALGORITHM); 
             return secretKey; 
        
           
         /**
          * 加密数据
          * @param data 待加密数据
          * @param key 密钥
          * @return byte[] 加密后的数据
          * */ 
         public static byte [] encrypt( byte [] data, byte [] key) throws Exception{ 
             //还原密钥 
             Key k=toKey(key); 
             /**
              * 实例化
              * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现
              * Cipher.getInstance(CIPHER_ALGORITHM,"BC")
              */ 
             Security.addProvider( new org.bouncycastle.jce.provider.BouncyCastleProvider());
             Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM, "BC" ); 
             //初始化,设置为加密模式 
             cipher.init(Cipher.ENCRYPT_MODE, k); 
             //执行操作 
             return cipher.doFinal(data); 
        
         /**
          * 解密数据
          * @param data 待解密数据
          * @param key 密钥
          * @return byte[] 解密后的数据
          * */ 
         public static 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); 
             //执行操作 
             return cipher.doFinal(data); 
        
         /**
          * @param args
          * @throws UnsupportedEncodingException
          * @throws Exception 
          */ 
         public static void main(String[] args) throws UnsupportedEncodingException{ 
             
             String str= "AES"
             System.out.println( "原文:" +str); 
 
             //初始化密钥 
             byte [] key;
             try {
                 key = AES256Encryption.initkey();
                 System.out.print( "密钥:" ); 
                 for ( int i = 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 ( int i = 0 ;i<data.length;i++){
                     System.out.printf( "%x" , data[i]);
                 }
                 System.out.print( "\n" );
                 
                 //解密数据 
                 data=AES256Encryption.decrypt(data, key); 
                 System.out.println( "解密后:" + new String(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>
 
@ class NSString;
 
@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>
static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
 
@implementation NSData (Encryption)
 
- (NSData *)AES256EncryptWithKey:(NSData *)key   //加密
{
 
     //AES256加密,密钥应该是32位的
     const void * keyPtr2 = [key bytes];
     char (*keyPtr)[32] = keyPtr2;
 
     //对于块加密算法,输出大小总是等于或小于输入大小加上一个块的大小
     //所以在下边需要再加上一个块的大小
     NSUInteger dataLength = [self length];
     size_t bufferSize = dataLength + kCCBlockSizeAES128;
     void *buffer = malloc (bufferSize);
 
     size_t numBytesEncrypted = 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
     return nil;
}
 
 
- (NSData *)AES256DecryptWithKey:(NSData *)key   //解密
{
 
     //同理,解密中,密钥也是32位的
     const void * keyPtr2 = [key bytes];
     char (*keyPtr)[32] = keyPtr2;
 
     //对于块加密算法,输出大小总是等于或小于输入大小加上一个块的大小
     //所以在下边需要再加上一个块的大小
     NSUInteger dataLength = [self length];
     size_t bufferSize = dataLength + kCCBlockSizeAES128;
     void *buffer = malloc (bufferSize);
 
     size_t numBytesDecrypted = 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);
     return nil;
}
 
 
- (NSString *)newStringInBase64FromData            //追加64编码
{
     NSMutableString *dest = [[NSMutableString alloc] initWithString:@ "" ];
     unsigned char * working = (unsigned char *)[self bytes];
     int srcLen = [self length];
     for ( int i=0; i<srcLen; i += 3) {
         for ( int nib=0; nib<4; nib++) {
             int byt = (nib == 0)?0:nib-1;
             int ix = (nib+1)*2;
             if (i+byt >= srcLen) break ;
             unsigned char curr = ((working[i+byt] << (8-ix)) & 0x3F);
             if (i+nib < srcLen) curr |= ((working[i+nib] >> ix) & 0x3F);
             [dest appendFormat:@ "%c" , base64[curr]];
         }
     }
     return dest;
}
 
+ (NSString*)base64encode:(NSString*)str
{
     if ([str length] == 0)
         return @ "" ;
     const char *source = [str UTF8String];
     int strlength  = strlen (source);
     char *characters = malloc (((strlength + 2) / 3) * 4);
     if (characters == NULL)
         return nil;
     NSUInteger length = 0;
     NSUInteger i = 0;
     while (i < strlength) {
         char buffer[3] = {0,0,0};
         short bufferLength = 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)];
         else characters[length++] = '=' ;
         if (bufferLength > 2)
             characters[length++] = base64[buffer[2] & 0x3F];
         else characters[length++] = '=' ;
     }
     NSString *g = [[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES];
     return g;
}
 
@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 ( int i=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
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
EVP_aes_256_cbc是OpenSSL库提供的一种加密算法,可以用于对大文件进行加密解密。下面是c++实现EVP_aes_256_cbc大文件加密解密的示例代码: ```c++ #include <openssl/evp.h> #include <iostream> #include <fstream> using namespace std; int aes_encrypt(const char *key, const char *iv, const char *infile, const char *outfile) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { cerr << "Error: EVP_CIPHER_CTX_new() failed" << endl; return -1; } // 初始化加密上下文 if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (const unsigned char *)key, (const unsigned char *)iv) != 1) { cerr << "Error: EVP_EncryptInit_ex() failed" << endl; EVP_CIPHER_CTX_free(ctx); return -1; } // 打开输入文件 ifstream fin(infile, ios::binary); if (!fin.is_open()) { cerr << "Error: Cannot open input file" << endl; EVP_CIPHER_CTX_free(ctx); return -1; } // 打开输出文件 ofstream fout(outfile, ios::binary); if (!fout.is_open()) { cerr << "Error: Cannot create output file" << endl; fin.close(); EVP_CIPHER_CTX_free(ctx); return -1; } // 读取输入文件并加密 const int BUFSIZE = 1024 * 1024; unsigned char inbuf[BUFSIZE]; unsigned char outbuf[BUFSIZE + EVP_CIPHER_block_size(EVP_aes_256_cbc())]; int inlen, outlen; while (!fin.eof()) { fin.read((char *)inbuf, BUFSIZE); inlen = fin.gcount(); if (EVP_EncryptUpdate(ctx, outbuf, &outlen, inbuf, inlen) != 1) { cerr << "Error: EVP_EncryptUpdate() failed" << endl; fin.close(); fout.close(); EVP_CIPHER_CTX_free(ctx); return -1; } fout.write((char *)outbuf, outlen); } // 结束加密 if (EVP_EncryptFinal_ex(ctx, outbuf, &outlen) != 1) { cerr << "Error: EVP_EncryptFinal_ex() failed" << endl; fin.close(); fout.close(); EVP_CIPHER_CTX_free(ctx); return -1; } fout.write((char *)outbuf, outlen); // 关闭文件和加密上下文 fin.close(); fout.close(); EVP_CIPHER_CTX_free(ctx); return 0; } int aes_decrypt(const char *key, const char *iv, const char *infile, const char *outfile) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { cerr << "Error: EVP_CIPHER_CTX_new() failed" << endl; return -1; } // 初始化解密上下文 if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, (const unsigned char *)key, (const unsigned char *)iv) != 1) { cerr << "Error: EVP_DecryptInit_ex() failed" << endl; EVP_CIPHER_CTX_free(ctx); return -1; } // 打开输入文件 ifstream fin(infile, ios::binary); if (!fin.is_open()) { cerr << "Error: Cannot open input file" << endl; EVP_CIPHER_CTX_free(ctx); return -1; } // 打开输出文件 ofstream fout(outfile, ios::binary); if (!fout.is_open()) { cerr << "Error: Cannot create output file" << endl; fin.close(); EVP_CIPHER_CTX_free(ctx); return -1; } // 读取输入文件并解密 const int BUFSIZE = 1024 * 1024 + EVP_CIPHER_block_size(EVP_aes_256_cbc()); unsigned char inbuf[BUFSIZE]; unsigned char outbuf[BUFSIZE]; int inlen, outlen; while (!fin.eof()) { fin.read((char *)inbuf, BUFSIZE); inlen = fin.gcount(); if (EVP_DecryptUpdate(ctx, outbuf, &outlen, inbuf, inlen) != 1) { cerr << "Error: EVP_DecryptUpdate() failed" << endl; fin.close(); fout.close(); EVP_CIPHER_CTX_free(ctx); return -1; } fout.write((char *)outbuf, outlen); } // 结束解密 if (EVP_DecryptFinal_ex(ctx, outbuf, &outlen) != 1) { cerr << "Error: EVP_DecryptFinal_ex() failed" << endl; fin.close(); fout.close(); EVP_CIPHER_CTX_free(ctx); return -1; } fout.write((char *)outbuf, outlen); // 关闭文件和解密上下文 fin.close(); fout.close(); EVP_CIPHER_CTX_free(ctx); return 0; } int main() { const char *key = "0123456789abcdef0123456789abcdef"; const char *iv = "0123456789abcdef"; const char *infile = "input.txt"; const char *outfile = "output.txt"; // 加密 if (aes_encrypt(key, iv, infile, outfile) != 0) { cerr << "Error: Encryption failed" << endl; return -1; } cout << "Encryption succeeded" << endl; // 解密 if (aes_decrypt(key, iv, outfile, "input_decrypted.txt") != 0) { cerr << "Error: Decryption failed" << endl; return -1; } cout << "Decryption succeeded" << endl; return 0; } ``` 这个示例代码中,我们使用了OpenSSL库提供的EVP_aes_256_cbc加密算法,对给定的输入文件进行加密解密。在实际使用中,你需要根据自己的需求修改参数,比如加密算法、密钥、向量、输入文件和输出文件等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值