最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

JAVA Cipher的RSA加解密淺淺探索

2022-08-07 11:36 作者:寂風(fēng)也過(guò)路  | 我要投稿

一、前言

1、本文使用代碼基于我寫的存放于github的公開代碼(倉(cāng)庫(kù)地址:https://github.com/17lhf/happyTest/tree/master/src/main/java/com/basic/happytest/modules/cryptology),歡迎前去查看是否有遺漏或者bug或者復(fù)制下來(lái)檢驗(yàn)

2、因?yàn)镽SA加密都是同一個(gè)路子,所以本文實(shí)驗(yàn)簡(jiǎn)單化——全采用公鑰加密,私鑰解密的方式(這種方式也是很常見的非對(duì)稱加解密的操作方式)

3、本文只討論最常見的SunJCE version 1.8(其實(shí)就是java1.8自帶的)BC version 1.7(引入依賴包)兩種庫(kù),其他的沒了解過(guò),不獻(xiàn)丑。此外,不清楚是否有版本的影響,所以這里只討論這兩個(gè)庫(kù)的這兩個(gè)版本。

4、如果你還想了解更多JAVA實(shí)現(xiàn)PKI體系相關(guān)功能的例子,歡迎閱讀這篇文章:JAVA密碼學(xué)功能實(shí)現(xiàn)大集合

5、如果你想了解OpenSSL這個(gè)著名的工具如何通過(guò)命令行使用,歡迎閱讀這篇文章:OpenSSL命令行實(shí)例

二、RSA加解密算法原理淺談

1、RSA算法原理上,因?yàn)閿?shù)據(jù)要對(duì)密鑰的模取余數(shù),所以要求的明文和密文都是:0<明文或密文大小<密鑰的模大小,其實(shí)也對(duì)應(yīng)了我們常說(shuō)的“0<明文或密文的長(zhǎng)度<密鑰的模的長(zhǎng)度(長(zhǎng)度相等時(shí)要額外多一步比較大小)

2、實(shí)際上一些加密工具之類的會(huì)對(duì)明文長(zhǎng)度為0的時(shí)候進(jìn)行特殊處理,另外,雖然最大可被加密的明文長(zhǎng)度是密鑰的長(zhǎng)度,但是具體的實(shí)現(xiàn)工具或規(guī)范(如PKCS標(biāo)準(zhǔn))都有進(jìn)行限制(特別是當(dāng)有填充時(shí))。所以具體情況要看你用的工具/庫(kù)的限制是怎樣的。

