開發(fā)常見密碼技術(shù)概念&RSA使用示例

一增蹭、單向散列函數(shù)

1.1 概念及術(shù)語

單向散列函數(shù)(one-way hash function)有一個輸入和一個輸出更胖,其中輸入稱為消息(message)秒梳,輸出稱為散列值(hash value)梆砸。單向散列函數(shù)可以根據(jù)消息的內(nèi)容計(jì)算出散列值,而散列值就可以被用來檢查消息的完整性佩抹。這里的消息可以是文檔圖像視頻音頻等,散列函數(shù)會將它們都當(dāng)成單純的bit序列來處理取董。這樣棍苹,要確認(rèn)完整性就不需要對比消息本身,對比小的散列值即可茵汰。

注意:單向散列函數(shù)并不是一種加密枢里,因此無法通過解密將散列值還原為原來的消息。
術(shù)語:單向散列函數(shù)也稱為消息摘要函數(shù)(message digest function)蹂午、哈希函數(shù)或者雜湊函數(shù)栏豺。輸入的消息也稱為原像(pre-image),輸出的散列值也成為消息摘要(message digest)或者指紋(fingerprint)豆胸,完整性也稱為一致性奥洼。

1.2 使用場景及常見函數(shù)

單向散列函數(shù)的使用場景

  1. 檢測軟件是否被篡改:有些軟件會將計(jì)算出的散列值公布在自己的官方網(wǎng)站上,用戶下載軟件后晚胡,可以自行計(jì)算散列值灵奖,然后與官網(wǎng)上的散列值對比來判斷是否被篡改。當(dāng)軟件作者通過多個站點(diǎn)(鏡像站點(diǎn))來發(fā)布軟件時估盘,單向散列函數(shù)就會在檢測是否被篡改方面發(fā)揮重要作用瓷患。
  2. 被用于基于口令的加密(Password Based Encryption,PBE):PBE原理是將口令和鹽(salt遣妥,通過偽隨機(jī)數(shù)生成器產(chǎn)生的隨機(jī)值)混合后計(jì)算其散列值擅编,然后將這個散列值用作加密的密鑰。通過這樣的方法能夠防御針對口令的字典攻擊箫踩。
  3. 構(gòu)造消息認(rèn)證碼:消息認(rèn)證碼是將“發(fā)送者和接收者之間的共享密鑰”和“消息”進(jìn)行混合后計(jì)算出的散列值爱态。使用消息認(rèn)證碼可以檢測并防止通信過程中的錯誤、篡改以及偽裝班套。消息認(rèn)證碼在SSL/TLS中也得到了運(yùn)用肢藐。
  4. 數(shù)字簽名:數(shù)字簽名的處理過程非常耗時,因此一般不會對整個消息內(nèi)容直接施加數(shù)字簽名吱韭,而是先通過單向散列函數(shù)計(jì)算出消息的散列值吆豹,然后再對這個散列值加數(shù)字簽名鱼的。
  5. 偽隨機(jī)數(shù)生成器:密碼技術(shù)中所使用的隨機(jī)數(shù)需要具備“事實(shí)上不可能根據(jù)過去的隨機(jī)數(shù)列預(yù)測未來的隨機(jī)數(shù)列”這樣的性質(zhì)。為了保證不可預(yù)測性痘煤,可以利用單向散列函數(shù)的單向性凑阶。
  6. 一次性口令(one-time password):一次性口令經(jīng)常被用于服務(wù)器對客戶端的合法性認(rèn)證。在這種方式中衷快,通過單向散列函數(shù)可以保證口令只在通信鏈路上傳送一次宙橱,因此即使竊聽者竊取了口令也無法使用。
  7. 存儲密碼:加鹽操作配合單向散列函數(shù)可以存儲用戶密碼蘸拔,滿足普通項(xiàng)目需求师郑。

常見的單向散列函數(shù)

MD5:MD是消息摘要 MessageDigest 的縮寫,MD5是由Rivest于1991年設(shè)計(jì)的單向散列函數(shù)调窍,能夠產(chǎn)生128bit的散列值宝冕。MD5的強(qiáng)抗碰撞性已經(jīng)被攻破,也就是說邓萨,現(xiàn)在已經(jīng)能夠產(chǎn)生具備相同散列值的兩條不同的消息地梨,因此它已經(jīng)不安全了。
SHA-1:是由NIST(National Institute of Standards and Technology缔恳,美國國家標(biāo)準(zhǔn)技術(shù)研究所)設(shè)計(jì)的一種能夠產(chǎn)生160bit散列值的單向散列函數(shù)宝剖。SHA-1處理的消息長度存在上限蚌卤,這個值接近于264bit劫哼。該函數(shù)的強(qiáng)抗碰撞性已于2005年被攻破。
SHA-256脑奠、SHA-384纸泄、SHA-512:NIST設(shè)計(jì)的單向散列函數(shù)雅镊,它們的散列值長度分別為256bit、384bit刃滓、512bit仁烹。這些函數(shù)合起來統(tǒng)稱 **SHA-2 **,它們處理的消息長度上限為:SHA-256上限接近于264bit咧虎,SHA-384和SHA-512上限接近2128bit卓缰。目前 SHA-2 的強(qiáng)抗碰撞性還未被攻破。

