[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 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 keyMap = new HashMap(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 keyMap){
		RSAPrivateKey privateKey = (RSAPrivateKey) keyMap.get(PRIVATE_KEY);
		return privateKey.getEncoded();
	}
	
	public static byte[] getPublicKey(Map 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 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 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 keyMap = new HashMap(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}

经过试验,按照往常的套路,这样的代码也是可行的:

	public static Map 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 keyMap = new HashMap(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}

而两种方法的关键都在于KeyPairGenerator类的init()函数:

Class KeyPairGenerator

  • java.lang.Object
    • java.security.KeyPairGeneratorSpi
      • java.security.KeyPairGenerator
void initialize(AlgorithmParameterSpec params)
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 initialize(AlgorithmParameterSpec params, SecureRandom random)
Initializes the key pair generator with the given parameter set and source of randomness.
void initialize(int keysize)
Initializes the key pair generator for a certain keysize using a default parameter set and the SecureRandom implementation of the highest-priority installed provider as the source of randomness.
void initialize(int keysize, SecureRandom random)
Initializes the key pair generator for a certain keysize with the given source of randomness (and a default parameter set).

Interface AlgorithmParameterSpec

  • All Known Subinterfaces:
    C14NMethodParameterSpec,  DigestMethodParameterSpec,  SignatureMethodParameterSpec,  TransformParameterSpec
    All Known Implementing Classes:
    DHGenParameterSpec,  DHParameterSpec,  DSAGenParameterSpec,  DSAParameterSpec,  ECGenParameterSpec,  ECParameterSpec, ExcC14NParameterSpec,  GCMParameterSpec,  HMACParameterSpec,  IvParameterSpec,  MGF1ParameterSpec,  OAEPParameterSpec, PBEParameterSpec,  PSSParameterSpec,  RC2ParameterSpec,  RC5ParameterSpec,  RSAKeyGenParameterSpec,  XPathFilter2ParameterSpec, XPathFilterParameterSpec,  XSLTTransformParameterSpec
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 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 keyMap = new HashMap(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}
	
	public static byte[] getPrivateKey(Map keyMap){
		Key key = (Key) keyMap.get(PRIVATE_KEY);
		return key.getEncoded();
	}
	
	public static byte[] getPublicKey(Map 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 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);
	}

}


你可能感兴趣的:([JAVA加解密]RSA算法、ElGamal算法)