1. 程式人生 > >RSA非對稱加密

RSA非對稱加密

概念

在說 RSA 之前,首先聊聊什麼是非對稱加密。在講對稱加密的時候,就曾經說過,對稱加密演算法在加密和解密時使用的是同一個祕鑰,加解密雙方必須使用同一個金鑰才能進行正常的溝通。而非對稱加密則不然,非對稱加密演算法需要兩個金鑰來進行加密和解密,分別是公鑰和私鑰。需要注意的一點,這個公鑰和私鑰必須是一對的,如果用公鑰對資料進行加密,那麼只有使用對應的私鑰才能解密,反之亦然。由於加密和解密使用的是兩個不同的金鑰,因此,這種演算法叫做非對稱加密演算法。

工作過程

如下圖,甲乙之間使用非對稱加密的方式傳輸資料。
  • 乙方生成一對金鑰(公鑰和私鑰)並將公鑰向其它方公開;
  • 得到該公鑰的甲方使用該金鑰對機密資訊進行加密後再發送給乙方;
  • 乙方再用自己儲存的另一把專用金鑰(私鑰)對加密後的資訊進行解密;
  • 乙方只能用其專用金鑰(私鑰)解密由對應的公鑰加密後的資訊;
  • 在傳輸過程中,即使攻擊者截獲了傳輸的密文,並得到了乙的公鑰,也無法破解密文,因為只有乙的私鑰才能解密密文。
在非對稱加密中使用的主要演算法有:RSA、Elgamal、揹包演算法、Rabin、D-H、ECC(橢圓曲線加密演算法)等。今天主要是介紹 RSA ,至於其他的演算法,後續會選擇幾個進行介紹。

RSA

其實,在早在 1978 年的時候,RSA就已經出現了,它是第一個既能用於資料加密也能用於數字簽名的演算法。它易於理解和操作,也很流行。其原理就如上面的工作過程所述。RSA 演算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但是想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密金鑰。

程式碼實現

下面來看一下具體的程式碼實現。
package com.github.hls.utils;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

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.HashMap;
import java.util.Map;

public class RSA {
    /**
     * 定義加密方式
     */
    private final static String KEY_RSA = "RSA";
    /**
     * 定義簽名演算法
     */
    private final static String KEY_RSA_SIGNATURE = "MD5withRSA";
    /**
     * 定義公鑰演算法
     */
    private final static String KEY_RSA_PUBLICKEY = "RSAPublicKey";
    /**
     * 定義私鑰演算法
     */
    private final static String KEY_RSA_PRIVATEKEY = "RSAPrivateKey";

