關於對稱加密與非對稱加密的概念這裏再也不多說,感興趣能夠看下我以前的幾篇文章,下面說一說二者的主要區別。java
對稱加密算法數據安全,密鑰管理複雜,密鑰傳遞過程複雜,存在密鑰泄露問題。算法
非對稱加密算法強度複雜、安全性依賴於算法與密鑰。可是因爲算法複雜,使得非對稱算法加解密速度沒有對稱算法加解密的速度快。安全
對稱密鑰體制中只有一種密鑰,而且是非公開的。若是要解密就得讓對方知道密鑰。因此保證其安全性就是保證密鑰的安全。網絡
非對稱密鑰體制有兩種密鑰,其中一個是公開的,這樣就能夠不須要像對稱密碼那樣向對方傳輸密鑰了。所以安全性就大了不少。dom
對稱加密 | 非對稱加密 | |
---|---|---|
算法複雜度 | 弱 | 強 |
加解密速度 | 快 | 慢 |
安全性 | 低 | 高 |
常見算法 | DES、3DES、Blowfish、IDEA、RC四、RC五、RC六、AES | RSA、DSA、ECC、Diffie-Hellman、El Gamal |
Diffie-Hellman算法(D-H算法),密鑰一致協議。是由公開密鑰密碼體制的奠定人Diffie和Hellman所提出的一種思想。簡單的說就是容許兩名用戶在公開媒體上交換信息以生成"一致"的、能夠共享的密鑰。換句話說,就是由甲方產出一對密鑰(公鑰、私鑰),乙方依照甲方公鑰產生乙方密鑰對(公鑰、私鑰)。以此爲基線,做爲數據傳輸保密基礎,同時雙方使用同一種對稱加密算法構建本地密鑰(SecretKey)對數據加密。這樣,在互通了本地密鑰(SecretKey)算法後,甲乙雙方公開本身的公鑰,使用對方的公鑰和剛纔產生的私鑰加密數據,同時能夠使用對方的公鑰和本身的私鑰對數據解密。不僅僅是甲乙雙方兩方,能夠擴展爲多方共享數據通信,這樣就完成了網絡交互數據的安全通信!該算法源於中國的同餘定理——中國餘數定理。ide
流程分析:編碼
1.甲方構建密鑰對兒,將公鑰公佈給乙方,將私鑰保留;雙方約定數據加密算法;乙方經過甲方公鑰構建密鑰對兒,將公鑰公佈給甲方,將私鑰保留。加密
2.甲方使用私鑰、乙方公鑰、約定數據加密算法構建本地密鑰,而後經過本地密鑰加密數據,發送給乙方加密後的數據;乙方使用私鑰、甲方公鑰、約定數據加密算法構建本地密鑰,而後經過本地密鑰對數據解密。code
3.乙方使用私鑰、甲方公鑰、約定數據加密算法構建本地密鑰,而後經過本地密鑰加密數據,發送給甲方加密後的數據;甲方使用私鑰、乙方公鑰、約定數據加密算法構建本地密鑰,而後經過本地密鑰對數據解密。xml
Java代碼:
import javax.crypto.Cipher; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DHParameterSpec; import java.security.*; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.Objects; public class DH { private static final String src = "dh test"; public static void main(String[] args) { jdkDH(); } // jdk實現: public static void jdkDH() { try { // 1.發送方初始化密鑰,將公鑰給接收方 KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance("DH"); senderKeyPairGenerator.initialize(512); KeyPair senderKeyPair = senderKeyPairGenerator.generateKeyPair(); // 發送方公鑰,發送給接收方(經過網絡或文件的形式) PublicKey senderPublicKey = senderKeyPair.getPublic(); // 公鑰 PrivateKey senderPrivateKey = senderKeyPair.getPrivate(); // 私鑰 // 接收方還原發送方公鑰 KeyFactory receiverKeyFactory = KeyFactory.getInstance("DH"); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(senderPublicKey.getEncoded()); senderPublicKey = receiverKeyFactory.generatePublic(x509EncodedKeySpec); // 2.接收方經過發送方的公鑰構建密鑰,將公鑰給發送方 DHParameterSpec dhParameterSpec = ((DHPublicKey) senderPublicKey).getParams(); KeyPairGenerator receiverKeyPairGenerator = KeyPairGenerator.getInstance("DH"); receiverKeyPairGenerator.initialize(dhParameterSpec); KeyPair receiverKeypair = receiverKeyPairGenerator.generateKeyPair(); PrivateKey receiverPrivateKey = receiverKeypair.getPrivate(); // 私鑰 PublicKey receiverPublicKey = receiverKeypair.getPublic(); // 公鑰 // 3.接收方使用本身的私鑰和發送方的公鑰構建本地密鑰 KeyAgreement receiverKeyAgreement = KeyAgreement.getInstance("DH"); receiverKeyAgreement.init(receiverPrivateKey); receiverKeyAgreement.doPhase(senderPublicKey, true); SecretKey receiverDesKey = receiverKeyAgreement.generateSecret("DES"); // 發送方還原接收方公鑰 KeyFactory senderKeyFactory = KeyFactory.getInstance("DH"); x509EncodedKeySpec = new X509EncodedKeySpec(receiverPublicKey.getEncoded()); receiverPublicKey = senderKeyFactory.generatePublic(x509EncodedKeySpec); // 4.發送方使用本身的私鑰和接收方的公鑰構建本地密鑰 KeyAgreement senderKeyAgreement = KeyAgreement.getInstance("DH"); senderKeyAgreement.init(senderPrivateKey); senderKeyAgreement.doPhase(receiverPublicKey, true); SecretKey senderDesKey = senderKeyAgreement.generateSecret("DES"); if (Objects.equals(receiverDesKey, senderDesKey)) { System.out.println("雙方密鑰相同"); } // 5.發送方使用本地密鑰加密 Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, senderDesKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("bc dh encrypt:" + Base64.getEncoder().encodeToString(result)); // 6.接收方使用本地密鑰解密 cipher.init(Cipher.DECRYPT_MODE, receiverDesKey); result = cipher.doFinal(result); System.out.println("bc dh decrypt:" + new String(result)); } catch (Exception e) { e.printStackTrace(); } } }
注意:由於JDK的版本問題,若是遇到異常java.security.NoSuchAlgorithmException: Unsupported secret key algorithm: DES
,能夠在運行的時候追加JVM參數-Djdk.crypto.KeyAgreement.legacyKDF=true
RSA是目前最有影響力的公鑰加密算法,它可以抵抗到目前爲止已知的絕大多數密碼攻擊,已被ISO推薦爲公鑰數據加密標準。
RSA算法支持公鑰加密、私鑰解密以及私鑰加密、公鑰解密。既能夠用於加密也可用於數字簽名。
關於更多RSA算法的實現原理等,本文再也不涉及。
Java代碼:
import javax.crypto.Cipher; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class RSA { public static final String src = "rsa test"; public static void main(String[] args) { jdkRSA(); } // jdk實現: public static void jdkRSA() { try { // 1.生成公鑰和私鑰 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); System.out.println("Public Key:" + Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded())); System.out.println("Private Key:" + Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded())); // 2.私鑰加密、公鑰解密 ---- 加密 //PKCS8EncodedKeySpec類表示私鑰的ASN.1編碼。 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("私鑰加密、公鑰解密 ---- 加密:" + Base64.getEncoder().encodeToString(result)); // 3.私鑰加密、公鑰解密 ---- 解密 //X509EncodedKeySpec類表示根據ASN.1類型SubjectPublicKeyInfo編碼的公鑰的ASN.1編碼。 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, publicKey); result = cipher.doFinal(result); System.out.println("私鑰加密、公鑰解密 ---- 解密:" + new String(result)); // 4.公鑰加密、私鑰解密 ---- 加密 //X509EncodedKeySpec類表示根據ASN.1類型SubjectPublicKeyInfo編碼的公鑰的ASN.1編碼。 X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); KeyFactory keyFactory2 = KeyFactory.getInstance("RSA"); PublicKey publicKey2 = keyFactory2.generatePublic(x509EncodedKeySpec2); Cipher cipher2 = Cipher.getInstance("RSA"); cipher2.init(Cipher.ENCRYPT_MODE, publicKey2); byte[] result2 = cipher2.doFinal(src.getBytes()); System.out.println("公鑰加密、私鑰解密 ---- 加密:" + Base64.getEncoder().encodeToString(result2)); // 5.私鑰解密、公鑰加密 ---- 解密 //PKCS8EncodedKeySpec類表示私鑰的ASN.1編碼。 PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); KeyFactory keyFactory5 = KeyFactory.getInstance("RSA"); PrivateKey privateKey5 = keyFactory5.generatePrivate(pkcs8EncodedKeySpec5); Cipher cipher5 = Cipher.getInstance("RSA"); cipher5.init(Cipher.DECRYPT_MODE, privateKey5); byte[] result5 = cipher5.doFinal(result2); System.out.println("公鑰加密、私鑰解密 ---- 解密:" + new String(result5)); } catch (Exception e) { e.printStackTrace(); } } }
圖解流程:
在密碼學中,ElGamal加密算法是一個基於迪菲-赫爾曼密鑰交換的非對稱加密算法。它在1985年由塔希爾·蓋莫爾提出。GnuPG和PGP等不少密碼學系統中都應用到了ElGamal算法。
ElGamal算法只提供了公鑰加密,私鑰解密形式,Jdk中沒有實現,Bouncy Castle中對其進行了實現。
導入Bouncy Castle依賴:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15</artifactId> <version>1.46</version> </dependency>
Java代碼:
import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.spec.DHParameterSpec; import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class ElGamal { //非對稱密鑰算法 public static final String EL_GAMAL = "ElGamal"; /** * 密鑰長度,DH算法的默認密鑰長度是1024 * 密鑰長度必須是8的倍數,在160到16384位之間 */ private static final int KEY_SIZE = 256; public static void main(String[] args) throws Exception { System.out.println("=============接收方構建密鑰對============="); // 加入對BouncyCastle支持 Security.addProvider(new BouncyCastleProvider()); AlgorithmParameterGenerator apg = AlgorithmParameterGenerator.getInstance(EL_GAMAL); //初始化參數生成器 apg.init(KEY_SIZE); //生成算法參數 AlgorithmParameters params = apg.generateParameters(); //構建參數材料 DHParameterSpec elParams = params.getParameterSpec(DHParameterSpec.class); //實例化密鑰生成器 KeyPairGenerator kpg = KeyPairGenerator.getInstance(EL_GAMAL); //初始化密鑰對生成器 kpg.initialize(elParams, new SecureRandom()); KeyPair keyPair = kpg.generateKeyPair(); //甲方公鑰 PublicKey publicKey = keyPair.getPublic(); //甲方私鑰 PrivateKey privateKey = keyPair.getPrivate(); System.out.println("公鑰:" + Base64.getEncoder().encodeToString(publicKey.getEncoded())); System.out.println("私鑰:" + Base64.getEncoder().encodeToString(privateKey.getEncoded())); System.out.println("=============密鑰對構造完畢,接收方將公鑰公佈給發送方============="); String str = "ElGamal密碼交換算法"; System.out.println("原文:" + str); System.out.println("=============發送方還原接收方公鑰,並使用公鑰對數據進行加密============="); //還原公鑰 KeyFactory keyFactory = KeyFactory.getInstance(EL_GAMAL); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey.getEncoded()); publicKey = keyFactory.generatePublic(x509KeySpec); System.out.println("公鑰:" + Base64.getEncoder().encodeToString(publicKey.getEncoded())); //數據加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] bytes = cipher.doFinal(str.getBytes()); System.out.println("加密後的數據:" + Base64.getEncoder().encodeToString(bytes)); System.out.println("=============接收方使用私鑰對數據進行解密==========="); //還原私鑰 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded()); keyFactory = KeyFactory.getInstance(EL_GAMAL); privateKey = keyFactory.generatePrivate(pkcs8KeySpec); //數據解密 cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] bytes1 = cipher.doFinal(bytes); System.out.println("解密後的數據:" + new String(bytes1)); } }
圖解流程: