RSA加密嫁艇、解密、簽名为鳄、驗簽的原理及方法

一裳仆、RSA加密簡介

RSA加密是一種非對稱加密腕让」虑眨可以在不直接傳遞密鑰的情況下,完成解密纯丸。這能夠確保信息的安全性偏形,避免了直接傳遞密鑰所造成的被破解的風險。是由一對密鑰來進行加解密的過程觉鼻,分別稱為公鑰和私鑰俊扭。兩者之間有數(shù)學相關(guān),該加密算法的原理就是對一極大整數(shù)做因數(shù)分解的困難性來保證安全性坠陈。通常個人保存私鑰萨惑,公鑰是公開的(可能同時多人持有)捐康。

二、RSA加密庸蔼、簽名區(qū)別

加密和簽名都是為了安全性考慮解总,但略有不同。常有人問加密和簽名是用私鑰還是公鑰姐仅?其實都是對加密和簽名的作用有所混淆花枫。簡單的說,加密是為了防止信息被泄露掏膏,而簽名是為了防止信息被篡改劳翰。這里舉2個例子說明。

第一個場景:戰(zhàn)場上馒疹,B要給A傳遞一條消息佳簸,內(nèi)容為某一指令。

RSA的加密過程如下:

  • 1行冰、A生成一對密鑰(公鑰和私鑰)溺蕉,私鑰不公開,A自己保留悼做。公鑰為公開的疯特,任何人可以獲取。

  • 2肛走、A傳遞自己的公鑰給B漓雅,B用A的公鑰對消息進行加密。

  • 3朽色、A接收到B加密的消息邻吞,利用A自己的私鑰對消息進行解密。

在這個過程中葫男,只有2次傳遞過程抱冷,第一次是A傳遞公鑰給B,第二次是B傳遞加密消息給A梢褐,即使都被敵方截獲旺遮,也沒有危險性,因為只有A的私鑰才能對消息進行解密盈咳,防止了消息內(nèi)容的泄露耿眉。

第二個場景:A收到B發(fā)的消息后,需要進行回復“收到”鱼响。

RSA簽名的過程如下:

  • 1鸣剪、A生成一對密鑰(公鑰和私鑰),私鑰不公開,A自己保留筐骇。公鑰為公開的债鸡,任何人可以獲取。

  • 2铛纬、A用自己的私鑰對消息加簽娘锁,形成簽名,并將加簽的消息和消息本身一起傳遞給B饺鹃。

  • 3莫秆、B收到消息后,在獲取A的公鑰進行驗簽悔详,如果驗簽出來的內(nèi)容與消息本身一致镊屎,證明消息是A回復的。

在這個過程中茄螃,只有2次傳遞過程缝驳,第一次是A傳遞加簽的消息和消息本身給B,第二次是B獲取A的公鑰归苍,即使都被敵方截獲用狱,也沒有危險性,因為只有A的私鑰才能對消息進行簽名拼弃,即使知道了消息內(nèi)容夏伊,也無法偽造帶簽名的回復給B,防止了消息內(nèi)容的篡改吻氧。

但是溺忧,綜合兩個場景你會發(fā)現(xiàn),第一個場景雖然被截獲的消息沒有泄露盯孙,但是可以利用截獲的公鑰鲁森,將假指令進行加密,然后傳遞給A振惰。第二個場景雖然截獲的消息不能被篡改歌溉,但是消息的內(nèi)容可以利用公鑰驗簽來獲得,并不能防止泄露骑晶。所以在實際應(yīng)用中痛垛,要根據(jù)情況使用,也可以同時使用加密和簽名透罢,比如A和B都有一套自己的公鑰和私鑰榜晦,當A要給B發(fā)送消息時冠蒋,先用B的公鑰對消息加密羽圃,再對加密的消息使用A的私鑰加簽名,達到既不泄露也不被篡改,更能保證消息的安全性朽寞。

總結(jié):公鑰加密识窿、私鑰解密、私鑰簽名脑融、公鑰驗簽喻频。

三、RSA加密肘迎、簽名的方法甥温,代碼例子如下:

3.1 首先引入commons-codec

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.14</version>
</dependency>

