1. 程式人生 > >[JAVA加解密]RSA演算法、ElGamal演算法

[JAVA加解密]RSA演算法、ElGamal演算法

一、RSA演算法:

1、簡介:RSA公鑰加密演算法是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。RSA是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰資料加密標準。今天只有短的RSA鑰匙才可能被強力方式解破。到2008年為止,世界上還沒有任何可靠的攻擊RSA演算法的方式。只要其鑰匙的長度足夠長,用RSA加密的資訊實際上是不能被解破的。但在分散式計算和量子計算機理論日趨成熟的今天,RSA加密安全性受到了挑戰。
RSA演算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但是想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密金鑰

RSA也是目前唯一一個基於因式分解難題的演算法。

2、演算法描述:RSA的演算法涉及三個引數,n、e1、e2。
其中,n是兩個大質數p、q的積,n的二進位制表示時所佔用的位數,就是所謂的金鑰長度。
e1和e2是一對相關的值,e1可以任意取,但要求e1與(p-1)*(q-1)互質;再選擇e2,要求(e2*e1)mod((p-1)*(q-1))=1。
(n,e1),(n,e2)就是金鑰對。其中(n,e1)為公鑰,(n,e2)為私鑰。[1] 
RSA加解密的演算法完全相同,設A為明文,B為密文,則:A=B^e2 mod n;B=A^e1 mod n;(公鑰加密體制中,一般用公鑰加密,私鑰解密)
e1和e2可以互換使用,即:
A=B^e1 mod n;B=A^e2 mod n;

3、演算法實現:

RSA演算法實現較簡單,沒有什麼需要特別注意的地方,以下程式碼僅進行了私鑰加密,公鑰解密,可用作數字簽名使用;

public abstract class RSACoder {
	private static final String KEY_ALGORITHM= "RSA";
	private static final int KEY_SIZE=512;
	private static final String PUBLIC_KEY = "RSAPublicKey";
	private static final String PRIVATE_KEY = "RSAPrivateKey";
	
	public static Map<String,Object> initKey() throws NoSuchAlgorithmException{
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(KEY_SIZE);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		//生成金鑰當然要生成最豐富的啦
		RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
		
		Map<String,Object> keyMap = new HashMap<String,Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
		
	}
	
	public static byte[] encryptByPrivateKey(byte[] data,byte[] priKey) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
		PKCS8EncodedKeySpec x509EncodedKeySpec = new PKCS8EncodedKeySpec(priKey);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateKey = keyFactory.generatePrivate(x509EncodedKeySpec);
		
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		return cipher.doFinal(data);
		
	}
	
	public static byte[] decryptByPublicKey(byte[] data,byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
		X509EncodedKeySpec pkcs8EncodedKeySpec = new X509EncodedKeySpec(pubKey);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PublicKey publicKey = keyFactory.generatePublic(pkcs8EncodedKeySpec);
		
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		return cipher.doFinal(data);
	}
	
	public static byte[] getPrivateKey(Map<String,Object> keyMap){
		RSAPrivateKey privateKey = (RSAPrivateKey) keyMap.get(PRIVATE_KEY);
		return privateKey.getEncoded();
	}
	
	public static byte[] getPublicKey(Map<String,Object> keyMap){
		RSAPublicKey publicKey = (RSAPublicKey)keyMap.get(PUBLIC_KEY);
		return publicKey.getEncoded();
	}
}
測試用例:
public class RSACoderTest {
	private byte[] privateKey;
	private byte[] publicKey;
	
	@Before
	public void initKey() throws NoSuchAlgorithmException{
		Map<String,Object> keyMap = RSACoder.initKey();
		publicKey = RSACoder.getPublicKey(keyMap);
		privateKey = RSACoder.getPrivateKey(keyMap);
		System.err.println("公鑰:\n"+Base64.encodeBase64String(publicKey));
		System.err.println("私鑰:\n"+Base64.encodeBase64String(privateKey));
		
	}
	
	@Test
	public void test() throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
		System.err.println("\n--私鑰加密,公鑰解密--");
		String input1 = "RSA加密演算法";
		System.err.println("原文:\n"+input1);
		byte[] data1 = input1.getBytes();
		byte[] encodedata1 = RSACoder.encryptByPrivateKey(data1, privateKey);
		System.err.println("加密後:\n"+Base64.encodeBase64String(encodedata1));
		byte[] decodedata1 = RSACoder.decryptByPublicKey(encodedata1, publicKey);
		System.err.println("解密後:\n"+Base64.encodeBase64String(decodedata1));
		String output1 = new String(decodedata1);
		System.out.println("結果:\n"+output1);
		assertEquals(input1,output1);
	}

}
輸出:

公鑰:
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIVn3mmbLJ35bRW51IM6voFzBcpIhhts85OlQJDTSdisfaEt9BRHOoTy2ND31oirr2gA/fGEEVWZvFgVyoz/8Y8CAwEAAQ==
私鑰:
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAhWfeaZssnfltFbnUgzq+gXMFykiGG2zzk6VAkNNJ2Kx9oS30FEc6hPLY0PfWiKuvaAD98YQRVZm8WBXKjP/xjwIDAQABAkARdQYly6iLA5jCvw6QHZ/QULzxO4xRCnDVYUqRqRSAxeEtUGv7rTdzvmD4e7sNnnmgdzJA4Tw6WFqI32e1UtgBAiEAxn8AWQuJ1LjC/Uvk90bh1J9rvLZAn22SV9z0TS3I45kCIQCsDZfV5Zl7vTT4cwXe+lcC7z6504R8i7aHyaPcRoq3ZwIgCYQxOhOpif9JqecnlQta7FywR53dP0d7iqbXN5QIW5ECIQCmWdo1aHH2ruC5W1UQ21EnuDQYrYcKeHc6YN56ywWx/wIgRzHu0AmIjaG94ihMBWvAywO/xetdY9tSQZpSHFSKLnU=


--私鑰加密,公鑰解密--
原文:
RSA加密演算法
加密後:
BltYXEfVGzfd78ejw70kIQ3eapFzZmb69i4LYdU5gopS4FtDNnshhkMiNTkzO8MVLKyHtsp6XCg+8fZ0GHcSUw==
解密後:
UlNBvNPD3Mvjt6g=
結果:
RSA加密演算法


小結:

1、RSA演算法公鑰遠遠小於私鑰;

2、Cipher.init()中引數第二為Key型別即可,還原Key型別即可;

3、getPrivateKey及getPublicKey都是通過傳入Map引數取得結果的;

4、PKCS8EncodedKeySpec是還原私鑰的,儘管PKCS是這個The Public-Key Cryptography Standards

二、ElGamal密碼體系:

本部分注重一個問題:ElGamal金鑰的生成

JAVA7不支援。引入BouncyCastle

 下面是教程提供的基於DHParameterSpec演算法引數材料建立金鑰

public static Map<String,Object> initKey() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException{
		Security.addProvider(new BouncyCastleProvider());
		AlgorithmParameterGenerator apg =AlgorithmParameterGenerator.getInstance(KEY_ALGORITHM);
		apg.init(KEY_SIZE);
		AlgorithmParameters params = apg.generateParameters();
		DHParameterSpec dhParams = params.getParameterSpec(DHParameterSpec.class);
		
		KeyPairGenerator keyPairGene = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGene.initialize(dhParams, new SecureRandom());
		
		KeyPair keyPair = keyPairGene.generateKeyPair();
		PublicKey publicKey = keyPair.getPublic();
		PrivateKey privateKey = keyPair.getPrivate();
		
		Map<String,Object> keyMap = new HashMap<String,Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}

經過試驗,按照往常的套路,這樣的程式碼也是可行的:

	public static Map<String,Object> initKey() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException{
		Security.addProvider(new BouncyCastleProvider());
		KeyPairGenerator keyPairGene = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGene.initialize(KEY_SIZE, new SecureRandom());
		KeyPair keyPair = keyPairGene.generateKeyPair();
		PublicKey publicKey = keyPair.getPublic();
		PrivateKey privateKey = keyPair.getPrivate();
		
		Map<String,Object> keyMap = new HashMap<String,Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}

而兩種方法的關鍵都在於KeyPairGenerator類的init()函式:

Class KeyPairGenerator