    /**
     * 初始化金鑰
     *
     * @return
     */
    public static Map<String, Object> init() {
        Map<String, Object> map = null;
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_RSA);
            generator.initialize(1024);
            KeyPair keyPair = generator.generateKeyPair();
            // 公鑰
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            // 私鑰
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            // 將金鑰封裝為map
            map = new HashMap<>();
            map.put(KEY_RSA_PUBLICKEY, publicKey);
            map.put(KEY_RSA_PRIVATEKEY, privateKey);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 用私鑰對資訊生成數字簽名
     *
     * @param data       加密資料
     * @param privateKey 私鑰
     * @return
     */
    public static String sign(byte[] data, String privateKey) {
        String str = "";
        try {
            // 解密由base64編碼的私鑰
            byte[] bytes = decryptBase64(privateKey);
            // 構造PKCS8EncodedKeySpec物件
            PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(bytes);
            // 指定的加密演算法
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            // 取私鑰物件
            PrivateKey key = factory.generatePrivate(pkcs);
            // 用私鑰對資訊生成數字簽名
            Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
            signature.initSign(key);
            signature.update(data);
            str = encryptBase64(signature.sign());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 校驗數字簽名
     *
     * @param data      加密資料
     * @param publicKey 公鑰
     * @param sign      數字簽名
     * @return 校驗成功返回true,失敗返回false
     */
    public static boolean verify(byte[] data, String publicKey, String sign) {
        boolean flag = false;
        try {
            // 解密由base64編碼的公鑰
            byte[] bytes = decryptBase64(publicKey);
            // 構造X509EncodedKeySpec物件
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
            // 指定的加密演算法
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            // 取公鑰物件
            PublicKey key = factory.generatePublic(keySpec);
            // 用公鑰驗證數字簽名
            Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
            signature.initVerify(key);
            signature.update(data);
            flag = signature.verify(decryptBase64(sign));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 私鑰解密
     *
     * @param data 加密資料
     * @param key  私鑰
     * @return
     */
    public static byte[] decryptByPrivateKey(byte[] data, String key) {
        byte[] result = null;
        try {
            // 對私鑰解密
            byte[] bytes = decryptBase64(key);
            // 取得私鑰
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            PrivateKey privateKey = factory.generatePrivate(keySpec);
            // 對資料解密
            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            result = cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 私鑰解密
     *
     * @param data 加密資料
     * @param key  公鑰
     * @return
     */
    public static byte[] decryptByPublicKey(byte[] data, String key) {
        byte[] result = null;
        try {
            // 對公鑰解密
            byte[] bytes = decryptBase64(key);
            // 取得公鑰
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            PublicKey publicKey = factory.generatePublic(keySpec);
            // 對資料解密
            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            result = cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 公鑰加密
     *
     * @param data 待加密資料
     * @param key  公鑰
     * @return
     */
    public static byte[] encryptByPublicKey(byte[] data, String key) {
        byte[] result = null;
        try {
            byte[] bytes = decryptBase64(key);
            // 取得公鑰
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            PublicKey publicKey = factory.generatePublic(keySpec);
            // 對資料加密
            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            result = cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 私鑰加密
     *
     * @param data 待加密資料
     * @param key  私鑰
     * @return
     */
    public static byte[] encryptByPrivateKey(byte[] data, String key) {
        byte[] result = null;
        try {
            byte[] bytes = decryptBase64(key);
            // 取得私鑰
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
            KeyFactory factory = KeyFactory.getInstance(KEY_RSA);
            PrivateKey privateKey = factory.generatePrivate(keySpec);
            // 對資料加密
            Cipher cipher = Cipher.getInstance(factory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            result = cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 獲取公鑰
     *
     * @param map
     * @return
     */
    public static String getPublicKey(Map<String, Object> map) {
        String str = "";
        try {
            Key key = (Key) map.get(KEY_RSA_PUBLICKEY);
            str = encryptBase64(key.getEncoded());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 獲取私鑰
     *
     * @param map
     * @return
     */
    public static String getPrivateKey(Map<String, Object> map) {
        String str = "";
        try {
            Key key = (Key) map.get(KEY_RSA_PRIVATEKEY);
            str = encryptBase64(key.getEncoded());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * BASE64 解密
     *
     * @param key 需要解密的字串
     * @return 位元組陣列
     * @throws Exception
     */
    public static byte[] decryptBase64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

    /**
     * BASE64 加密
     *
     * @param key 需要加密的位元組陣列
     * @return 字串
     * @throws Exception
     */
    public static String encryptBase64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    /**
     * 測試方法
     *
     * @param args
     */
    public static void main(String[] args) {
        String privateKey = "";
        String publicKey = "";
        // 生成公鑰私鑰
        Map<String, Object> map = init();
        publicKey = getPublicKey(map);
        privateKey = getPrivateKey(map);
        System.out.println("公鑰: \n\r" + publicKey);
        System.out.println("私鑰: \n\r" + privateKey);
        System.out.println("公鑰加密--------私鑰解密");
        String word = "你好,世界!Salt";
        byte[] encWord = encryptByPublicKey(word.getBytes(), publicKey);
        String decWord = new String(decryptByPrivateKey(encWord, privateKey));
        System.out.println("加密前: " + word + "\n\r" + "解密後: " + decWord);
        System.out.println("私鑰加密--------公鑰解密");
        String english = "Hello, World!";
        byte[] encEnglish = encryptByPrivateKey(english.getBytes(), privateKey);
        String decEnglish = new String(decryptByPublicKey(encEnglish, publicKey));
        System.out.println("加密前: " + english + "\n\r" + "解密後: " + decEnglish);
        System.out.println("私鑰簽名——公鑰驗證簽名");
        // 產生簽名
        String sign = sign(encEnglish, privateKey);
        System.out.println("簽名:\r" + sign);
        // 驗證簽名
        boolean status = verify(encEnglish, publicKey, sign);
        System.out.println("狀態:\r" + status);
    }
}