單向散列函數(shù)無法辨別“偽裝”

假設(shè)主動攻擊者M(jìn)偽裝成了A砰诵,向B同時發(fā)送了消息和散列值征唬,這時B能夠用函數(shù)檢查消息完整性,但是這只是對M發(fā)出的消息進(jìn)行檢查茁彭,沒辦法試別出是其他人偽裝成了A总寒。

也就是說單向散列函數(shù)無法直接用于認(rèn)證。需要結(jié)合消息認(rèn)證碼數(shù)字簽名技術(shù)理肺,保證消息完整性和進(jìn)行身份認(rèn)證摄闸。

二善镰、對稱密碼

根據(jù)密鑰的使用方法,可以將密碼分為對稱密碼和公鑰密碼兩種年枕。
對稱密碼是指在加密和解密時使用同一密鑰的方式炫欺。
非對稱密碼又叫公鑰密碼,指在加密和解密時使用不同密鑰的方式熏兄。

用相同的密鑰進(jìn)行加密和解密就是對稱加密品洛。

常見對稱加密算法:

DES(Data Encryption Standard)

DES是1977年美國聯(lián)邦信息處理標(biāo)準(zhǔn)(FIPS)中所采用的一種對稱密碼,一直以來被美國以及其他國家的政府和銀行等廣泛使用摩桶。然而隨著計(jì)算機(jī)的進(jìn)步桥状,現(xiàn)在DES已經(jīng)能夠被暴力破解,強(qiáng)度大不如前了硝清。

DES是一種將64bit的明文加密成64bit的密文的對稱密碼算法岛宦,它的密鑰長度是56bit。盡管從規(guī)格上來說耍缴,DES的密鑰長度是64bit,但由于每隔7bit會設(shè)置一個用于錯誤檢查的bit挽霉,因此實(shí)質(zhì)上長度是56bit防嗡。

DES是以64bit的明文(bit序列)為單位來進(jìn)行加密的,這個64bit的單位稱為分組侠坎。一般來說蚁趁,以分組為單位進(jìn)行處理的密碼算法稱為分組密碼(block cipher),DES就是分組密碼的一種实胸。

DES每次只能加密64bit的數(shù)據(jù)他嫡,如果要加密的明文比較長,就需要對DES加密進(jìn)行迭代(反復(fù))庐完,而迭代的具體方式就稱為模式(mode)钢属。

三重DES

三重DES(triple-DES)是為了增加DES的強(qiáng)度,將DES重復(fù)3次得到的一種密碼算法门躯,通诚常縮寫為3DES。

三重DES.png

明文經(jīng)過三次DES處理才能變成最后的密文讶凉,由于DES密鑰的長度實(shí)質(zhì)上是56bit染乌,因此三重DES的密鑰長度就是56*3=168bit。加密中加入解密懂讯,這個方法是IBM公司設(shè)計(jì)的荷憋,目的是為了3DES能夠兼容普通的DES,當(dāng)三個密鑰都相同時褐望,就可以向下兼容DES了勒庄。

盡管3DES目前還被銀行等機(jī)構(gòu)使用串前,但其處理速度不高,而且在安全性方面也逐漸顯現(xiàn)出了一些問題锅铅。

對稱密碼的新標(biāo)準(zhǔn)——AES

AES(Advanced Encryption Standard)是取代舊標(biāo)準(zhǔn)DES而成為新標(biāo)準(zhǔn)的一種對稱密碼算法酪呻。全世界的企業(yè)和密碼學(xué)家提交了多個對稱密碼算法作為AES的候選,最終在2000年從這些候選算法中選出了一種名為Rijndael的對稱密碼算法盐须,并將其確定為了AES玩荠。

應(yīng)該使用哪種對稱密碼?

現(xiàn)在DES由于安全性問題最好是不要使用了贼邓。出于兼容性因素3DES還會使用一段時間阶冈,但是會逐漸被AES所取代。
一般來說現(xiàn)在不應(yīng)該使用使用任何自制的算法塑径,而是應(yīng)該使用AES女坑,因?yàn)锳ES在其選定過程中,經(jīng)過了全世界密碼學(xué)家所進(jìn)行的高品質(zhì)的檢驗(yàn)工作统舀,而對于自制密碼算法則很難進(jìn)行這樣的驗(yàn)證匆骗。

三、非對稱密碼

在對稱密碼中誉简,加密密鑰和解密密鑰是相同的碉就,但非對稱密碼,也叫公鑰密碼中闷串,加密密鑰和解密密鑰卻是不同的瓮钥。

公鑰密碼(public-key cryptography)中,密鑰分為加密密鑰和解密密鑰兩種烹吵。發(fā)送者用加密密鑰(公鑰)對消息進(jìn)行加密
碉熄,接收者用解密密鑰(私鑰)對密文進(jìn)行解密。常見公鑰密碼有:RSA肋拔、EIGamal方式锈津、Rabin方式、橢圓曲線密碼凉蜂。
不難發(fā)現(xiàn)一姿,公鑰和私鑰有如下特點(diǎn):

  • 發(fā)送者只需要公鑰
  • 接收者只需要私鑰
  • 私鑰由接收者持有,不能被竊聽者獲取
  • 公鑰被竊聽者獲取也沒問題

