关于AES256算法java端加密,ios端解密出现无法解密问题的解决方案
来源:互联网 发布:外贸进销存软件 编辑:程序博客网 时间:2024/05/16 07:11
之前在项目上用到AES256加密解密算法,刚开始在java端加密解密都没有问题,在iOS端加密解密也没有问题。但是奇怪的是在java端加密后的文件在iOS端无法正确解密打开,然后简单测试了一下,发现在java端和iOS端采用相同明文,相同密钥加密后的密文不一样!上网查了资料后发现iOS中AES加密算法采用的填充是PKCS7Padding,而java不支持PKCS7Padding,只支持PKCS5Padding。我们知道加密算法由算法+模式+填充组成,所以这两者不同的填充算法导致相同明文相同密钥加密后出现密文不一致的情况。那么我们需要在java中用PKCS7Padding来填充,这样就可以和iOS端填充算法一致了。
要实现在java端用PKCS7Padding填充,需要用到bouncycastle组件来实现,下面我会提供该包的下载。啰嗦了一大堆,下面是一个简单的测试,上代码!
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文件
//
// 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
// 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文件
//
// 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
终于写完了。。。第一次写博文,虽然有点费时,但感觉很好!如有转载,请注明出处,谢谢大家!
- 关于AES256算法java端加密,ios端解密出现无法解密问题的解决方案
- 【转】关于AES256算法java端加密,ios端解密出现无法解密问题的解决方案
- 关于AES256算法java端加密,ios端解密出现无法解密问题的解决方案
- 关于AES256算法java端加密,ios端解密出现无法解密问题的解决方案
- Java AES256加密解密实现
- Java AES256加密解密实现
- java关于base64加密解密的算法!
- iOS AES256/RSA/DES/MD5/sha-1 (加密/解密)
- [iOS]加密解密算法
- 网络上DesUtil.java关于汉字加密解密出现乱码的问题
- Java实现AES256加解密
- Java实现AES256加解密
- Java加密解密算法-AES加密解密
- Java加密解密算法-Base64加密解密
- Java加密解密算法-MD5加密解密
- Java加密解密算法-DES加密解密
- Java加密解密算法-PBE加密解密
- java加密解密算法
- IOS7 TableView适配
- 在外部PHP文件中引用Magento代码
- 组合数学--错排问题
- 【原创】Zend Framework 2框架之MVC
- C语言实现大整数运算
- 关于AES256算法java端加密,ios端解密出现无法解密问题的解决方案
- 小波变换wavelet
- IOS6之AutoLayout(三)
- 手把手,快速搭建 Cocos2d-HTML5 开发调试环境 分享0
- Hello world!
- 楼上ajax请求超时设置
- Linux----文件I/O
- 百度地图API之定位+公交地铁线路导航
- AC自动机