[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 SecureRandom implementation 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);
}
}