3、密文長(zhǎng)度通常情況下都是模的長(zhǎng)度(這是標(biāo)準(zhǔn)規(guī)定的,可以在RFC文檔里看到https://rfc2cn.com/rfc3447.html

三、實(shí)踐探尋Cipher實(shí)際會(huì)調(diào)用的Provider(算法提供者)

0、首先,完成一個(gè)前提條件:在類里面引入BC庫(kù),其實(shí)就是意味著你的安全算法引入了除了Sun以外的一個(gè)叫BC的提供者。(默認(rèn)情況下,Sun的庫(kù)的優(yōu)先級(jí)是最高的,也就是會(huì)先檢索sun庫(kù),然后再檢索其他庫(kù))

如果不想用BC庫(kù),只想用Sun庫(kù),那么就不要加這一段代碼。不然當(dāng)Sun庫(kù)不支持你指定的算法時(shí),會(huì)自動(dòng)調(diào)用BC庫(kù)。

詳情可見3.1里的示例。

注:通過(guò)這段代碼,可以查看你當(dāng)前的代碼里有哪些算法提供者以及相關(guān)信息

這是我打印出來(lái)的結(jié)果:

可見大部分都是java默認(rèn)的sun提供的,但是也有對(duì)用途進(jìn)行了區(qū)分。

對(duì)于本文研究的Cipher加解密,只會(huì)用到其中的SunJCE和BC

1、不去指定時(shí)的調(diào)用情況

官網(wǎng)解釋:獲得Cipher實(shí)例時(shí),如果默認(rèn)的提供程序包提供了請(qǐng)求的轉(zhuǎn)換實(shí)現(xiàn),則會(huì)返回包含該實(shí)現(xiàn)的一個(gè)?Cipher?實(shí)例。如果默認(rèn)的提供程序包中沒有可用的轉(zhuǎn)換,則將搜索其他的提供程序包。(這里的“轉(zhuǎn)換”,就是你在getInstance時(shí)輸入的那個(gè)算法字符串)

代碼:

實(shí)際測(cè)試結(jié)果如圖:

注:奇怪的是,RSA/ECB/OAEPWithSHA-1AndMGF1Padding 和 RSA/ECB/OAEPWithSHA-256AndMGF1Padding 在是Sun提供支持的時(shí)候,加密會(huì)報(bào)錯(cuò),但是BC提供支持的時(shí)候就一切正常,加解密都沒問(wèn)題。

注:Cipher的getInstance() 實(shí)現(xiàn)邏輯如下:

特別地,如果getInstance有指定使用的provider,則會(huì)先判斷輸入的這個(gè)provider是否存在,不存在則報(bào)錯(cuò):java.security.NoSuchProviderException: No such provider: ***。然后才是按照以下步驟進(jìn)行(但是不會(huì)列舉算法提供者列表,而是直接從指定的這個(gè)算法提供者獲取服務(wù)實(shí)例,然后構(gòu)造cipher對(duì)象的時(shí)候的處理也會(huì)比較不一樣,這里不展開討論)

①先解析提供的算法串變成一個(gè)List<Cipher.Transform>,其實(shí)里面還把輸入的算法轉(zhuǎn)成好幾個(gè)書寫格式進(jìn)行保存,以適應(yīng)不同算法提供者。每個(gè)Cipher.Transform都存儲(chǔ)了你所寫的算法可能對(duì)應(yīng)的加密形式等信息。

②把List<Cipher.Transform>轉(zhuǎn)成List<ServiceId>

③獲取所有的算法提供者,也就是sun.security.jca.ProviderList

④利用ProviderList來(lái)把List<ServiceId>轉(zhuǎn)成所有的實(shí)現(xiàn)的加解密服務(wù)實(shí)例列表List<Service>

⑤遍歷List<Service>,先看算法提供者能不能用,然后看遍歷服務(wù)支持的算法能不能支持這個(gè)指定的算法,再獲取服務(wù)支持的填充模式代碼,都不能支持輸入的算法,就報(bào)錯(cuò):java.security.NoSuchAlgorithmException: Cannot find any provider supporting ******

(填充模式代碼不為0時(shí)才認(rèn)為可行,否則繼續(xù)尋找符合全部條件的服務(wù)實(shí)例。另外,模式填充代碼為2時(shí),此時(shí)不設(shè)置cipherSpi,即置為null,等初始化的時(shí)候設(shè)置)(暫時(shí)不是很清楚這個(gè)模式代碼的幾個(gè)數(shù)字的含義)

⑥初始化Cipher:Cipher(CipherSpi var1, Service var2, Iterator<Service> var3, String var4, List<Cipher.Transform> var5)

2、Cipher.getInstance時(shí)顯式指定使用BC provider

代碼:

實(shí)際測(cè)試結(jié)果:

3、顯式指定provider

也可以顯示指定SunJCE之類的provider,寫法類似3.2,只是叫的名字不一樣

4、當(dāng)使用的算法提供者是SunJCE時(shí)請(qǐng)注意

當(dāng)傳入密鑰為公鑰時(shí),RSACipher的模式其實(shí)只有加密和驗(yàn)簽兩種可能,不一定會(huì)是輸入的那個(gè)模式。
當(dāng)傳入密鑰為私鑰時(shí),RSACipher的模式其實(shí)只支持解密和簽名兩種,不一定會(huì)是輸入指定的那個(gè)模式。
詳細(xì)可見:com.sun.crypto.provider.RSACipher?的init方法

上面是常量,下面是方法實(shí)現(xiàn)

四、分組

1、使用SunJCE時(shí),分組必須使用ECB否則會(huì)報(bào)錯(cuò)

此時(shí)使用的服務(wù)是:type=Cipher,algorithm=RSA(本質(zhì)上其實(shí)是com.sun.crypto.provider.RSACipher)

通過(guò)在Cipher的getInstance里打斷點(diǎn)發(fā)現(xiàn):

此時(shí)支持的信息:

可見,此時(shí)只支持ECB的分組方式,不支持其他分組方式。

2、

其實(shí)ECB是對(duì)稱密碼的一種分組模式,對(duì)非對(duì)稱密鑰沒啥用,用None就行,例如RSA/None/PKCS1Padding,像RSA加密是不對(duì)明文數(shù)據(jù)進(jìn)行分組的。

但是,實(shí)測(cè)發(fā)現(xiàn),Sun庫(kù)要求得帶ECB,None的話得用BC庫(kù)才支持(BC庫(kù)也支持ECB的寫法)

五、填充方式

1、來(lái)自國(guó)外老哥:

OAEP is less vulnerable to padding oracle attacks than PKCS#1 v1.5 padding. GCM is also protected against padding oracle attacks。

2、NoPadding

其實(shí)在理論的RSA密碼學(xué)算法里,就是沒有padding的。

正常情況下,NoPadding占用大小是0,也就是你提供的明文可以是當(dāng)前密鑰最大的支持長(zhǎng)度。

如果你提供的明文長(zhǎng)度小于密鑰支持的最大長(zhǎng)度,那么是不會(huì)進(jìn)行額外的填充的。

但是算法實(shí)現(xiàn)標(biāo)準(zhǔn)里推薦是添加padding,主要是為了避免RSA算法上的一些漏洞,相關(guān)文章閱讀指引:《為什么 RSA 加密填充至關(guān)重要》(https://rdist.root.org/2009/10/06/why-rsa-encryption-padding-is-critical/)

3、PKCS1Padding

PKCS1Padding這種填充方式是要占用11的長(zhǎng)度, 也就是明文最長(zhǎng)只能是256-11=245了。

解密的時(shí)候就會(huì)把這些字符剔除

(SunJCE默認(rèn)使用的RSACipher里,默認(rèn)值就是使用這種填充方式)

相關(guān)標(biāo)準(zhǔn)需要查閱PKCS#1.5標(biāo)準(zhǔn)

相關(guān)規(guī)范細(xì)節(jié)可見:https://rfc2cn.com/rfc3447.html7.2.1

簡(jiǎn)單化描述:真正被加密的明文(EM)是這樣的:

EM=0x00 | | 0x02 | | PS | | 0x00 | | M

其中,PS是至少8字節(jié)長(zhǎng)度的隨機(jī)字符串,M是要被加密的消息。

另外,因?yàn)橛须S機(jī)數(shù)據(jù)的引入,所以每次加密出來(lái)的結(jié)果會(huì)不大一樣,這也提升了安全性。

六、加密長(zhǎng)度限制

1、RSA加密實(shí)現(xiàn)不允許明文超過(guò)密鑰長(zhǎng)度(也就是密鑰模長(zhǎng)度)(單位是字節(jié),也就是 byte)

也就是說(shuō),如果我們定義的密鑰(如:java.security.KeyPairGenerator.initialize(int keySize) 來(lái)定義密鑰長(zhǎng)度)長(zhǎng)度為 1024(單位是位,也就是 bit)。

生成的密鑰長(zhǎng)度就是 1024位 /(8位/字節(jié)) = 128字節(jié),那么我們需要加密的明文長(zhǎng)度不能超過(guò) 128字節(jié)。相應(yīng)的,2048bit長(zhǎng)度的RSA密鑰,對(duì)應(yīng)的最大明文長(zhǎng)度是256字節(jié)。

還有,當(dāng)“明文數(shù)據(jù)”大于等于模的大小(一般是正好被加密的數(shù)據(jù)長(zhǎng)度就是上限長(zhǎng)度的時(shí)候),也會(huì)使得加密失敗。(padding的時(shí)候,因?yàn)閿?shù)據(jù)都是加在開頭,且第一位是0,所以理論上不可能使得加完padding后的數(shù)據(jù)大于模的大小,也就是有填充的加密方式,是不會(huì)出現(xiàn)這種情況的。只有在設(shè)置為NoPadding的時(shí)候才會(huì)出現(xiàn)這種情況)

