不可逆雙鑰加密通信-張人傑版

在現代通信中往往採用https方式來實現加密通信。https通信通道的建立需要首先交換服務器與客戶端的公鑰,而誰來確保這個交換過程不被替換呢?對了那就是內置在瀏覽器中的根證書以及服務器端從該證書頒發機構獲取保存的服務器端證書。當然證書頒發機構如果攔截了你們之間的通信數據,是完全可以在中間知道你們的公鑰的並且模擬發送數據的,但其他人不能,因爲其他人沒有解密你們的公鑰的密鑰。那怎樣才能確保絕對的安全呢?其實不難,使用內置的公鑰和私鑰。當然這個方案僅限於服務器間的通信或者U盾通信。因爲如果把私鑰放到通用的客戶端程序裏,破解客戶端拿到私鑰,服務器向客戶端發送的信息就全都能夠解密了,當然如果U盾裏的密鑰(公鑰和私鑰)被人拷貝走了,那拿到密鑰的人如果獲取了你的通信數據也就能知道你收到的是什麼信息也能模擬你發送請求數據了。當然,服務器間的加密通信可以直接使用對方的RSA公鑰加密發送自身數據,使用自身的RSA私鑰解密對方發給自己的數據,來避免公鑰交換過程中的風險。但RSA的算法是兩個大素數的算法,一次加密只能加密245個byte,爲此,提出了張氏加解密過程來解決通信數據量膨脹的問題並確保數據的安全性。本工具包可應用於任何場景的服務器間通信,爲服務器間加密通信提供了一種安全可靠的方法:

package com.zhrenjie04.alex.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
/**
 * RSA雙鑰加解密工具
 * 本工具包經過加密前241個byte後的密文循環加密後續數據,具有密文長度與Base64密文長度接近,沒有私鑰無法解密、無法破解密文的特性。
 * 本工具包RSA的jdk調用部分來源於網上,雙鑰加解密過程爲本人首創,轉載、使用本代碼請註明作者信息
 * @author 張人傑
 *
 */
public class RSAUtil {
	public static final String KEY_ALGORITHM = "RSA";
	/** 貌似默認是RSA/NONE/PKCS1Padding,未驗證 */
	public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
	public static final String PUBLIC_KEY = "publicKey";
	public static final String PRIVATE_KEY = "privateKey";

	/** RSA密鑰長度必須是64的倍數,在512~65536之間。默認是1024 */
	public static final int KEY_SIZE = 2048;

	public static final String PLAIN_TEXT = "Up Task is the greatest comminicating tool in the world";

	private static PublicKey publicKey;
	private static PrivateKey privateKey;
	/**
	 * 通過base64的公鑰數據設置公鑰,加密數據必須設置公鑰,可以不設置私鑰
	 * @param publicKeyBase64
	 */
	public static void restorePublicKey(String publicKeyBase64) {
		restorePublicKey(Base64Util.decode(publicKeyBase64));
	}
	/**
	 * 通過base64的私鑰數據設置私鑰,解密數據必須設置私鑰,可以不設置公鑰
	 * @param privateKeyBase64
	 */
	public static void restorePrivateKey(String privateKeyBase64) {
		restorePrivateKey(Base64Util.decode(privateKeyBase64));
	}
	/**
	 * 用公鑰加密數據,加密數據必須設置公鑰,可以不設置私鑰,循環加密法加密
	 * @param s 數據內容
	 * @return
	 */
	public static String encode(String s,String charSetName) {
		byte[] bytes=null;
		try {
			bytes=s.getBytes(charSetName);
		} catch (UnsupportedEncodingException e1) {
			throw new RuntimeException(e1);
		}
		if(bytes.length<244) {
			return Base64Util.encode(RSAEncode(publicKey, bytes));
		}else {
			byte r0=(byte)(Math.random()*256);//4個隨機數,防止知道密文和明文來模擬數據(作者:張人傑)
			byte r1=(byte)(Math.random()*256);
			byte r2=(byte)(Math.random()*256);
			byte r3=(byte)(Math.random()*256);
			byte[] key=new byte[245];
			key[0]=r0;
			key[1]=r1;
			key[2]=r2;
			key[3]=r3;
			for(int i=0;i<241;i++) {
				key[i+4]=bytes[i];
			}
			byte[] keyBytes=RSAEncode(publicKey, key);
			System.out.println(keyBytes.length);
			//keyBytes作爲循環加密密鑰,前245個byte再做一次加密後存入
			byte[] keyBytes245=new byte[245];
			for(int i=0;i<245;i++) {
				keyBytes245[i]=keyBytes[i];
			}
			byte[] keyKeyBytes=RSAEncode(publicKey,keyBytes245);//前245個byte再做一次加密後存入
			ByteArrayOutputStream baos=new ByteArrayOutputStream();
			try {
				baos.write(keyKeyBytes);
				for(int i=245;i<keyBytes.length;i++) {//寫入keyBytes加密後的剩餘部分
					baos.write(keyBytes[i]);
				}
				int j=0;
				for(int i=241;i<bytes.length;i++) {//keyBytes作爲循環加密密鑰
					byte b=bytes[i];
					byte a=keyBytes[j%keyBytes.length];
					byte c=(byte)(a^b);
					baos.write(c);
					++j;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			return Base64Util.encode(baos.toByteArray());
		}
	}
	/**
	 * 用私鑰解密數據,解密數據必須設置私鑰,可以不設置公鑰
	 * @param s 密文內容
	 * @return
	 */
	public static String decode(String s,String charSetName) {
		byte[] encodedText=Base64Util.decode(s);
		if(encodedText.length<=256) {
			try {
				return new String(RSADecode(privateKey, encodedText),charSetName);
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e);
			}
		}else {
			byte[] encodedKeyKey=new byte[256];
			for(int i=0;i<256;i++) {
				encodedKeyKey[i]=encodedText[i];
			}
			byte[] encodedKey=RSADecode(privateKey, encodedKeyKey);//245key的密文
			byte[] fullEncodedKey=new byte[256];
			for(int i=0;i<245;i++) {
				fullEncodedKey[i]=encodedKey[i];
			}
			for(int i=0;i<256-245;i++) {//拼接key的密文
				fullEncodedKey[i+245]=encodedText[i+256];
			}
			byte[] key=RSADecode(privateKey, fullEncodedKey);//解密key
			ByteArrayOutputStream baos=new ByteArrayOutputStream();
			for(int i=4;i<key.length;i++) {
				baos.write(key[i]);
			}
			int j=0;
			for(int i=256+256-245;i<encodedText.length;i++) {//用key的密文解密剩餘部分
				byte b=encodedText[i];
				byte a=fullEncodedKey[j%fullEncodedKey.length];
				byte c=(byte)(a^b);
				baos.write(c);
				++j;
			}
			try {
				return new String(baos.toByteArray(),charSetName);
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e);
			}
		}
	}
	/**
	 * 生成公鑰、私鑰部分及測試加密、解密過程
	 * @param args
	 */
	public static void main(String[] args) {
		long t=System.currentTimeMillis();
		Map<String, byte[]> keyMap = generateKeyBytes();
		System.out.println("generateKeyBytes time:"+(System.currentTimeMillis()-t));
		t=System.currentTimeMillis();
		// 加密
		PublicKey publicKey = restorePublicKey(keyMap.get(PUBLIC_KEY));
		byte[] encodedText = RSAEncode(publicKey, PLAIN_TEXT.getBytes());
		System.out.println("restorePublicKey time:"+(System.currentTimeMillis()-t));
		System.out.println("RSA encoded: " + Base64Util.encode(encodedText));
		// 解密
		t=System.currentTimeMillis();
		PrivateKey privateKey = restorePrivateKey(keyMap.get(PRIVATE_KEY));
		System.out.println("restorePrivateKey time:"+(System.currentTimeMillis()-t));
		System.out.println("RSA decoded: " + new String(RSADecode(privateKey, encodedText)));
		String s="你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli你好啊afjladfowureowrajgldjfwifjiwaoeifjawli啊啊啊哈哈哈哈哈哈";
		t=System.currentTimeMillis();
		String encodedString=RSAUtil.encode(s, "UTF-8");
		System.out.println("RSAUtil.encode time:::"+(System.currentTimeMillis()-t));
		System.out.println("encoded:::::"+encodedString);
		System.out.println("base64:::::"+Base64Util.encode(s.getBytes()));
		t=System.currentTimeMillis();
		System.out.println("decoded:::::"+RSAUtil.decode(encodedString, "UTF-8"));
		System.out.println("RSAUtil.decode time:::"+(System.currentTimeMillis()-t));
	}

	/**
	 * 生成密鑰對。注意這裏是生成密鑰對KeyPair,再由密鑰對獲取公私鑰
	 * 
	 * @return
	 */
	public static Map<String, byte[]> generateKeyBytes() {
		try {
			KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
			keyPairGenerator.initialize(KEY_SIZE, SecureRandom.getInstanceStrong());
			KeyPair keyPair = keyPairGenerator.generateKeyPair();
			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
			Map<String, byte[]> keyMap = new HashMap<String, byte[]>();
			keyMap.put(PUBLIC_KEY, publicKey.getEncoded());
			keyMap.put(PRIVATE_KEY, privateKey.getEncoded());
			System.out.println("public key: "+Base64Util.encode(publicKey.getEncoded()));
			System.out.println("private key: "+Base64Util.encode(privateKey.getEncoded()));
			return keyMap;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 還原公鑰,X509EncodedKeySpec 用於構建公鑰的規範
	 * 
	 * @param keyBytes
	 * @return
	 */
	private static PublicKey restorePublicKey(byte[] keyBytes) {
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
		try {
			KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
			PublicKey publicKey = factory.generatePublic(x509EncodedKeySpec);
			RSAUtil.publicKey=publicKey;
			return publicKey;
		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 還原私鑰,PKCS8EncodedKeySpec 用於構建私鑰的規範
	 * 
	 * @param keyBytes
	 * @return
	 */
	private static PrivateKey restorePrivateKey(byte[] keyBytes) {
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
		try {
			KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
			PrivateKey privateKey = factory.generatePrivate(pkcs8EncodedKeySpec);
			RSAUtil.privateKey=privateKey;
			return privateKey;
		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 加密,三步走。
	 * 
	 * @param key
	 * @param plainText
	 * @return
	 */
	private static byte[] RSAEncode(PublicKey key, byte[] plainText) {
		try {
			Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
			cipher.init(Cipher.ENCRYPT_MODE, key);
			return cipher.doFinal(plainText);
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
				| BadPaddingException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 解密,三步走。
	 * 
	 * @param key
	 * @param encodedText
	 * @return
	 */
	private static byte[] RSADecode(PrivateKey key, byte[] encodedText) {
		try {
			Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
			cipher.init(Cipher.DECRYPT_MODE, key);
			return cipher.doFinal(encodedText);
		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
				| BadPaddingException e) {
			e.printStackTrace();
		}
		return null;
	}
}





 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章