3.2 新建密鑰對對象

/**
 * @author: huangyibo
 * @Date: 2022/4/29 18:47
 * @Description: 非對稱加密 密鑰對對象
 */

public class RsaKeyPair {

    private String publicKey;

    private String privateKey;

    public RsaKeyPair(String publicKey, String privateKey) {
        this.publicKey = publicKey;
        this.privateKey = privateKey;
    }

    public String getPublicKey() {
        return publicKey;
    }

    public String getPrivateKey() {
        return privateKey;
    }
}

3.3 構(gòu)建RSA工具類

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author: huangyibo
 * @Date: 2022/5/6 15:52
 * @Description:
 */

public class RSAUtil {

    /**
     * RSA編碼
     */
    public static final String ALGORITHM = "RSA";

    public static final String SIGN_ALGORITHM = "SHA512withRSA";

    /**
     * 默認種子, 構(gòu)建RSA密鑰對, 生成的密鑰對不變
     */
    private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 構(gòu)建RSA密鑰對
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        // 初始化隨機產(chǎn)生器
        /*SecureRandom secureRandom = new SecureRandom();
        //加入默認種子, 生成的密鑰對不變
        secureRandom.setSeed(DEFAULT_SEED.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);*/

        // 不加入默認種子, 每次生成的密鑰對會變化
        keyPairGenerator.initialize(1024, new SecureRandom());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
        String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
        return new RsaKeyPair(publicKeyString, privateKeyString);
    }


    /**
     * RSA 公鑰加密
     * @param data              加密字符串
     * @param publicKeyText     公鑰
     * @return                  密文
     * @throws Exception        加密過程中的異常信息
     */
    public static String encryptByPublicKey(String data, String publicKeyText) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 對數(shù)據(jù)分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // 獲取加密內(nèi)容使用base64進行編碼,并以UTF-8為標準轉(zhuǎn)化成字符串
        // 加密后的字符串
        return Base64.encodeBase64String(encryptedData);
    }


    /**
     * RSA 私鑰解密
     * @param data              加密字符串
     * @param privateKeyText    私鑰
     * @return                  明文
     * @throws Exception        解密過程中的異常信息
     */
    public static String decryptByPrivateKey(String data, String privateKeyText) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.decodeBase64(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 對數(shù)據(jù)分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        // 解密后的內(nèi)容
        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    /**
     * 私鑰加密
     *
     * @param data              加密數(shù)據(jù)
     * @param privateKeyText    私鑰
     * @return                  密文
     * @throws Exception        加密過程中的異常信息
     */
    public static String encryptByPrivateKey(String data, String privateKeyText) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 對數(shù)據(jù)分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // 獲取加密內(nèi)容使用base64進行編碼,并以UTF-8為標準轉(zhuǎn)化成字符串
        // 加密后的字符串
        return Base64.encodeBase64String(encryptedData);
    }

    /**
     * 公鑰解密
     *
     * @param data          加密字符串
     * @param publicKeyText 公鑰
     * @return              明文
     * @throws Exception    解密過程中的異常信息
     */
    public static String decryptByPublicKey(String data, String publicKeyText) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] dataBytes = Base64.decodeBase64(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 對數(shù)據(jù)分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        // 解密后的內(nèi)容
        return new String(decryptedData, StandardCharsets.UTF_8);
    }


    /**
     * 簽名
     *
     * @param data       待簽名數(shù)據(jù)
     * @param privateKey 私鑰
     * @return 簽名
     */
    public static String sign(String data, String privateKey) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey key = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initSign(key);
        signature.update(data.getBytes());
        return new String(Base64.encodeBase64(signature.sign()));
    }

    /**
     * 驗簽
     *
     * @param srcData 原始字符串
     * @param publicKey 公鑰
     * @param sign    簽名
     * @return 是否驗簽通過
     */
    public static boolean verify(String srcData, String publicKey, String sign) throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initVerify(key);
        signature.update(srcData.getBytes());
        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }

    public static void main(String[] args) {
        try {
            // 生成密鑰對
            RsaKeyPair rsaKeyPair = generateKeyPair();
            System.out.println("私鑰:" + rsaKeyPair.getPrivateKey());
            System.out.println("公鑰:" + rsaKeyPair.getPublicKey());

            // RSA 公鑰加密
            String data = "待加密的文字內(nèi)容";
            String encryptPublicKeyData = encryptByPublicKey(data, rsaKeyPair.getPublicKey());
            System.out.println("公鑰加密后內(nèi)容:" + encryptPublicKeyData);

            // RSA 私鑰解密
            String decryptPrivateKeyData = decryptByPrivateKey(encryptPublicKeyData, rsaKeyPair.getPrivateKey());
            System.out.println("私鑰解密后內(nèi)容:" + decryptPrivateKeyData);

            // RSA 私鑰加密
            String data1 = "待加密的文字內(nèi)容";
            String encryptPrivateKeyData = encryptByPrivateKey(data1, rsaKeyPair.getPrivateKey());
            System.out.println("私鑰加密后內(nèi)容:" + encryptPrivateKeyData);
            // RSA 公鑰解密
            String decryptPublicKeyData = decryptByPublicKey(encryptPrivateKeyData, rsaKeyPair.getPublicKey());
            System.out.println("公鑰解密后內(nèi)容:" + decryptPublicKeyData);

            // RSA簽名
            String sign = sign(data, rsaKeyPair.getPrivateKey());
            // RSA驗簽
            boolean result = verify(data, rsaKeyPair.getPublicKey(), sign);
            System.out.println("驗簽結(jié)果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.print("加解密異常");
        }
    }
}