得出結(jié)論:公鑰密碼解決了使用對稱密碼時的密鑰配送問題跃惫。

公鑰加解密流程圖示如下:


公鑰加解密流程.PNG

3.1 公鑰密碼無法解決的問題

公鑰密碼解決了密鑰配送問題叮叹,但這并不意味著它能夠解決所有問題,因?yàn)槲覀冃枰袛嗨玫降墓€是否正確合法爆存,這個問題被稱為公鑰認(rèn)證問題蛉顽;
還有個問題是,它的處理速度很慢先较,只有對稱密碼的幾百分之一携冤。

四悼粮、混合密碼——用對稱密碼提高速度,用公鑰密碼保護(hù)會話密鑰

通過使用對稱密碼曾棕,能夠在通信中確保機(jī)密性扣猫。然而在實(shí)際中運(yùn)用時,就必須解決密鑰配送問題翘地。
而使用公鑰密碼就可以避免解密密鑰(私鑰)配送申尤,從而也就解決了對稱密碼所存在的密鑰配送問題。但是衙耕,公鑰密碼還有兩個很大的問題:

  1. 公鑰密碼處理速度遠(yuǎn)遠(yuǎn)低于對稱密碼
  2. 公鑰密碼難以抵御中間人攻擊

混合密碼系統(tǒng)可以解決問題1昧穿,而要解決問題2,則需要對公鑰進(jìn)行認(rèn)證橙喘。

混合密碼系統(tǒng)(hybrid cryptosystem)是將對稱密碼和公鑰密碼的優(yōu)勢相結(jié)合的方法时鸵。具體加解密流程如下:

4.1 加密

混合密碼加密.PNG

會話密鑰(session key)是指為本次通信而生成的的臨時密鑰,它一般是通過偽隨機(jī)數(shù)生成器產(chǎn)生的厅瞎。會話密鑰也會作為對稱密碼的密鑰使用饰潜。
簡言之,會話密鑰是對稱密碼的密鑰和簸,同時也是公鑰密碼的明文彭雾。

4.2 解密

混合密碼解密.PNG

分離:雙方要事先約定好密文結(jié)構(gòu),這樣對密文的分離操作就很容易完成比搭。

著名的密碼軟件PGP、以及網(wǎng)絡(luò)上的密碼通信所使用的SSL/TLS都運(yùn)用了混合密碼系統(tǒng)南誊。

五身诺、數(shù)字簽名——消息到底是誰寫的

數(shù)字簽名是根據(jù)消息內(nèi)容生成的一串“只有自己才能計(jì)算出來的數(shù)值”,因此數(shù)字簽名的內(nèi)容是隨消息的改變而改變的抄囚。

數(shù)字簽名跟公鑰密碼有著非常緊密的聯(lián)系霉赡。簡言之,數(shù)字簽名就是通過將公鑰密碼“反過來用”而實(shí)現(xiàn)的幔托。如下圖所示:


公鑰密碼與數(shù)字簽名的密鑰使用方式.PNG

下面再來對比一下公鑰密碼和私鑰密碼:
公鑰密碼包括一個由公鑰和私鑰組成的密鑰對穴亏,其中公鑰加密,私鑰解密重挑。如圖:


公鑰加密.PNG

數(shù)字簽名中也同樣會使用公鑰密碼和私鑰密碼組成的密鑰對嗓化,不過這兩個密鑰的用法和公鑰密碼是相反的,即用私鑰加密相當(dāng)于生成簽名谬哀,
用公鑰解密則相當(dāng)于驗(yàn)證簽名(嚴(yán)格來說刺覆,RSA算法中公鑰加密和數(shù)字簽名正好是完全相反的關(guān)系,但在其他算法中有可能不是這樣完全相反的關(guān)系)史煎。
<meta name="source" content="lake">

私鑰加密.PNG

公鑰密碼中谦屑,任何人都能夠進(jìn)行加密驳糯;
數(shù)字簽名中,任何人都能夠驗(yàn)證簽名氢橙。

5.1 數(shù)字簽名的方法

一般有兩種生成和驗(yàn)證數(shù)字簽名的方法:

  1. 直接對消息簽名的方法
  2. 對消息的散列值簽名的方法

直接對消息簽名的方法比較容易理解酝枢,但實(shí)際上并不會使用;對消息的散列值簽名的方法稍微復(fù)雜一點(diǎn)悍手,但實(shí)際中一般都使用這種方法帘睦。

直接對消息簽名很簡單,就是用私鑰對消息簽名就行谓苟,接收者接收到消息和簽名后官脓,用對應(yīng)公鑰解簽,再對比消息涝焙,一致則驗(yàn)簽成功卑笨,否則失敗。

5.1.1 對消息的散列值簽名

