詳解RSA加密算法的原理與Java實現(xiàn)
前幾天阿粉剛剛說了這個 MD5 加密的前世今生,因為 MD5 也確實用的人不是很多了,阿粉就不再繼續(xù)的一一贅述了,今天阿粉想給大家分享的,是非對稱加密中的一種,那就是 RSA 加密算法。
對稱加密和非對稱加密
在說 RSA 之前,我們得先來說說這個什么事對稱加密,什么又是非對稱加密?
對稱加密指的就是加密和解密使用同一個秘鑰,所以叫對稱加密。對稱加密只有一個秘鑰,作為私鑰。
非對稱加密指的是:加密和解密使用不同的秘鑰,一把作為公開的公鑰,另一把作為私鑰。公鑰加密的信息,只有私鑰才能解密。
那么對稱加密和非對稱加密之間又有什么區(qū)別呢?
- 對稱加密中加密和解密使用的秘鑰是同一個;非對稱加密中采用兩個密鑰,一般使用公鑰進(jìn)行加密,私鑰進(jìn)行解密。
- 對稱加密解密的速度比較快,非對稱加密和解密花費的時間長、速度相對較慢。
- 對稱加密的安全性相對較低,非對稱加密的安全性較高。
今天我們來講的就是非對稱加密中的 RSA 加密。
RSA加密是什么
RSA加密是一種非對稱加密??梢栽诓恢苯觽鬟f密鑰的情況下,完成解密。這能夠確保信息的安全性,避免了直接傳遞密鑰所造成的被破解的風(fēng)險。是由一對密鑰來進(jìn)行加解密的過程,分別稱為公鑰和私鑰。
通常情況下個人保存私鑰,公鑰是公開的(可能同時多人持有)。
雖然私鑰是根據(jù)公鑰決定的, 但是,我們是沒有辦法根據(jù)公鑰來推算出私鑰來的。
為提高保密強(qiáng)度,RSA密鑰至少為500位長。這就使加密的計算量很大。為減少計算量,在傳送信息時,常采用傳統(tǒng)加密方法與公開密鑰加密方法相結(jié)合的方式,即信息采用改進(jìn)的DES或IDEA對話密鑰加密,然后使用RSA密鑰加密對話密鑰和信息摘要。對方收到信息后,用不同的密鑰解密并可核對信息摘要
RSA的加密過程
RSA的加密過程其實并不復(fù)雜,
(1)A生成一對密鑰(公鑰和私鑰),私鑰不公開,A自己保留。公鑰為公開的,任何人可以獲取。
(2)A傳遞自己的公鑰給B,B用A的公鑰對消息進(jìn)行加密。
(3)A接收到B加密的消息,利用A自己的私鑰對消息進(jìn)行解密。
在這個過程中,只有2次傳遞過程,第一次是A傳遞公鑰給B,第二次是B傳遞加密消息給A,即使都被其他人截獲,也沒有危險性,因為只有A的私鑰才能對消息進(jìn)行解密,防止了消息內(nèi)容的泄露。
但是大家有沒有想過,如果我們的消息被截獲了,雖然沒有被解密出來,但是如果說我們的公鑰被攔截,然后將假指令進(jìn)行加密,然后傳遞給A,這不就涼涼了?那數(shù)據(jù)是不是就不能稱之為安全了?
不,RSA還有簽名的過程。
簽名過程如下:
(1)A生成一對密鑰(公鑰和私鑰),私鑰不公開,A自己保留。公鑰為公開的,任何人可以獲取。
(2)A用自己的私鑰對消息加簽,形成簽名,并將加簽的消息和消息本身一起傳遞給B。
(3)B收到消息后,在獲取A的公鑰進(jìn)行驗簽,如果驗簽出來的內(nèi)容與消息本身一致,證明消息是A回復(fù)的。
但是問題又來了,雖然截獲的消息不能被篡改,但是消息的內(nèi)容可以利用公鑰驗簽來獲得,并不能防止泄露。
那么應(yīng)該怎么用呢?
其實這就顯的并不是很好理解了 我們是不是可以這么設(shè)計:
A和B都有一套自己的公鑰和私鑰,當(dāng)A要給B發(fā)送消息時,先用B的公鑰對消息加密,再對加密的消息使用A的私鑰加簽名,達(dá)到既不泄露也不被篡改,更能保證消息的安全性。
那么 Java 代碼怎么實現(xiàn) RSA 的呢?代碼如下:
import?java.io.ByteArrayOutputStream; import?java.security.KeyFactory; import?java.security.KeyPair; import?java.security.KeyPairGenerator; import?java.security.PrivateKey; import?java.security.PublicKey; import?java.security.Signature; import?java.security.spec.PKCS8EncodedKeySpec; import?java.security.spec.X509EncodedKeySpec; import?javax.crypto.Cipher; import?org.apache.commons.codec.binary.Base64; public?class?TestRSA?{ ????/** ?????*?RSA最大加密明文大小 ?????*/ ????private?static?final?int?MAX_ENCRYPT_BLOCK?=?117; ????/** ?????*?RSA最大解密密文大小 ?????*/ ????private?static?final?int?MAX_DECRYPT_BLOCK?=?128; ????/** ?????*?獲取密鑰對 ?????* ?????*?@return?密鑰對 ?????*/ ????public?static?KeyPair?getKeyPair()?throws?Exception?{ ????????KeyPairGenerator?generator?=?KeyPairGenerator.getInstance("RSA"); ????????generator.initialize(1024); ????????return?generator.generateKeyPair(); ????} ????/** ?????*?獲取私鑰 ?????* ?????*?@param?privateKey?私鑰字符串 ?????*?@return ?????*/ ????public?static?PrivateKey?getPrivateKey(String?privateKey)?throws?Exception?{ ????????KeyFactory?keyFactory?=?KeyFactory.getInstance("RSA"); ????????byte[]?decodedKey?=?Base64.decodeBase64(privateKey.getBytes()); ????????PKCS8EncodedKeySpec?keySpec?=?new?PKCS8EncodedKeySpec(decodedKey); ????????return?keyFactory.generatePrivate(keySpec); ????} ????/** ?????*?獲取公鑰 ?????* ?????*?@param?publicKey?公鑰字符串 ?????*?@return ?????*/ ????public?static?PublicKey?getPublicKey(String?publicKey)?throws?Exception?{ ????????KeyFactory?keyFactory?=?KeyFactory.getInstance("RSA"); ????????byte[]?decodedKey?=?Base64.decodeBase64(publicKey.getBytes()); ????????X509EncodedKeySpec?keySpec?=?new?X509EncodedKeySpec(decodedKey); ????????return?keyFactory.generatePublic(keySpec); ????} ????/** ?????*?RSA加密 ?????* ?????*?@param?data?待加密數(shù)據(jù) ?????*?@param?publicKey?公鑰 ?????*?@return ?????*/ ????public?static?String?encrypt(String?data,?PublicKey?publicKey)?throws?Exception?{ ????????Cipher?cipher?=?Cipher.getInstance("RSA"); ????????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進(jìn)行編碼,并以UTF-8為標(biāo)準(zhǔn)轉(zhuǎn)化成字符串 ????????//?加密后的字符串 ????????return?new?String(Base64.encodeBase64String(encryptedData)); ????} ????/** ?????*?RSA解密 ?????* ?????*?@param?data?待解密數(shù)據(jù) ?????*?@param?privateKey?私鑰 ?????*?@return ?????*/ ????public?static?String?decrypt(String?data,?PrivateKey?privateKey)?throws?Exception?{ ????????Cipher?cipher?=?Cipher.getInstance("RSA"); ????????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,?"UTF-8"); ????} ????/** ?????*?簽名 ?????* ?????*?@param?data?待簽名數(shù)據(jù) ?????*?@param?privateKey?私鑰 ?????*?@return?簽名 ?????*/ ????public?static?String?sign(String?data,?PrivateKey?privateKey)?throws?Exception?{ ????????byte[]?keyBytes?=?privateKey.getEncoded(); ????????PKCS8EncodedKeySpec?keySpec?=?new?PKCS8EncodedKeySpec(keyBytes); ????????KeyFactory?keyFactory?=?KeyFactory.getInstance("RSA"); ????????PrivateKey?key?=?keyFactory.generatePrivate(keySpec); ????????Signature?signature?=?Signature.getInstance("MD5withRSA"); ????????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,?PublicKey?publicKey,?String?sign)?throws?Exception?{ ????????byte[]?keyBytes?=?publicKey.getEncoded(); ????????X509EncodedKeySpec?keySpec?=?new?X509EncodedKeySpec(keyBytes); ????????KeyFactory?keyFactory?=?KeyFactory.getInstance("RSA"); ????????PublicKey?key?=?keyFactory.generatePublic(keySpec); ????????Signature?signature?=?Signature.getInstance("MD5withRSA"); ????????signature.initVerify(key); ????????signature.update(srcData.getBytes()); ????????return?signature.verify(Base64.decodeBase64(sign.getBytes())); ????} ????public?static?void?main(String[]?args)?{ ????????try?{ ????????????//?生成密鑰對 ????????????KeyPair?keyPair?=?getKeyPair(); ????????????String?privateKey?=?new?String(Base64.encodeBase64(keyPair.getPrivate().getEncoded())); ????????????String?publicKey?=?new?String(Base64.encodeBase64(keyPair.getPublic().getEncoded())); ????????????System.out.println("私鑰:"?+?privateKey); ????????????System.out.println("公鑰:"?+?publicKey); ????????????//?RSA加密 ????????????String?data?=?"待加密的文字內(nèi)容"; ????????????String?encryptData?=?encrypt(data,?getPublicKey(publicKey)); ????????????System.out.println("加密后內(nèi)容:"?+?encryptData); ????????????//?RSA解密 ????????????String?decryptData?=?decrypt(encryptData,?getPrivateKey(privateKey)); ????????????System.out.println("解密后內(nèi)容:"?+?decryptData); ????????????//?RSA簽名 ????????????String?sign?=?sign(data,?getPrivateKey(privateKey)); ????????????//?RSA驗簽 ????????????boolean?result?=?verify(data,?getPublicKey(publicKey),?sign); ????????????System.out.print("驗簽結(jié)果:"?+?result); ????????}?catch?(Exception?e)?{ ????????????e.printStackTrace(); ????????????System.out.print("加解密異常"); ????????} ????} }
同樣,當(dāng)我們看到 RSA 的 Java實現(xiàn)的時候,我們就看到了他的缺點,上來就先定義最大加密明文大小和最大解密密文大小,那么這個 117 是怎么來的?
Java 默認(rèn)的 RSA 加密實現(xiàn)不允許明文長度超過密鑰長度減去 11(單位是字節(jié),也就是 byte)。也就是說,如果我們定義的密鑰(我們可以通過 java.security.KeyPairGenerator.initialize(int keysize
) 來定義密鑰長度)長度為 1024(單位是位,也就是 bit),生成的密鑰長度就是 1024位 / 8位/字節(jié) = 128字節(jié),那么我們需要加密的明文長度不能超過 128字節(jié) -11 字節(jié) = 117字節(jié)。也就是說,我們最大能將 117 字節(jié)長度的明文進(jìn)行加密,否則會出問題( javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes
的異常)。
那么我們使用 RSA 的時候應(yīng)該注意什么內(nèi)容呢?
1.加密的系統(tǒng)不要具備解密的功能,否則 RSA 可能不太合適,
因為這樣即使黑客攻破了加密系統(tǒng),他拿到的也只是一堆無法破解的密文數(shù)據(jù)。
2.生成密文的長度和明文長度無關(guān),但明文長度不能超過密鑰長度
不管明文長度是多少,RSA 生成的密文長度總是固定的。但是明文長度不能超過密鑰長度。
也就是阿粉上面說的那個117字節(jié)數(shù),不然就只能等著出現(xiàn)異常了。
到此這篇關(guān)于詳解RSA加密算法的原理與Java實現(xiàn)的文章就介紹到這了,更多相關(guān)Java RSA加密算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java VisualVM監(jiān)控遠(yuǎn)程JVM(詳解)
下面小編就為大家?guī)硪黄狫ava VisualVM監(jiān)控遠(yuǎn)程JVM(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10SpringBoot集成pf4j實現(xiàn)插件開發(fā)功能的代碼示例
pf4j是一個插件框架,用于實現(xiàn)插件的動態(tài)加載,支持的插件格式(zip、jar),本文給大家介紹了SpringBoot集成pf4j實現(xiàn)插件開發(fā)功能的示例,文中通過代碼示例給大家講解的非常詳細(xì),需要的朋友可以參考下2024-07-07Spring Schedule Task動態(tài)改寫Cron配置方式
這篇文章主要介紹了Spring Schedule Task動態(tài)改寫Cron配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11解決mybatis使用char類型字段查詢oracle數(shù)據(jù)庫時結(jié)果返回null問題
這篇文章主要介紹了mybatis使用char類型字段查詢oracle數(shù)據(jù)庫時結(jié)果返回null問題的解決方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2018-06-06