PS:RSA加密對明文的長度有所限制,規(guī)定需加密的明文最大長度=密鑰長度-11(單位是字節(jié)妓布,即byte)姻蚓,所以在加密和解密的過程中需要分塊進行。而密鑰默認是1024位匣沼,即1024位/8位-11=128-11=117字節(jié)狰挡。所以默認加密前的明文最大長度117字節(jié),解密密文最大長度為128字释涛。那么為啥兩者相差11字節(jié)呢加叁?是因為RSA加密使用到了填充模式(padding),即內(nèi)容不足117字節(jié)時會自動填滿唇撬,用到填充模式自然會占用一定的字節(jié)它匕,而且這部分字節(jié)也是參與加密的。

密鑰長度的設(shè)置就是上面例子的keyPairGenerator.initialize(1024, new SecureRandom());窖认〕剩可自行調(diào)整,當然非對稱加密隨著密鑰變長耀态,安全性上升的同時性能也會有所下降轮傍。

參考:
https://www.cnblogs.com/pcheng/p/9629621.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市首装,隨后出現(xiàn)的幾起案子创夜,更是在濱河造成了極大的恐慌,老刑警劉巖仙逻,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驰吓,死亡現(xiàn)場離奇詭異,居然都是意外死亡系奉,警方通過查閱死者的電腦和手機檬贰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缺亮,“玉大人翁涤,你說我怎么就攤上這事。” “怎么了葵礼?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵号阿,是天一觀的道長。 經(jīng)常有香客問我鸳粉,道長扔涧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任届谈,我火速辦了婚禮枯夜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艰山。我一直安慰自己卤档,他們只是感情好,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布程剥。 她就那樣靜靜地躺著劝枣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪织鲸。 梳的紋絲不亂的頭發(fā)上舔腾,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機與錄音搂擦,去河邊找鬼稳诚。 笑死,一個胖子當著我的面吹牛瀑踢,可吹牛的內(nèi)容都是我干的扳还。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼橱夭,長吁一口氣:“原來是場噩夢啊……” “哼氨距!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起棘劣,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤俏让,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后茬暇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體首昔,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年糙俗,在試婚紗的時候發(fā)現(xiàn)自己被綠了勒奇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡巧骚,死狀恐怖赊颠,靈堂內(nèi)的尸體忽然破棺而出格二,到底是詐尸還是另有隱情,我是刑警寧澤巨税,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站粉臊,受9級特大地震影響草添,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扼仲,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一远寸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧屠凶,春花似錦驰后、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至唉韭,卻和暖如春夜涕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背属愤。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工女器, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人住诸。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓驾胆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贱呐。 傳聞我的和親對象是個殘疾皇子丧诺,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359