原贴地址: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 | 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 [] { 0x08 , 0x08 , 0x04 , 0x0b , 0x02 , 0x0f , 0x0b , 0x0c , |
047 | 0x01 , 0x03 , 0x09 , 0x07 , 0x0c , 0x03 , 0x07 , 0x0a , 0x04 , 0x0f , |
048 | 0x06 , 0x0f , 0x0e , 0x09 , 0x05 , 0x01 , 0x0a , 0x0a , 0x01 , 0x09 , |
049 | 0x06 , 0x07 , 0x09 , 0x0d }; |
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 |
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 |
|