SunJCE測(cè)試代碼

當(dāng)明文數(shù)據(jù)等于模大小的時(shí)候,就會(huì)開始報(bào)錯(cuò):javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes

BC庫(kù)測(cè)試代碼

當(dāng)明文數(shù)據(jù)等于模大小的時(shí)候,就會(huì)開始報(bào)錯(cuò):java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block

上述這些限制是由RSA的算法原理決定的。

注意,這里的1024、2048可以由解析密鑰獲得(就是二進(jìn)制下的模modulus的長(zhǎng)度):

2、Sun時(shí)的加密數(shù)據(jù)長(zhǎng)度限制(以2048bit的密鑰為例)

當(dāng)輸入為RSA時(shí),SunJCE的RSACipher會(huì)被設(shè)置為paddingType=PKCS1Padding,oaepHashAlgorithm=“SHA-1”(這兩個(gè)值其實(shí)是默認(rèn)值)

3、BC時(shí)的加密數(shù)據(jù)長(zhǎng)度限制(以2048bit的密鑰為例)

4、注意

① 輸入RSA時(shí)的不同

Sun庫(kù)在加解密時(shí),輸入RSA,等同于RSA/ECB/PKCS1Padding,因?yàn)榇藭r(shí)使用了com.sun.crypto.provider.RSACipher的默認(rèn)值,此時(shí)blockSize即最大分段長(zhǎng)度為245(對(duì)于2048bit的密鑰)

