天天看點

RSA 加密 — 詳解RSA 簡介RSA 常用的加密填充模式實際運用注意事項實戰Https 證書

轉載請标明出處:http://blog.csdn.net/zhaoyanjun6/article/details/120316606

本文出自【趙彥軍的部落格】

文章目錄

  • RSA 簡介
  • RSA 常用的加密填充模式
  • 實際運用注意事項
  • 實戰
    • RSA/ECB/PKCS1Padding 封裝類
    • RSA 預設實作
  • Https 證書

RSA 簡介

RSA

——非對稱加密,會産生公鑰和私鑰,公鑰在用戶端,私鑰在服務端。公鑰用于加密,私鑰用于解密。

RSA

其實是三位數學家名字的縮寫,1977年,三位數學家

Rivest、Shamir 和 Adleman

設計了一種算法,可以實作非對稱加密。這種算法用他們三個人的名字命名,叫做RSA算法。從那時直到現在,RSA算法一直是最廣為使用的"非對稱加密算法"。毫不誇張地說,隻要有計算機網絡的地方,就有RSA算法。

在1977年的RSA論文裡,提到分解一個75位十進制數字大約需要104天。人類的技術進步是如此驚人!

當攻方的矛越來越鋒利時,守方的盾就必須越來越厚重。是以,1024比特RSA已經不安全,應用系統不應該使用少于2048比特的公鑰 值。而當需要高安全性時,選擇4096比特RSA。

RSA 常用的加密填充模式

  • RSA/None/PKCS1Padding
  • RSA/ECB/PKCS1Padding

Java 預設的 RSA 實作是

RSA/None/PKCS1Padding

, 預設實作如下:

使用模式方式的 Cipher 生成的密文總是不一緻的 ,

Bouncy Castle

的預設 RSA 實作是

RSA/None/NoPadding

為什麼 Java 預設的 RSA 實作每次生成的密文都不一緻呢,即使每次使用同一個明文、同一個公鑰?這是因為 RSA 的 PKCS #1 padding 方案在加密前對明文資訊進行了随機數填充。

實際運用注意事項

  • 1、一般由伺服器建立秘鑰對,私鑰儲存在伺服器,公鑰下發至用戶端
  • 2、公鑰是二進制資料,怎麼下發給用戶端呢?

    第一種方式:伺服器把二進制資料寫入檔案,然後把檔案傳給用戶端。由用戶端從檔案讀取二進制資料。

    第二種方式:伺服器把二進制資料轉成

    base64

    字元串,用戶端擷取到

    base64

    字元串後,再轉碼為二進制資料。
  • 3、使用相同的公鑰加密後的資料,每次都不一樣,這是因為 RSA 的填充方案在加密前對明文資訊進行了随機數填充。但是不并不影響解密的結果
  • 4、在建立RSA秘鑰對時,長度最好選擇 2048的整數倍,長度為1024在今天的網際網路已經不安全了。

實戰

RSA/ECB/PKCS1Padding 封裝類

我們封存一個

RSA

加解密工具類

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;

public class RSA {

    /**
     * 使用公鑰加密
     *
     * @param data
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
        // 得到公鑰對象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        // 加密資料
        Cipher cp = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cp.init(Cipher.ENCRYPT_MODE, pubKey);
        return cp.doFinal(data);
    }

    /**
     * 使用私鑰解密
     *
     * @param encrypted
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {
        // 得到私鑰對象
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey keyPrivate = kf.generatePrivate(keySpec);
        // 解密資料
        Cipher cp = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cp.init(Cipher.DECRYPT_MODE, keyPrivate);
        byte[] arr = cp.doFinal(encrypted);
        return arr;
    }

    /**
     * 建立非對稱加密RSA秘鑰對
     * @param keyLength
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static KeyPair generateRSAKeyPair(int keyLength) throws NoSuchAlgorithmException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(keyLength);
        return kpg.genKeyPair();
    }

    /**
     * 擷取公鑰,列印為48-12613448136942-12272-122-913111503-126115048-12...等等一長串用-拼接的數字
     */
    public static byte[] getPublicKey(KeyPair keyPair) {
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        return rsaPublicKey.getEncoded();
    }

    /**
     * 擷取私鑰,同上
     */
    public static byte[] getPrivateKey(KeyPair keyPair) {
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        return rsaPrivateKey.getEncoded();
    }

}

           

測試代碼

import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.util.Base64;

public class Test {

    public static void main(String[] args) {
        String message = "星期二,我好開心哦";

        try {
            //建立秘鑰對
            KeyPair keyPair = RSA.generateRSAKeyPair(1024);
            
            //擷取公鑰
            byte[] publicKey = RSA.getPublicKey(keyPair);
            //擷取私鑰
            byte[] privateKey = RSA.getPrivateKey(keyPair);
            //公鑰base64編碼
            String publicBase64 = Base64.getEncoder().encodeToString(publicKey);
            System.out.println("publicBase64 = " + publicBase64);

            //加密
            byte[] enResult = RSA.encryptByPublicKey(message.getBytes(), publicKey);
            //解密
            byte[] deResult = RSA.decryptByPrivateKey(enResult, privateKey);

            System.out.println("解密=" + new String(deResult, StandardCharsets.UTF_8));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
           

測試結果:

解密=星期二,我好開心哦
           

RSA 預設實作

主要就是下面這一句代碼:

完整代碼如下:

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;

public class RSA {

    /**
     * 使用公鑰加密
     *
     * @param data
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
        // 得到公鑰對象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        // 加密資料
        Cipher cp = Cipher.getInstance("RSA");
        cp.init(Cipher.ENCRYPT_MODE, pubKey);
        return cp.doFinal(data);
    }

    /**
     * 使用私鑰解密
     *
     * @param encrypted
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {
        // 得到私鑰對象
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey keyPrivate = kf.generatePrivate(keySpec);
        // 解密資料
        Cipher cp = Cipher.getInstance("RSA");
        cp.init(Cipher.DECRYPT_MODE, keyPrivate);
        byte[] arr = cp.doFinal(encrypted);
        return arr;
    }

    /**
     * 建立非對稱加密RSA秘鑰對
     *
     * @param keyLength
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static KeyPair generateRSAKeyPair(int keyLength) throws NoSuchAlgorithmException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(keyLength);
        return kpg.genKeyPair();
    }

    /**
     * 擷取公鑰,列印為48-12613448136942-12272-122-913111503-126115048-12...等等一長串用-拼接的數字
     */
    public static byte[] getPublicKey(KeyPair keyPair) {
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        return rsaPublicKey.getEncoded();
    }

    /**
     * 擷取私鑰,同上
     */
    public static byte[] getPrivateKey(KeyPair keyPair) {
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        return rsaPrivateKey.getEncoded();
    }

}
           

Https 證書

我們看一下百度的 https 證書資訊

RSA 加密 — 詳解RSA 簡介RSA 常用的加密填充模式實際運用注意事項實戰Https 證書

公鑰資訊是 RSA 算法