關於AES256算法java端加密,ios端解密出現沒法解密問題的解決方案

      我想關於AES算法你們應該都已經瞭解了,我就很少介紹了。這是本人第一次寫技術博文,若是有不對之處歡迎你們指正,共同討論,一塊兒學習! html

      以前在項目上用到AES256加密解密算法,剛開始在java端加密解密都沒有問題,在iOS端加密解密也沒有問題。可是奇怪的是在java端加密後的文件在iOS端沒法正確解密打開,而後簡單測試了一下,發如今java端和iOS端採用相同明文,相同密鑰加密後的密文不同!上網查了資料後發現iOS中AES加密算法採用的填充是PKCS7Padding,而java不支持PKCS7Padding,只支持PKCS5Padding。咱們知道加密算法由算法+模式+填充組成,因此這二者不一樣的填充算法致使相同明文相同密鑰加密後出現密文不一致的狀況。那麼咱們須要在java中用PKCS7Padding來填充,這樣就能夠和iOS端填充算法一致了。 java

      要實如今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();
			}  
	         
	    }  
	}

      運行程序後的結果截圖: oracle

      

      上圖能夠看到密鑰和密文,好了,咱們來看看iOS端實現AES256加密解密的方法,有點複雜,你們耐心點看就好。 app

EncryptAndDecrypt.h文件 ide

//
//  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文件

//
//  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

      終於寫完了。。。第一次寫博文,雖然有點費時,但感受很好!若有轉載,請註明出處,謝謝你們!

相關文章
相關標籤/搜索