BC庫(kù)在加解密時(shí)輸入RSA,等同于NoPadding,此時(shí)blockSize即最大分段長(zhǎng)度為256(對(duì)于2048bit的密鑰)

②?另外,Sun在加解密時(shí),如果用的是NoPadding,則會(huì)出現(xiàn)解密結(jié)果與原文對(duì)不上的情況。是為什么呢?(分段和不分段都有一樣的問(wèn)題)

不分段的測(cè)試代碼舉例

分段加密時(shí),如果指定的轉(zhuǎn)換為RSA/ECB/NoPadding,且使用的時(shí)SunJCE,最大輸入長(zhǎng)度是256,但是如果blockSize或者數(shù)據(jù)長(zhǎng)度不是正好被模大小整除,則加密時(shí)會(huì)自動(dòng)在長(zhǎng)度不夠的數(shù)據(jù)前面補(bǔ)0填充,這使得解密后的數(shù)據(jù)與原文對(duì)不上。

原因是SunJCE的RSACipher的解密是先創(chuàng)建大小為私鑰模大小的byte[],然后往里面填入解密結(jié)果數(shù)據(jù)。而java的byte[]數(shù)組初始化會(huì)默認(rèn)置0,所以如果結(jié)果長(zhǎng)度小于模大小,解密結(jié)果數(shù)據(jù)會(huì)帶上多余的0。

但是,實(shí)測(cè)發(fā)現(xiàn),BC庫(kù)在解密的時(shí)候,就不會(huì)有這個(gè)問(wèn)題。

所以,如果加密時(shí)用SunJCE的RSA/ECB/NoPadding,解密時(shí)用BC的RSA/ECB/NoPadding
則結(jié)果就是正確的。

③BC庫(kù)時(shí),使用NoPadding,出現(xiàn)解密結(jié)果與原文對(duì)不上的特殊情況

出錯(cuò)情況代碼示例如圖