上面說過仑撞,公鑰密碼算法是很慢的赤兴,那么對長消息加密也很慢。那么隧哮,能不能生成一條很短的數(shù)據(jù)來代替消息本身呢桶良?
回想一下,上面說過的單向散列函數(shù)就非常契合沮翔。長消息的散列值永遠(yuǎn)都很短陨帆,對其加簽是很輕松的。
對散列值簽名的流程圖如下:

簽名驗(yàn)簽流程.PNG

5.2 數(shù)字簽名無法解決的問題

數(shù)字簽名既可以識別出篡改和偽裝采蚀,還可以防止否認(rèn)疲牵。也就是說,同時實(shí)現(xiàn)了確認(rèn)消息的完整性榆鼠、進(jìn)行認(rèn)證以及否認(rèn)防止纲爸。

然而要正確使用數(shù)字簽名,有一個大前提妆够,那就是用于驗(yàn)證簽名的公鑰必須屬于真正的發(fā)送者识啦。即便數(shù)字簽名算法再強(qiáng)大,如果你得到的公鑰是偽造的神妹,那么數(shù)字簽名也會完全失效颓哮。

為了能夠確認(rèn)自己得到的公鑰是否合法,我們需要使用證書鸵荠。所謂證書题翻,就是將公鑰當(dāng)作一條消息,由一個可信的第三方對其簽名后所得到的公鑰。

六嵌赠、證書——為公鑰加上數(shù)字簽名

公鑰證書(Public-Key Certificate,PKC)其實(shí)和駕照很相似塑荒,里面記有姓名、組織姜挺、郵箱地址等個人信息齿税,以及屬于此人的公鑰,并由認(rèn)證機(jī)構(gòu)(Certification Authority炊豪、Certifying Authority,CA)施加數(shù)字簽名凌箕。只要看到公鑰證書,我們就可以知道認(rèn)證機(jī)構(gòu)認(rèn)定該公鑰的確屬于此人词渤。公鑰證書也簡稱為證書(certificate)牵舱。

認(rèn)證機(jī)構(gòu)就是能夠認(rèn)定“公鑰確實(shí)屬于此人”并能夠生成數(shù)字簽名的個人或者組織。世界上最有名的認(rèn)證機(jī)構(gòu)當(dāng)屬VeriSign(verisign.com)公司缺虐。

6.1 證書應(yīng)用場景

認(rèn)證機(jī)構(gòu)必須是可信的芜壁,對于“可信的第三方”,這里用 Trent 這個名字代表高氮,那么場景示例如下圖所示

<meta name="source" content="lake">
證書使用場景示例.PNG

七慧妄、RSA

7.1 RSA加密

RSA是現(xiàn)在使用最廣泛的公鑰密碼算法 。它的名字是由它的三位開發(fā)者剪芍,即Ron Rivest塞淹、Adi Shamir 和 Leonard Adleman 的姓氏的首字母組成。
RSA可以被用于公鑰密碼和數(shù)字簽名罪裹。1983年饱普,RSA公司為RSA算法在美國取得了專利,但現(xiàn)在專利已過期状共。

RSA加密過程:

在RSA中套耕,明文、密鑰和密文都是數(shù)字口芍。

RSA的加密解密公式如下:


RSA的加密解密公式.PNG

7.2 RSA數(shù)字簽名

RSA的簽名生成和驗(yàn)證公式如下:


RSA的簽名生成和驗(yàn)證公式.PNG

7.3 對RSA的攻擊

RSA的加密是求“E次方的 mod N”箍铲,解密是求“D次方的 mod N”雇卷,原理很簡單鬓椭,但它是否容易被破譯呢?
通過密文直接破譯原文的方法有如下幾種(詳細(xì)說明見參考書目對應(yīng)章節(jié)):

  • 通過密文求得明文
  • 暴力破解找出 D
  • 通過 E 和 N 求出 D
  • 對 N 進(jìn)行質(zhì)因數(shù)分解攻擊
  • 通過推測 p 和 q 進(jìn)行攻擊
  • 其他攻擊关划。只要對 N 進(jìn)行質(zhì)因數(shù)分解并求出 p 和 q小染,就能夠求出 D

除了直接對密文破譯以外,還有一種攻擊方式贮折,叫中間人攻擊(main-in-the-middle attack)裤翩。這種方法雖然不能破譯 RSA,但卻是一種針對機(jī)密性的有效攻擊。中間人攻擊方式如下圖:

中間人攻擊.png

上圖的過程可以被重復(fù)多次踊赠,Bob 向 Alice 發(fā)送加密郵件時也可能受到同樣的攻擊呵扛,因此 Bob 即便要發(fā)郵件給 Alice 以詢問她真正的想法,也會被 Mallory 隨意篡改筐带。

這種攻擊不僅針對 RSA今穿,而是可以針對任何公鑰密碼。在這個過程中伦籍,公鑰密碼并沒有被破譯蓝晒,保證了信息的機(jī)密性。然而帖鸦,所謂的機(jī)密性并非在 Alice 和 Bob 之間芝薇,而是在 Alice 和 Mallory 之間,以及 Mallory 和 Bob 之間成立的作儿。僅靠公鑰密碼本身洛二,是無法防御中間人攻擊的。