void Initializes the key pair generator using the specified parameter set and the SecureRandomimplementation of the highest-priority installed provider as the source of randomness.
void Initializes the key pair generator with the given parameter set and source of randomness.
void Initializes the key pair generator for a certain keysize using a default parameter set and theSecureRandom implementation of the highest-priority installed provider as the source of randomness.
void Initializes the key pair generator for a certain keysize with the given source of randomness (and a default parameter set).

Interface AlgorithmParameterSpec

public class AlgorithmParameters
extends Object

兩者沒有半毛錢關係

再需要注意的是ElGamal需要隨機數的,詳細可參照加密原理,傳輸金鑰對有乙方選擇的隨機數k

最後,實現程式碼如下:

public abstract class ElGamalCoder {
	public static final String KEY_ALGORITHM="ElGamal";
	private static final int KEY_SIZE=256;
	private static final String PUBLIC_KEY="ElGamalPublicKey";
	private static final String PRIVATE_KEY = "ElGamalPrivateKey";
	
	public static byte[] decryptByPrivateKey(byte[] data,byte[]key) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
		Security.addProvider(new BouncyCastleProvider());
		PKCS8EncodedKeySpec pkc = new PKCS8EncodedKeySpec(key);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateKey = keyFactory.generatePrivate(pkc);
		
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		return cipher.doFinal(data);
		
	}
	
	public static byte[] encryptByPublicKey(byte[] data,byte[]key) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
		Security.addProvider(new BouncyCastleProvider());
		X509EncodedKeySpec x509 = new X509EncodedKeySpec(key);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicKey = keyFactory.generatePublic(x509);
		
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		return cipher.doFinal(data);
		
	}
	
	public static Map<String,Object> initKey() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException{
		Security.addProvider(new BouncyCastleProvider());
		AlgorithmParameterGenerator apg =AlgorithmParameterGenerator.getInstance(KEY_ALGORITHM);
		apg.init(KEY_SIZE);
		AlgorithmParameters params = apg.generateParameters();
		DHParameterSpec dhParams = params.getParameterSpec(DHParameterSpec.class);
		
		KeyPairGenerator keyPairGene = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGene.initialize(dhParams, new SecureRandom());
	
//		keyPairGene.initialize(KEY_SIZE, new SecureRandom());
		KeyPair keyPair = keyPairGene.generateKeyPair();
		PublicKey publicKey = keyPair.getPublic();
		PrivateKey privateKey = keyPair.getPrivate();
		
		Map<String,Object> keyMap = new HashMap<String,Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}
	
	public static byte[] getPrivateKey(Map<String,Object> keyMap){
		Key key = (Key) keyMap.get(PRIVATE_KEY);
		return key.getEncoded();
	}
	
	public static byte[] getPublicKey(Map<String,Object> keyMap){
		Key key = (Key) keyMap.get(PUBLIC_KEY);
		return key.getEncoded();
	}
}

public class ElGamalCoderTest {
	private byte[] privateKey;
	private byte[] publicKey;
	
	@Before
	public void initKey() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException{
		Map<String,Object> keyMap = ElGamalCoder.initKey();
		publicKey = ElGamalCoder.getPublicKey(keyMap);
		privateKey = ElGamalCoder.getPrivateKey(keyMap);
		System.err.println("公鑰:\n"+Base64.encodeBase64String(publicKey));
		System.err.println("私鑰:\n"+Base64.encodeBase64String(privateKey));
		
	}
	
	@Test
	public void test() throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
		System.err.println("\n--公鑰加密,私鑰解密--");
		String input1 = "ElGamal加密演算法";
		System.err.println("原文:\n"+input1);
		byte[] data1 = input1.getBytes();
		byte[] encodedata1 = ElGamalCoder.encryptByPublicKey(data1, publicKey);
		System.err.println("加密後:\n"+Base64.encodeBase64String(encodedata1));
		byte[] decodedata1 = ElGamalCoder.decryptByPrivateKey(encodedata1, privateKey);
		System.err.println("解密後:\n"+Base64.encodeBase64String(decodedata1));
		String output1 = new String(decodedata1);
		System.err.println("結果:\n"+output1);
		assertEquals(input1,output1);
	}

}