中文用於網路傳輸要先用 java.net.URLEncoder 的encode方法加密再調用你自己的加密方法。
反過來,接收到密文的時候在調用自己的解密方法解密後需再調用 java.net.URLDecoder 解密,這樣中文就正常了
2. 關於IOS客戶端使用AES加密(解密),雲端java實現加密(解密)不一致的問題。
對AES不了解,但可以考慮將解密作個DLL,java的jni機制調用。。。
3. ios開發中aes加密填充位元組iv怎麼填充
之前在項目上用到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[] { 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) 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) {
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 }
4. JAVA和IOS區別是什麼
安卓和ios要比java先進很多。java是一個編程語言,手機支持java是指的支持用java語言寫成的程序,算不上智能系統。安卓和ios最大的區別就是品牌范圍。安卓是開源系統,因此很多廠商用,用戶多,應用程序數量也在增長,已經超過了ios,這個系統最大的好處就是不用花錢買應用,都是免費的,但是系統與程序兼容性不好,因為各個廠商的手機配置不一樣,性能不一樣,程序編寫技術員很難做到讓每個手機都能匹配合適,因此,如果要用安卓的手機,就選大品牌的或者是銷量領先的,以保證兼容問題。
5. java如何用Aes加密和解密
你解密的key必須是加密的key啊
你看看,你解密的時候又KeyGenerator.getInstance("AES").generateKey();這是重新搞了一個key啊,當然解不出來了
我估計你這代碼人家原先是寫在一起的吧,加密完了再直接解密給你看,人家只generateKey一次,自然很順利,你分成了兩個例子,居然分別generateKey,自然失敗
6. 關於iOS aes256加密的問題,請各位幫忙,搞了一個星期,急求答案!
之前在項目上用到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[] { 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) 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) {
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 }
運行程序後的結果截圖:
ViewController.m文件
01 //
02 // ViewController.m
03 // AES256EncryptionDemo
04 //
05 // Created by 孫 裔 on 12-12-13.
06 // Copyright (c) 2012年 rich sun. All rights reserved.
07 //
08
09 #import "ViewController.h"
10 #import "EncryptAndDecrypt.h"
11
12 @interface ViewController ()
13
14 @end
15
16 @implementation ViewController
17 @synthesize plainTextField;
18 - (void)viewDidLoad
19 {
20 [super viewDidLoad];
21 // Do any additional setup after loading the view, typically from a nib.
22 }
23
24 - (void)didReceiveMemoryWarning
25 {
26 [super didReceiveMemoryWarning];
27 // Dispose of any resources that can be recreated.
28 }
29 //這個函數實現了用戶輸入完後點擊視圖背景,關閉鍵盤
30 - (IBAction)backgroundTap:(id)sender{
31 [plainTextField resignFirstResponder];
32 }
33
34 - (IBAction)encrypt:(id)sender {
35
36 NSString *plainText = plainTextField.text;//明文
37 NSData *plainTextData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
38
39 //為了測試,這里先把密鑰寫死
40 Byte keyByte[] = {0x08,0x08,0x04,0x0b,0x02,0x0f,0x0b,0x0c,0x01,0x03,0x09,0x07,0x0c,0x03,
41 0x07,0x0a,0x04,0x0f,0x06,0x0f,0x0e,0x09,0x05,0x01,0x0a,0x0a,0x01,0x09,
42 0x06,0x07,0x09,0x0d};
43 //byte轉換為NSData類型,以便下邊加密方法的調用
44 NSData *keyData = [[NSData alloc] initWithBytes:keyByte length:32];
45 //
46 NSData *cipherTextData = [plainTextData AES256EncryptWithKey:keyData];
47 Byte *plainTextByte = (Byte *)[cipherTextData bytes];
48 for(int i=0;i<[cipherTextData length];i++){
49 printf("%x",plainTextByte[i]);
50 }
51
52 }
53 @end
附上出處鏈接:http://blog.csdn.net/pjk1129/article/details/8489550
7. android,java 通用的加密解密方式有幾種
移動端越來越火了,我們在開發過程中,總會碰到要和移動端打交道的場景,比如.NET和android或者iOS的打交道。為了讓數據交互更安全,我們需要對數據進行加密傳輸。今天研究了一下,把幾種語言的加密都實踐了一遍,實現了.NET,java(android),iOS都同一套的加密演算法,下面就分享給大家。
AES加密有多種演算法模式,下面提供兩套模式的可用源碼。
加密方式:
先將文本AES加密
返回Base64轉碼
解密方式:
將數據進行Base64解碼
進行AES解密
一、CBC(Cipher Block Chaining,加密塊鏈)模式
是一種循環模式,前一個分組的密文和當前分組的明文異或操作後再加密,這樣做的目的是增強破解難度.
密鑰
密鑰偏移量
java/adroid加密AESOperator類:
package com.bci.wx.base.util;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
* AES 是一種可逆加密演算法,對用戶的敏感信息加密處理 對原始數據進行AES加密後,在進行Base64編碼轉化;
*/
public class AESOperator {
/*
* 加密用的Key 可以用26個字母和數字組成 此處使用AES-128-CBC加密模式,key需要為16位。
*/
private String sKey = "smkldospdosldaaa";//key,可自行修改
private String ivParameter = "0392039203920300";//偏移量,可自行修改
private static AESOperator instance = null;
private AESOperator() {
}
public static AESOperator getInstance() {
if (instance == null)
instance = new AESOperator();
return instance;
}
public static String Encrypt(String encData ,String secretKey,String vector) throws Exception {
if(secretKey == null) {
return null;
}
if(secretKey.length() != 16) {
return null;
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = secretKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(vector.getBytes());// 使用CBC模式,需要一個向量iv,可增加加密演算法的強度
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(encData.getBytes("utf-8"));
return new BASE64Encoder().encode(encrypted);// 此處使用BASE64做轉碼。
}
// 加密
public String encrypt(String sSrc) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = sKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());// 使用CBC模式,需要一個向量iv,可增加加密演算法的強度
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
return new BASE64Encoder().encode(encrypted);// 此處使用BASE64做轉碼。
}
// 解密
public String decrypt(String sSrc) throws Exception {
try {
byte[] raw = sKey.getBytes("ASCII");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "utf-8");
return originalString;
} catch (Exception ex) {
return null;
}
}
public String decrypt(String sSrc,String key,String ivs) throws Exception {
try {
byte[] raw = key.getBytes("ASCII");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivs.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "utf-8");
return originalString;
} catch (Exception ex) {
return null;
}
}
public static String encodeBytes(byte[] bytes) {
StringBuffer strBuf = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a')));
strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a')));
}
return strBuf.toString();
}
8. ios aes解密為空是怎麼回事
那應該是你加密的出錯了,你在看看你加密的方法和解密的方法是否一致
9. 通過Java如何實現AES密碼演算法
1. AES加密字元串
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");// 創建AES的Key生產者
kgen.init(128, new SecureRandom(password.getBytes()));// 利用用戶密碼作為隨機數初始化出
// 128位的key生產者
//加密沒關系,SecureRandom是生成安全隨機數序列,password.getBytes()是種子,只要種子相同,序列就一樣,所以解密只要有password就行
SecretKey secretKey = kgen.generateKey();// 根據用戶密碼,生成一個密鑰
byte[] enCodeFormat = secretKey.getEncoded();// 返回基本編碼格式的密鑰,如果此密鑰不支持編碼,則返回
// null。
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 轉換為AES專用密鑰
Cipher cipher = Cipher.getInstance("AES");// 創建密碼器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化為加密模式的密碼器
byte[] result = cipher.doFinal(byteContent);// 加密
return result;
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
2. AES解密
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");// 創建AES的Key生產者
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();// 根據用戶密碼,生成一個密鑰
byte[] enCodeFormat = secretKey.getEncoded();// 返回基本編碼格式的密鑰
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 轉換為AES專用密鑰
Cipher cipher = Cipher.getInstance("AES");// 創建密碼器
cipher.init(Cipher.DECRYPT_MODE, key);// 初始化為解密模式的密碼器
byte[] result = cipher.doFinal(content);
return result; // 明文
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
10. java rsa證書 ios 支持嗎
了之前三個終端進行AES加解密的經驗,實現RSA加解密倒不是太復雜。兩個關鍵點:1)RSA需要密鑰文件,且密鑰文件的格式很重要;2)padding方式需要一致,比較被廣泛支持且被很多RAS實現設置為默認padding方式的是PKCS1PADDING V1.5,建議採用。
我是在linux下用openssl生成公鑰、私鑰文件的,分三個步驟,命令如下:
1、生成公鑰public_key.der 和 私鑰private_key.pem(加密)
openssl req -x509 -out public_key.der -outform der -new -newkey rsa:1024 -keyout private_key.pem
按照提示,填入私鑰的密碼,簽名證書的組織名、郵件等信息之後,就會生成包含有公鑰的證書文件public_key.der和私鑰文件private_key.pem。public_key.der文件用於分發到ios客戶端進行公鑰加密。
2、生成公鑰public_key.pem。
openssl rsa -in private_key.pem -pubout -out public_key.pem ,這步生成的public_key.pem用於分發到c++客戶端和安卓客戶端進行公鑰加密。
3、將私鑰 private_key.pem(加密)轉換為PKCS#8編碼格式(且不加密)。
openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt
這步生成的pkcs8_private_key.pem用於在Java服務端進行私鑰解密。
RSA的一般用法是在客戶端用公鑰加密,在網路上傳輸密文,然後服務端用私鑰解密獲取原文。所以RSA實現都會支持公鑰加密、私鑰解密。反過來用私鑰加密然後公鑰解密,理論上也是可行的。不過我沒有試過。如果決定要這么做,祝君好運。
注意:RSA提供的API不像AES,不會自動分塊處理。需要手動將原文切割為128-11的塊去進行加密 (128是因為採用RSA1024,128=1024/8, 11是因為padding採用版本1.5的PKCS1PADDING。這種分塊方式是多個RSA實現的默認方式,推薦使用),每次加密輸出的密文長度均是128位元組(RSA1024),把這些密文順序拼裝起來。在解密的時候,則是將密文按128位元組進行切割,解密後再拼裝在一起即可。
======================C++客戶端公鑰加密=======================
使用大名鼎鼎openssl類庫實現,感慨下其API設計得真爛。。。。
注意:雖然輸入和輸出都是std::string,但不要理解成字元串,實際上都是二進制數據。另外公鑰得保存在文件中,我還沒找到使用內存中公鑰的方法,但對我已經夠用了。
std::string EvpHelper::rsaEncryptUsingPublicKeyFile(const std::string& source, const std::string& keyFile)
{
std::string result;
BIO* bp = NULL;
EVP_PKEY* key = NULL;
RSA* rsa = NULL;
EVP_PKEY_CTX* ctx = NULL;
unsigned char* encryptedData = NULL;
try
{
// load public key
OpenSSL_add_all_algorithms();
bp = BIO_new(BIO_s_file());
if (bp == NULL)
{
throw std::runtime_error("BIO_new failed.");
}
if (BIO_read_filename(bp, keyFile.c_str()) <= 0)
{
throw std::runtime_error("BIO_read_filename failed.");
}
rsa = PEM_read_bio_RSA_PUBKEY(bp, NULL, NULL, NULL);
if (rsa == NULL)
{
throw std::runtime_error("PEM_read_bio_RSA_PUBKEY failed.");
}
key = EVP_PKEY_new();
if (key == NULL)
{
throw std::runtime_error("EVP_PKEY_new failed.");
}
EVP_PKEY_assign_RSA(key, rsa);
rsa = NULL;
// encrypt
OpenSSL_add_all_ciphers();
ctx = EVP_PKEY_CTX_new(key, NULL);
if (ctx == NULL)
{
throw std::runtime_error("EVP_PKEY_CTX_new failed.");
}
if (EVP_PKEY_encrypt_init(ctx) <= 0)
{
throw std::runtime_error("EVP_PKEY_encrypt_init failed.");
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
{
throw std::runtime_error("EVP_PKEY_CTX_set_rsa_padding failed.");
}
encryptedData = new unsigned char[source.size() * 2];
size_t encryptedDataLen = 0;
size_t BLOCK_SIZE = 128 - 11;
size_t sourceDataLen = (int)source.size();
for (size_t i = 0; i < sourceDataLen; i += BLOCK_SIZE)
{
size_t leftBytes = sourceDataLen - i;
size_t sourceBlockLen = (leftBytes <= BLOCK_SIZE) ? leftBytes : BLOCK_SIZE;
size_t encryptedBlockLen = 128;
if(EVP_PKEY_encrypt(ctx, (encryptedData + encryptedDataLen), &encryptedBlockLen,
(const unsigned char *)(source.data() + i), sourceBlockLen) <= 0)
{
throw std::runtime_error("EVP_PKEY_encrypt failed.");
}
encryptedDataLen += encryptedBlockLen;
}
result = std::string((char*)encryptedData, encryptedDataLen);
}
catch (const std::exception& e)
{
LErr(e.what());
}
if (bp != NULL)
{
LInfoCMD(BIO_free(bp));
}
if(rsa != NULL)
{
LInfoCMD(RSA_free(rsa));
}
if (ctx != NULL)
{
LInfoCMD(EVP_PKEY_CTX_free(ctx));
}
if (key != NULL)
{
LInfoCMD(EVP_PKEY_free(key));
}
if (encryptedData != NULL)
{
delete[] encryptedData;
}
return result;
}
======================Java客戶端公鑰加密=======================
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
public class RSAEncrypt {
public static RSAPublicKey loadPublicKeyFromFile(String keyPath)
throws Exception {
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(
keyPath)));
String line = null;
while ((line = br.readLine()) != null) {
if (line.charAt(0) == '-') {
continue;
} else {
sb.append(line);
sb.append('\r');
}
}
} finally {
if (br != null) {
br.close();
}
}
return loadPublicKey(sb.toString());
}
public static RSAPublicKey loadPublicKey(String publicKeyStr)
throws Exception {
Base64 decoder = new Base64();
byte[] buffer = decoder.decode(publicKeyStr);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
public static void main(String[] args) throws Exception {
RSAPublicKey publicKey = loadPublicKeyFromFile("public_key.pem");
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] rawText = CommonUtil.readFileContents("from.txt");
FileOutputStream out = null;
try {
out = new FileOutputStream("java_out.bin");
int BLOCK_SIZE = 128 - 11;
for (int i = 0; i < rawText.length; i += BLOCK_SIZE) {
int leftBytes = rawText.length - i;
int length = (leftBytes <= BLOCK_SIZE) ? leftBytes : BLOCK_SIZE;
out.write(cipher.doFinal(rawText, i, length));
}
} finally {
if (out != null) {
out.close();
}
}
}
}
======================IOS客戶端公鑰加密=======================
static SecKeyRef _public_key = nil;
- (SecKeyRef) getPublicKey{
if (_public_key == nil){
NSString* filePath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"];
NSData* certificateData = [NSData dataWithContentsOfFile:filePath];
SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
if (myCertificate == nil) {
LErr(@"無法讀取公鑰內容");
return nil;
}
SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
SecTrustRef myTrust;
OSStatus status = (myCertificate,myPolicy,&myTrust);
SecTrustResultType trustResult;
if (status == noErr) {
status = SecTrustEvaluate(myTrust, &trustResult);
}else{
return nil;
}
_public_key = SecTrustCopyPublicKey(myTrust);
CFRelease(myCertificate);
CFRelease(myPolicy);
CFRelease(myTrust);
}
return _public_key;
}
- (NSData*) rsaEncrypt:(NSData*) data{
SecKeyRef key = [self getPublicKey];
if (key == nil) {
return nil;
}
size_t cipherBufferSize = SecKeyGetBlockSize(key);
uint8_t* cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
size_t blockSize = cipherBufferSize - 11;
size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
NSMutableData *encryptedData = [[NSMutableData alloc] init];
for (int i=0; i
int bufferSize = (int)MIN(blockSize,[data length] - i * blockSize);
NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)[buffer bytes],
[buffer length], cipherBuffer, &cipherBufferSize);
if (status == noErr){
NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
[encryptedData appendData:encryptedBytes];
}else{
if (cipherBuffer) free(cipherBuffer);
return nil;
}
}