按:前面(
http://m.tkk7.com/heyang/archive/2010/12/25/341518.html)已經提到過混合加密方式,今天這里再來贅述一下完成的代碼和流程,熟悉者就不用往下看了。完整的IM程序在(
http://www.box.net/shared/jcrbps2hk3)可以下載。
整個流程的UML Sequence圖(Amaterus UML用得還不熟練請見諒,會意就好)
下面是以上八個步驟中使用到的代碼和信息。
步驟一:
取得服務器端RSA公鑰的Socket通信代碼:
public static byte[] getPublicKey() throws Exception{
Socket s=new Socket("127.0.0.1",8888);
InputStream inStram=s.getInputStream();
OutputStream outStream=s.getOutputStream();
// 輸出
PrintWriter out=new PrintWriter(outStream,true);
out.print("getPublicKey");
out.flush();
s.shutdownOutput();// 輸出結束
// 輸入
Scanner in=new Scanner(inStram);
StringBuilder sb=new StringBuilder();
while(in.hasNextLine()){
String line=in.nextLine();
sb.append(line);
}
String response=sb.toString();
byte[] arr=Base64.decodeBase64(response);
s.close();
return arr;
}
步驟二:
客戶端加密過程代碼:
// 待加密的明文
StringBuilder sb1=new StringBuilder();
sb1.append("<request>");
sb1.append("<command>register</command>");
sb1.append("<username>趙云</username>");
sb1.append("<password>123456</password>");
sb1.append("</request>");
String plainText=sb1.toString();
// 對明文進行AES加密
byte[] aesArr=aesCoder.getEncryptByteArray(plainText); // 對明文進行AES加密
String cipherText=Base64.encodeBase64String(aesArr);// 得到AES加密后的密文
// 使用RSA對AES密鑰進行加密
String key=aesCoder.getAesKey();// 取得AES的密鑰
byte[] rsaArr=rsaCoder.getEncryptArray(key, serverPublicKey);
String encryptedKey=Base64.encodeBase64String(rsaArr);
步驟三:
將密文,AES密鑰,本地RSA公鑰送到服務器端的代碼(粗體部分):
Socket s=new Socket("127.0.0.1",8888);
InputStream inStram=s.getInputStream();
OutputStream outStream=s.getOutputStream();
// 輸出
PrintWriter out=new PrintWriter(outStream,true);
// 待加密的明文
StringBuilder sb1=new StringBuilder();
sb1.append("<request>");
sb1.append("<command>register</command>");
sb1.append("<username>趙云</username>");
sb1.append("<password>123456</password>");
sb1.append("</request>");
String plainText=sb1.toString();
// 對明文進行AES加密
byte[] aesArr=aesCoder.getEncryptByteArray(plainText); // 對明文進行AES加密
String cipherText=Base64.encodeBase64String(aesArr);// 得到AES加密后的密文
// 使用RSA對AES密鑰進行加密
String key=aesCoder.getAesKey();// 取得AES的密鑰
byte[] rsaArr=rsaCoder.getEncryptArray(key, serverPublicKey);
String encryptedKey=Base64.encodeBase64String(rsaArr);
// 在發出的密文前附帶經服務器RSA公鑰加密的AES密鑰
StringBuilder sb3=new StringBuilder();
sb3.append("<aeskey>"+encryptedKey+"</aeskey>");
sb3.append("<rsakey>"+rsaCoder.getPublicKeyString()+"</rsakey>");
sb3.append("<text>"+cipherText+"</text>");
// 請求送出前用Base64加密
String request=Base64SecurityUtil.getEncryptString(sb3.toString());
out.print(request);
out.flush();
s.shutdownOutput();// 輸出結束
步驟四:
服務器端解密過程代碼(變量request中就是客戶端送來的請求文):
// 得到請求后先用Base64解密
request=Base64SecurityUtil.getDecryptString(request);
// 用正則表達式得到密鑰文,客戶端的RSA公鑰和密文
String regex="<(\\w+)>((.|\\s)+)</\\1>";
Pattern pattern=Pattern.compile(regex);
Matcher matcher=pattern.matcher(request);
String cipheredAesKey="";// 經服務器RSA公鑰加密的客戶端AES鑰匙密文
String clientRsaKey="";// 客戶端的RSA公鑰
String cipherText="";// 經客戶端AES加密的密文
Map<String,String> map=new HashMap<String,String>();
while(matcher.find()){
map.put(matcher.group(1), matcher.group(2));
}
if(map.size()==3){
cipheredAesKey=map.get("aeskey");
clientRsaKey=map.get("rsakey");
cipherText=map.get("text");
}
else{
return "無法用正則表達式解析服務器端請求";
}
// 得到經過服務器RSA私鑰解密后的AES密鑰
String plainAesKey="";
try {
byte[] cipheredAesKeyArr=Base64.decodeBase64(cipheredAesKey);
plainAesKey=model.getRsaCoder().getDecryptString(cipheredAesKeyArr);
} catch (Exception e) {
e.printStackTrace();
return null;
}
// 使用AES密鑰解密出明文
byte[] cipherTextArr=Base64.decodeBase64(cipherText);
String plainText=model.getAesCoder().getDecryptString(cipherTextArr, plainAesKey);
步驟五:
這里的主要操作是根據看用戶名是否在用戶列表中存在,是則創建用戶,否則告知名稱重復。這段代碼很簡單常見,故省略之。
步驟六:
服務器端加密過程代碼:
// 對明文進行AES加密
byte[] aesArr=model.getAesCoder().getEncryptByteArray(retval); // 對明文進行AES加密
String cipherRetval=Base64.encodeBase64String(aesArr);// 得到AES加密后的密文
// 使用RSA對AES密鑰進行加密
String key=model.getAesCoder().getAesKey();// 取得AES的密鑰
String aesKey="";
try{
byte[] clientRsaKeyArr=null;
clientRsaKeyArr=Base64.decodeBase64(clientRsaKey);
byte[] rsaArr=model.getRsaCoder().getEncryptArray(key, clientRsaKeyArr);
aesKey=Base64.encodeBase64String(rsaArr);
}
catch(Exception ex){
ex.printStackTrace();
}
// 在發出的密文前附帶經服務器RSA公鑰加密的AES密鑰
StringBuilder sb3=new StringBuilder();
sb3.append("<aeskey>"+aesKey+"</aeskey>");
sb3.append(cipherRetval);
步驟七:
將響應發還給客戶端的代碼(粗體部分):
InputStream inStram=incoming.getInputStream();
OutputStream outStream=incoming.getOutputStream();
Scanner in=new Scanner(inStram);
PrintWriter out=new PrintWriter(outStream,true);
// 得到客戶端的請求
StringBuilder sb=new StringBuilder();
while(in.hasNextLine()){
String line=in.nextLine();
sb.append(line);
}
String request=sb.toString();
String response="";
if("getPublicKey".equals(request)){
// 求服務器公鑰
response=model.getPublicKey();
}
else{
response=getResponse(request);
}
// 向客戶端送出反饋
out.print(response);
out.flush();
out.close();
步驟八:
客戶端解密服務器端響應的過程:
String cipheredAesKey="";// 經服務器RSA公鑰加密的客戶端AES鑰匙密文
String cipheredResponse="";// 經客戶端AES加密的密文
// 用正則表達式得到密鑰文,客戶端的RSA公鑰和密文
String regex="<aeskey>(.+)</aeskey>(.+)";
Pattern pattern=Pattern.compile(regex);
Matcher matcher=pattern.matcher(response);
while(matcher.find()){
cipheredAesKey=matcher.group(1);
cipheredResponse=matcher.group(2);
break;
}
// 得到經過服務器RSA私鑰解密后的AES密鑰
String plainAesKey="";
try {
byte[] cipheredAesKeyArr=Base64.decodeBase64(cipheredAesKey);
plainAesKey=rsaCoder.getDecryptString(cipheredAesKeyArr);
} catch (Exception e) {
e.printStackTrace();
}
// 使用AES密鑰解密出明文
byte[] cipheredResponseArr=Base64.decodeBase64(cipheredResponse);
String plainResponse=aesCoder.getDecryptString(cipheredResponseArr, plainAesKey);
System.out.println(plainResponse);
好了,整個過程的代碼都貼出來了,感謝您花費寶貴時間看到這里。另外三個加密解密類的代碼如下:
AESSecurityCoder類:
package com.heyang.common.code;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
/**
* AES加密解密類
* 說明:
* 作者:何楊(heyang78@gmail.com)
* 創建時間:2010-12-25 下午12:19:12
* 修改時間:2010-12-25 下午12:19:12
*/
public class AESSecurityCoder{
// 加密方法
private static final String Algorithm="AES";
// 進行加密解密的密鑰
private String aesKey="";
/**
* 構造函數
* @throws NoSuchAlgorithmException
*/
public AESSecurityCoder() throws NoSuchAlgorithmException{
KeyGenerator kg=KeyGenerator.getInstance(Algorithm);
kg.init(256);
SecretKey sk=kg.generateKey();
byte[] arr=sk.getEncoded();
aesKey=new String(Hex.encodeHex(arr));
}
/**
* 取得解密后的字符串
*
* 說明:
* @param encryptArr
* @return
* 創建時間:2010-12-1 下午03:33:31
*/
public String getDecryptString(byte[] encryptArr){
try{
Cipher cp=Cipher.getInstance(Algorithm);
cp.init(Cipher.DECRYPT_MODE, getKey());
byte[] arr=cp.doFinal(encryptArr);
return new String(arr);
}
catch(Exception ex){
System.out.println("無法進行解密,原因是"+ex.getMessage());
return null;
}
}
/**
* 傳入密鑰,得到解密后的字符串
*
* 說明:
* @param encryptArr
* @param aesKey
* @return
* 創建時間:2010-12-25 下午01:55:42
*/
public String getDecryptString(byte[] encryptArr,String aesKeyIn){
try{
Cipher cp=Cipher.getInstance(Algorithm);
byte[] arr1=Hex.decodeHex(aesKeyIn.toCharArray());
cp.init(Cipher.DECRYPT_MODE, new SecretKeySpec(arr1,Algorithm));
byte[] arr=cp.doFinal(encryptArr);
return new String(arr);
}
catch(Exception ex){
System.out.println("無法進行解密,原因是"+ex.getMessage());
return null;
}
}
/**
* 取得加密后的字節數組
*
* 說明:
* @param originalString
* @return
* 創建時間:2010-12-1 下午03:33:49
*/
public byte[] getEncryptByteArray(String originalString){
try{
Cipher cp=Cipher.getInstance(Algorithm);
cp.init(Cipher.ENCRYPT_MODE, getKey());
return cp.doFinal(originalString.getBytes());
}
catch(Exception ex){
System.out.println("無法進行加密,原因是"+ex.getMessage());
return null;
}
}
/**
* 取得密鑰
*
* 說明:
* @return
* @throws Exception
* 創建時間:2010-12-1 下午03:33:17
*/
private Key getKey() throws Exception{
byte[] arr=Hex.decodeHex(aesKey.toCharArray());
return new SecretKeySpec(arr,Algorithm);
}
/**
* 取得AES加密鑰匙
*
* 說明:
* @return
* 創建時間:2010-12-25 下午12:27:16
*/
public String getAesKey() {
return aesKey;
}
}
RSASecurityCoder類:
package com.heyang.common.code;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
/**
* RSA加密解密類
* 說明:
* 作者:何楊(heyang78@gmail.com)
* 創建時間:2010-12-1 下午06:14:38
* 修改時間:2010-12-1 下午06:14:38
*/
public class RSASecurityCoder{
// 非對稱加密密鑰算法
private static final String Algorithm="RSA";
// 密鑰長度,用來初始化
private static final int Key_Size=1024;
// 公鑰
private byte[] publicKey;
// 私鑰
private byte[] privateKey;
/**
* 構造函數,在其中生成公鑰和私鑰
* @throws Exception
*/
public RSASecurityCoder() throws Exception{
// 得到密鑰對生成器
KeyPairGenerator kpg=KeyPairGenerator.getInstance(Algorithm);
kpg.initialize(Key_Size);
// 得到密鑰對
KeyPair kp=kpg.generateKeyPair();
// 得到公鑰
RSAPublicKey keyPublic=(RSAPublicKey)kp.getPublic();
publicKey=keyPublic.getEncoded();
// 得到私鑰
RSAPrivateKey keyPrivate=(RSAPrivateKey)kp.getPrivate();
privateKey=keyPrivate.getEncoded();
}
/**
* 用公鑰對字符串進行加密
*
* 說明:
* @param originalString
* @param publicKeyArray
* @return
* @throws Exception
* 創建時間:2010-12-1 下午06:29:51
*/
public byte[] getEncryptArray(String originalString,byte[] publicKeyArray) throws Exception{
// 得到公鑰
X509EncodedKeySpec keySpec=new X509EncodedKeySpec(publicKeyArray);
KeyFactory kf=KeyFactory.getInstance(Algorithm);
PublicKey keyPublic=kf.generatePublic(keySpec);
// 加密數據
Cipher cp=Cipher.getInstance(Algorithm);
cp.init(Cipher.ENCRYPT_MODE, keyPublic);
return cp.doFinal(originalString.getBytes());
}
/**
* 使用私鑰進行解密
*
* 說明:
* @param encryptedDataArray
* @return
* @throws Exception
* 創建時間:2010-12-1 下午06:35:28
*/
public String getDecryptString(byte[] encryptedDataArray) throws Exception{
// 得到私鑰
PKCS8EncodedKeySpec keySpec=new PKCS8EncodedKeySpec(privateKey);
KeyFactory kf=KeyFactory.getInstance(Algorithm);
PrivateKey keyPrivate=kf.generatePrivate(keySpec);
// 解密數據
Cipher cp=Cipher.getInstance(Algorithm);
cp.init(Cipher.DECRYPT_MODE, keyPrivate);
byte[] arr=cp.doFinal(encryptedDataArray);
// 得到解密后的字符串
return new String(arr);
}
/**
* 取得數組形式的公鑰
*
* 說明:
* @return
* 創建時間:2010-12-25 上午07:50:04
*/
public byte[] getPublicKey() {
return publicKey;
}
/**
* 取得字符串形式的公鑰
*
* 說明:
* @return
* 創建時間:2010-12-25 上午07:51:11
*/
public String getPublicKeyString() {
return Base64.encodeBase64String(getPublicKey());
}
public static void main(String[] arr) throws Exception{
String str="你好,世界! Hello,world!";
System.out.println("準備用公鑰加密的字符串為:"+str);
// 用公鑰加密
RSASecurityCoder rsaCoder=new RSASecurityCoder();
byte[] publicKey=rsaCoder.getPublicKey();
byte[] encryptArray=rsaCoder.getEncryptArray(str, publicKey);
System.out.print("用公鑰加密后的結果為:");
for(byte b:encryptArray){
System.out.print(b);
}
System.out.println();
// 用私鑰解密
String str1=rsaCoder.getDecryptString(encryptArray);
System.out.println("用私鑰解密后的字符串為:"+str1);
}
}
Base64SecurityUtil類:
package com.heyang.common.code;
import org.apache.commons.codec.binary.Base64;
/**
* 常規Base64加密解密實用工具類
* 說明:
* 作者:何楊(heyang78@gmail.com)
* 創建時間:2010-11-29 上午07:52:01
* 修改時間:2010-11-29 上午07:52:01
*/
public class Base64SecurityUtil{
/**
* 得到Base64加密后的字符串
*
* 說明:
* @param originalString
* @return
* 創建時間:2010-11-29 上午07:53:30
*/
public static String getEncryptString(String originalString){
byte[] arr = Base64.encodeBase64(originalString.getBytes(), true);
return new String(arr);
}
/**
* 得到Base64解密后的字符串
*
* 說明:
* @param encryptString
* @return
* 創建時間:2010-11-29 上午07:56:02
*/
public static String getDecryptString(String encryptString){
byte[] arr = Base64.decodeBase64(encryptString.getBytes());
return new String(arr);
}
/**
* 測試
*
* 說明:
* @param args
* 創建時間:2010-11-29 上午07:56:39
*/
public static void main(String[] args){
String str="Hello world!你好,世界。";
String str1=Base64SecurityUtil.getEncryptString(str);
System.out.println("經Base64加密后的密文為"+str1);
String str2=Base64SecurityUtil.getDecryptString(str1);
System.out.println("經Base64解密后的原文為"+str2);
}
}