要防御中間人攻擊立倍,還需要一種手段來確認(rèn)所收到的公鑰是否真的屬于 Bob灭红,這種手段稱為認(rèn)證,這里就用到了證書口注。

7.4 整個加密簽名变擒,解密驗(yàn)簽過程

根據(jù)個人理解,整合上述的所有流程寝志,在不考慮中間人攻擊的情況下娇斑,在開發(fā)中的使用流程如下:


加密與簽名流程.png

接收者Bob收到消息后的處理流程如下:


解密與驗(yàn)簽流程.png

7.4.1 代碼模擬

根據(jù)上面流程的代碼實(shí)現(xiàn)如下:

依賴

Hutool、JDK8材部、Spring Boot

<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.7.5</version>
</dependency>

明文DP(消息發(fā)送方進(jìn)行加密簽名):

package com.jiangxb.rsa.dp;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;

/**
 * 明文DP
 * 
 * @author: jiangxiangbo
 * @date: 2021/9/1
 */
@Data
@Slf4j
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PlainText {

    private final static String ENCRYPT_DATA_SEPARATOR = "----SEPARATE----";
    private final static String publicKey_B = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv8GjRufWGPI7Xe6caZ5h5PbnRIQVzD4P1gDjKZaibcxcApGEaqFkT3Am2U6iKv6paELuwxy+dUL1Jvbs09QljuHgDB9SV0VxSM5LscpCmWJ5P1V6Y/QiholCQHCFR6ok6oE2HWGRw/bPQWr/gHfa2zNPu+CB64cbOxLHIQYIRji47tyywAL5ABhF1msZY2vW8xaFKHGq74sxNpf8s0NUnRnVRANjHtuDa/zvrHim45gqBWg+3gPVSQyPU3ydMoj0AiORJQmqprHaZDB7BufpTEZA6I2WElsKJcsGMdwfSd1s0B1iCzrkMmT30n/XXxyw8qQGsvJvQ2V90QiAV9bV+wIDAQAB";
    private final static String privateKey_A = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCyd6fTUJ1BzbOH8eKEI01gjq7sAVd/82tn/1q4+8WwlwrVxaPkQUFTeWu2zY5O+uZhW6uEAGDaD9hruCXNS3oPBSiMc5a2nAXFIfW/sVQky/EOxt3gZcpF1aoySqRwIxBgOkamyFsvcgBZgjDqmCcFwqSxvOQOtkJkwDIdrVsHfOHm4Sb2ZytGUFQaKts81W377fO0h91zhWnmFvD751o0wWv9UYOiExWDcOWThmS0OAdzmdqReQTyFKvpQNPDjkml9lnR0/1dOL8JJVB2hgP52wkhny6V62VusStF9kQk3H+TTDzw/iR8a140EaGXPNWjjm0yQdPfc7xAHonEjjFVAgMBAAECggEAec/qIPXZIF0CuTuEXKSr38gD5NpVmuPO38EPb0uJ96pgnuCzqMxRhmRN/Qv4ojfmn3UucH7BnJVMJtoeEy39NdtTfeo3aJS963vufNTQlf0NoARk1RElKt1XudPwwQlt2ABu0M/YTV4GlxGhyb3ohKoCN76x+si0MIhurIryovyabZCtlhGD2fg3V1t8RBlCEuz68FtB9fSh4zk7u6RhAL5LCOGNbVAiY4hx/NhrDiBfvQBhJZmPG+3gWjjZFgZEH5B0tGByuG2M+dj2qT5LFepkhGyI/upJwOhJrjiRrvR7LmSYCz0lI8/2fVCF8jN/TJav/1xVR82d/165Movm0QKBgQDZv/H1bhPoCoeh8z3ww8Um5FjFb1MMjmh4oB1d2+0QlYTbSVx6mOxBoh0yk+jKztotJyWs61nnHekOhdHFx9Ij1L8oMxybBK+heTuzl2WIs9/2CRBV4XfKMwNiYxJYkaXxUgeHx/2IVXTuFMKMrWhf7kxk2iFrK+Gv63oY0dmvhwKBgQDR0TWXRXC2qEqH/NV/6d24UHl4i/+UP1aKE9jA8xArJYBlKtTWCgM7g3/wxr0IRB6RocVupop/kZJ9RUFjprfaykDOj+A0oC+IDwUmGIjGbR4P921qjWEVQGIFSJvnIwHwGfEAPxvw0uW2tqz9C2GUZ9OB17lecfIdeJQX2Hb3QwKBgA9bWCclAkZlJ7emPgIS7H6XsCMMfODv0jJfqHKMJiX7RYlpnRoQWukuE70TbWGQQRbaIfAWERsZouwhR/AY7ZsVT/33zNap9/D9adZ6oPCJLwxdC0fjRN1/x4dS0WJpszhXvqw20Iyi6kI4OJhPSoMpfT3HnH/AcoRDqTLC6gVVAoGBAI3f9GfseZHZbERV75wF7HoEWI7tw41f4smNMAUQln9GZXKDKtXsgVEN00ZhbFMZlL4O8GyoyoAGVFLGsLeMdUfJeVbzrLyJEHrlBStEbcAW6rwLJ/5jySDQnzdJaLo7TsUnFXKAOgl24gPRtFmLB5mNN1TWJS86x2esMB+LrK33AoGADEDHIUtulc4zclLH9MJj7JcZPkgVz5llJ1jQj3fOu4iPc9TNvV2gV2kWU446gyMmRrQ2We1awnrjaeSzeFnf0OhL+yTzNUmRLMYWZhja/KMhr7b9vVRCCrysZJod+MWodEH+HIJlu9RGIxv7fNNy1S4yRU92OQU43XQ1S93eaEE=";
    
    /**
     * 消息明文json
     */
    private String msg;
    
    /**
     * 加密簽名
     */
    public Map<String, Object> encrypt() {
        assert StringUtils.isNotBlank(msg);
        Map<String, Object> map = Maps.newHashMap();
        try {
            // 一毫缆、 AES密鑰加密

            // 1. 隨機(jī)生成AES密鑰
            byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
            SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
            // 2. 明文加密
            String encryptMsg = aes.encryptBase64(msg);

            // 3. 用B公鑰加密AES密鑰
            RSA rsa = new RSA(null, publicKey_B);
            String encryptKey = rsa.encryptBase64(key, KeyType.PublicKey);

            // 4. 組合
            String content = encryptKey + ENCRYPT_DATA_SEPARATOR + encryptMsg;
            log.info("A發(fā)送的content為:{}", content);

            // 二、簽名

            // 1. 計(jì)算消息散列值
            String msgHash = DigestUtil.sha256Hex(msg, CharsetUtil.UTF_8);

            // 2. 對消息散列值用自己私鑰簽名
            Sign sign = SecureUtil.sign(SignAlgorithm.SHA1withRSA, privateKey_A, null);
            String signed = Base64.encodeBase64String(sign.sign(msgHash));
            log.info("簽名為:{}", signed);
            
            map.put("content", content);
            map.put("signed", signed);
            log.info("發(fā)送的消息:{}", map);
        } catch (Exception e) {
            log.error("消息明文加密失敗", e);
        }
        return map;
    }
    
}

密文DP(消息接收方進(jìn)行解密驗(yàn)簽):

package com.jiangxb.rsa.dp;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.validation.constraints.NotBlank;

/**
 * 密文DP
 * 
 * @author: jiangxiangbo
 * @date: 2021/8/4
 */
@Data
@Slf4j
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CipherText {

    private final static String ENCRYPT_DATA_SEPARATOR = "----SEPARATE----";
    private final static String publicKey_A = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsnen01CdQc2zh/HihCNNYI6u7AFXf/NrZ/9auPvFsJcK1cWj5EFBU3lrts2OTvrmYVurhABg2g/Ya7glzUt6DwUojHOWtpwFxSH1v7FUJMvxDsbd4GXKRdWqMkqkcCMQYDpGpshbL3IAWYIw6pgnBcKksbzkDrZCZMAyHa1bB3zh5uEm9mcrRlBUGirbPNVt++3ztIfdc4Vp5hbw++daNMFr/VGDohMVg3Dlk4ZktDgHc5nakXkE8hSr6UDTw45JpfZZ0dP9XTi/CSVQdoYD+dsJIZ8uletlbrErRfZEJNx/k0w88P4kfGteNBGhlzzVo45tMkHT33O8QB6JxI4xVQIDAQAB";
    private final static String privateKey_B = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC/waNG59YY8jtd7pxpnmHk9udEhBXMPg/WAOMplqJtzFwCkYRqoWRPcCbZTqIq/qloQu7DHL51QvUm9uzT1CWO4eAMH1JXRXFIzkuxykKZYnk/VXpj9CKGiUJAcIVHqiTqgTYdYZHD9s9Bav+Ad9rbM0+74IHrhxs7EschBghGOLju3LLAAvkAGEXWaxlja9bzFoUocarvizE2l/yzQ1SdGdVEA2Me24Nr/O+seKbjmCoFaD7eA9VJDI9TfJ0yiPQCI5ElCaqmsdpkMHsG5+lMRkDojZYSWwolywYx3B9J3WzQHWILOuQyZPfSf9dfHLDypAay8m9DZX3RCIBX1tX7AgMBAAECggEBAIkRYyMGCTYfwGvuagPdYOCH1NxXBjXOjwdL7xUFRenyUDrNxbdq0gcuhbaDzMuq6XFLltwFKecsC4zkqHjqhkZSExLXOMaFLur5+4WErIJzr3OkKC5Wjm9YofDp/XsyldzCq+nomodXXuLGFwi/o8NYNEB5xKSVGNPrIkfqxfNazdR63738zq0ZPQmMjxEb/AK5uc+fdF9qosDrNI0SqQng00mhfpilvwHZbOYPfKNfh26lpqTEAGk0gaFGfr/QnhUDAnxfaoLhr9zELr4utrkwpaCzX958MrRB5naeScocYSl1h4Bi6htjjpdLWDKkk/vQ8Keno6GtF9Iha8MdvnECgYEA9p5UzaP3qYek4pe96milqmPiYkQeqSAUWSp1TpC5ppIoyPGMYl7Ia8SOsIYBw+9WL9iIR6jqZOpd/E68j8YsW5PyJuFYQTVjXZrVD76D435mskl4gsKz1izEWz5jzU/oE4mGfuaobfaOuw5ixun7dd8FrXknIVHbne0zyEZ+FY0CgYEAxw0Jq3aJbrJHX8ahPsZAodbWKYH8Ojkt2GkXKdrB9HJ6EGKockjPj7R/+ForXw2XWoGdoL4QPalhKuJX+3bsSQIgt7mDRiDEPK7XkbKd5mS/HTXWXsTIkGaDhYlq6yOkbmRsR3QgCfnhLYaaYkZ6kIKDsGgUJF8oIqxKrlDWo6cCgYBUyulzbu3nLwklE3Er2GElbYRXrv4vviTg53U/1wjN2bEGLe7Ln7UfQIyi6uBOgsrKVpO8t7onimFYL6YrdMKplfuLHK2gdf+9HlAlQqbMIBilMheqNdFpUSkOCix8Wf38QauplBrS/BPlArQ5mhdoVo74LxCiJyfwa68DLCGLvQKBgEGg4tdNtfJxhWbmrrNr2lOB6gq1eNwZjiwUOjbqkZhvRh+w56kGqKjQ8oCH+lTUvlpw8e/VurUZ65egGTIn+6/2q6Ln34h3tTvsydaX9cfI39pZrdyBNT+nDSYyMLZmggiDw8+rUgT4Bm5kOvK8Gh0bax/2sO1tEmacN+NRc/NxAoGALzEnmKI9B46NVNOWi0VLtTdiloSI6bxgli0Rm8T+6wD6y9JNnWsibYydGx9pDn6w2qihuP1QcKruHUiZ7V8aahhD4o4e/a+IgRzNYcQh4CngUzL+XymlqQVbDSaiEiVi/qSNv+i9mgeF/mSYbYcZjhFPjzdOy3hvtO3GtjDSMQw=";
    
    @NotBlank(message = "密文不能為空")
    private String content;

    @NotBlank(message = "簽名不能為空")
    private String signed;

    /**
     * 解密驗(yàn)簽
     */
    public String decrypt() {
        try {
            // 一乐导、分解密文
            log.info("接收到的密文:{}", content);
            String[] split = content.split(ENCRYPT_DATA_SEPARATOR);
            assert split.length == 2;
            String encryptKey = split[0];
            String encryptMsg = split[1];

            // 獲取對稱密鑰
            RSA rsa = new RSA(privateKey_B, null);

            byte[] key = rsa.decrypt(Base64.decodeBase64(encryptKey), KeyType.PrivateKey);
            log.info("獲取對稱密鑰成功, 解析到的對稱密鑰為:{}", key);

            // 用對稱密鑰解密苦丁,提取消息明文
            SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
            String msg = aes.decryptStr(Base64.decodeBase64(encryptMsg));

            assert msg != null;

            // 計(jì)算明文散列值
            String msgHash = DigestUtil.sha256Hex(msg, CharsetUtil.UTF_8);

            // 二、解簽
            log.info("接收到的簽名:{}", signed);
            assert StringUtils.isNotBlank(signed);

            Sign sign = SecureUtil.sign(SignAlgorithm.SHA1withRSA, null, publicKey_A);
            boolean isVerified = sign.verify(msgHash.getBytes(CharsetUtil.UTF_8), Base64.decodeBase64(signed));
            if (!isVerified) {
                log.info("驗(yàn)簽失敗");
            } else {
                log.info("驗(yàn)簽成功, 收到的消息為:{}", msg);
                return msg;
            }
        } catch (Exception e) {
            log.error("解密或驗(yàn)簽失敗", e);
        }
        return null;
    }
    
}

