DH、RSA與ElGamal非對稱加密算法實現及應用

1.對稱加密與非對稱加密概述

關於對稱加密與非對稱加密的概念這裏再也不多說,感興趣能夠看下我以前的幾篇文章,下面說一說二者的主要區別。java

對稱加密算法數據安全,密鑰管理複雜,密鑰傳遞過程複雜,存在密鑰泄露問題。算法

非對稱加密算法強度複雜、安全性依賴於算法與密鑰。可是因爲算法複雜,使得非對稱算法加解密速度沒有對稱算法加解密的速度快。安全

對稱密鑰體制中只有一種密鑰,而且是非公開的。若是要解密就得讓對方知道密鑰。因此保證其安全性就是保證密鑰的安全。網絡

非對稱密鑰體制有兩種密鑰,其中一個是公開的,這樣就能夠不須要像對稱密碼那樣向對方傳輸密鑰了。所以安全性就大了不少。dom

對稱加密 非對稱加密
算法複雜度
加解密速度
安全性
常見算法 DES、3DES、Blowfish、IDEA、RC四、RC五、RC六、AES RSA、DSA、ECC、Diffie-Hellman、El Gamal

2.DH算法實現過程及相關類詳解

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

3.RSA算法實現及應用

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();
        }

    }
}

圖解流程:

4.非對稱加密算法ElGamal

在密碼學中,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));
    }
}

圖解流程:

相關文章
相關標籤/搜索