使用BC庫(kù)加解密(填充方式選擇NoPadding),在待加密的數(shù)據(jù)開頭是至少有兩個(gè)0時(shí)(十六進(jìn)制文本),解密后會(huì)丟失最開始的兩個(gè)0,目測(cè)是在內(nèi)部處理時(shí)因?yàn)槟撤N處理方式導(dǎo)致被刪除掉了,于是解密結(jié)果與原文對(duì)不上。

神奇的是,這個(gè)特殊的內(nèi)部處理,只在開頭大于等于兩個(gè)0(十六進(jìn)制文本)的情況下出問(wèn)題,如果是1個(gè)0或者開頭壓根就不是0的時(shí)候,就一切正常。

其實(shí),解決辦法也很簡(jiǎn)單,就是改為加解密都使用PKCS1Padding就沒問(wèn)題了

改用PKCS1Padding

而且,經(jīng)過(guò)上萬(wàn)次各種各樣的原文數(shù)據(jù)加解密測(cè)試,并未發(fā)現(xiàn)PKCS1有某種特殊條件下會(huì)出錯(cuò)的情況。

不過(guò),話說(shuō)回來(lái),因?yàn)镹oPadding這種出錯(cuò)的情況下原文是很特殊的,所以實(shí)際觸發(fā)概率并不高,看是否可以接受這種出錯(cuò),如果可以,不去理會(huì)其實(shí)也行。

七、解密長(zhǎng)度限制

1、對(duì)長(zhǎng)度有要求,其實(shí)也是標(biāo)準(zhǔn)規(guī)定的

例如2048長(zhǎng)度的RSA只能解密256長(zhǎng)度的數(shù)據(jù),1024的RSA只能解密128長(zhǎng)度的數(shù)據(jù)

2、實(shí)測(cè)使用Sun時(shí)(指定使用RSA/ECB/NoPadding)

對(duì)于2048長(zhǎng)度的密鑰,待解密的密文的長(zhǎng)度不是256時(shí)報(bào)錯(cuò):javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes

待解密的密文的長(zhǎng)度小于256時(shí)能解密,不會(huì)報(bào)錯(cuò),但是結(jié)果數(shù)據(jù)與原文對(duì)不上。(理論上來(lái)說(shuō)這種密文本身就是殘缺的,不是正常RSA加密生成的)

3、實(shí)測(cè)使用BC時(shí)(指定使用RSA/ECB/NoPadding)

對(duì)于2048長(zhǎng)度的密鑰,待解密的密文的長(zhǎng)度大于256時(shí)報(bào)錯(cuò):org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.

待解密的密文的長(zhǎng)度小于256時(shí)能解密,不會(huì)報(bào)錯(cuò),但是結(jié)果數(shù)據(jù)與原文對(duì)不上。(理論上來(lái)說(shuō)這種密文本身就是殘缺的,不是正常RSA加密生成的)

八、遺憾

很可惜,我試過(guò)好多BC里面的Cipher去打斷點(diǎn),但是都沒有停在我打的斷點(diǎn)上,所以也不清楚到底哪個(gè)方法才是BC的實(shí)現(xiàn)方法。所以,BC庫(kù)的底層實(shí)現(xiàn)原理只有猜測(cè)和實(shí)踐結(jié)論,沒有更為具體的代碼展示......

JAVA Cipher的RSA加解密淺淺探索的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
诏安县| 会理县| 三亚市| 承德县| 准格尔旗| 长治市| 昌黎县| 玉山县| 长兴县| 齐齐哈尔市| 普宁市| 伊川县| 蒲江县| 安丘市| 翼城县| 富蕴县| 迭部县| 桃园县| 临漳县| 八宿县| 扬州市| 淳化县| 黎川县| 古浪县| 旌德县| 肇州县| 清镇市| 右玉县| 钟山县| 晋州市| 五莲县| 垦利县| 晋宁县| 衡阳县| 福泉市| 深水埗区| 霍邱县| 咸阳市| 平南县| 玛沁县| 故城县|