demo測試:

package com.jiangxb.rsa;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.jiangxb.model.Student;
import com.jiangxb.rsa.dp.CipherText;
import com.jiangxb.rsa.dp.PlainText;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * @author: jiangxiangbo
 * @date: 2021/7/30
 */
@Validated
@RestController
@RequestMapping("/rsa")
public class RsaController {
    
    /**
     * 接收消息
     */
    @PostMapping("/post")
    public String testPost(@Validated CipherText cipherText) {
        return cipherText.decrypt();
    }

    /**
     * 發(fā)送消息
     */
    public static void main(String[] args) {
        String msg = JSONObject.toJSONString(Student.builder().name("張三").age(20).build());
        Map<String, Object> encrypt = PlainText.builder().msg(msg).build().encrypt();
        String resopnse = HttpUtil.post("127.0.0.1:8080/rsa/post", encrypt);
        System.out.println(resopnse);
    }
    
}

Student實(shí)體:

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private String name;

    private Integer age;
    
}

測試結(jié)果:

[INFO ] [http-nio-8080-exec-4] [com.jiangxb.rsa.dp.CipherText ] 
接收到的密文:rVcet45asWcB2KZwQv6HmOktquLyPPqixOjPpIc3eIbULnUcuorl2KIxK4XDipvreT7tATlW4VFzbC7gSFW0DfiWLdqknLyPzmC2nXBeaP8o89bMk4pe4sQ/NVct5JTRpJqx+0+8MGA4QrMUlQZ+4REhZmdLg+JyJijRSccRvQMZhBh1uOZCRWeo8gmR5oWMuquo0r91Y91ewaMoVHjWfMdtu2Y1H8vXAeb7b1aDc26SEtstZb8q/TqZlpo+hh2pVoDGIhXJUQH5VlizF9sZnZBPuLUCnar7NU+wi2oEWlrNHscaP0dKCRKsikAcf0kplM3UhXCXghC6xkZsk1uptA==----SEPARATE----Wo89FnRcZ3e8PXL40tgieUYQssiUlYIzbzP8gMXB04w=
[INFO ] [http-nio-8080-exec-4] [com.jiangxb.rsa.dp.CipherText ] 獲取對稱密鑰成功, 解析到的對稱密鑰為:[-28, 4, -90, 9, 120, 77, 28, 73, 119, 18, -112, 90, 65, -59, 89, -113]
[INFO ] [http-nio-8080-exec-4] [com.jiangxb.rsa.dp.CipherText ] 接收到的簽名:kh7HtOnSPQi8t/vDrle31tRWUeSGc3cyc8S5heHKThq5HtH7dCeZEY8vq+UFtlMr/eqxF19y/VonXlaSaFsRxzKLM3fVJ8AGWivdH7MVpBI3/x3cHbqDfgnSRmhVQDBnTBjrgTgO00KwZx/hxJn6x5NzfXawxpoFyIXauJuqOpW+tts8ysNJMhAeR7/LdXuISEFLK7lZf/zWESwiH+f4davQv3XiVuXzTEH6U05NGwDq2LqWMiCBh/MgQFJfLaOBUO9kCNoMcuNiS3KOG0JBLr718rPX4lUKI37C0Mc3rAzgvnlZNMv68YtPxFiHe0VhpW/ssOHk245zUPWNjIsdPA==
[INFO ] [http-nio-8080-exec-4] [com.jiangxb.rsa.dp.CipherText ] 驗(yàn)簽成功, 收到的消息為:{"age":20,"name":"張三"}

參考

《圖解密碼技術(shù)》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末物臂,一起剝皮案震驚了整個濱河市旺拉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棵磷,老刑警劉巖蛾狗,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仪媒,居然都是意外死亡沉桌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來留凭,“玉大人佃扼,你說我怎么就攤上這事“梗” “怎么了松嘶?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挎扰。 經(jīng)常有香客問我翠订,道長,這世上最難降的妖魔是什么遵倦? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任尽超,我火速辦了婚禮,結(jié)果婚禮上梧躺,老公的妹妹穿的比我還像新娘似谁。我一直安慰自己,他們只是感情好掠哥,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布巩踏。 她就那樣靜靜地躺著,像睡著了一般续搀。 火紅的嫁衣襯著肌膚如雪塞琼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天禁舷,我揣著相機(jī)與錄音彪杉,去河邊找鬼。 笑死牵咙,一個胖子當(dāng)著我的面吹牛派近,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播洁桌,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼渴丸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了另凌?” 一聲冷哼從身側(cè)響起谱轨,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎途茫,沒想到半個月后碟嘴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溪食,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡囊卜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栅组。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡雀瓢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玉掸,到底是詐尸還是另有隱情刃麸,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布司浪,位于F島的核電站泊业,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏啊易。R本人自食惡果不足惜吁伺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望租谈。 院中可真熱鬧篮奄,春花似錦、人聲如沸割去。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呻逆。三九已至夸赫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咖城,已是汗流浹背憔足。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酒繁,地道東北人滓彰。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像州袒,于是被迫代替她去往敵國和親揭绑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 對稱密碼 加密與解密的密鑰相同共鑰密碼 加密與解密密鑰不同 單向散列函數(shù) hash 保證完整性消息認(rèn)證碼 不僅能夠...
    SueLyon閱讀 320評論 0 0
  • 前言 《圖解密碼技術(shù)》一書介紹了很多關(guān)于密碼的知識郎哭,通讀一遍需要不少時間他匪。為了方便學(xué)習(xí),我對書中關(guān)鍵的部分進(jìn)行了總...
    咖枯閱讀 7,149評論 1 25
  • 前言 密碼技術(shù)是網(wǎng)絡(luò)安全的基礎(chǔ)夸研,也是核心“蠲郏現(xiàn)在對隱私保護(hù)、敏感信息尤其重視亥至,所以不論是系統(tǒng)開發(fā)還是App開發(fā)悼沈,只要...
    張聰_2048閱讀 456評論 1 0
  • 對稱加密: DES DES是1977年美國聯(lián)邦信息處理標(biāo)準(zhǔn)中使用的一種對稱密碼技術(shù)贱迟,曾今被美國和其他國家政府銀行使...
    十三億少女夢丶閱讀 1,112評論 0 10
  • 工作或者面試過程中我們總會接觸到密碼相關(guān)的技術(shù)和問題,本篇文章是我對近期閱讀《圖解密碼技術(shù)》的總結(jié)絮供,這本書很值得推...
    樂Coding閱讀 1,442評論 0 5