前段時間遇到過RSA這裏把前段時間學到的東西做一些總結:
RSA
RSA是目前爲止應用最爲廣泛的非對稱加密算法。非對稱加密算法簡單的說就是分成公鑰和私鑰。加密和解密採用不同的算法實現,這樣的好處是不需要像傳統對稱加密算法一樣將相同算法的密鑰分發給對方,從而減少密鑰被獲取所帶來的嚴重危害,目前基本上都是採用非對稱算法,而RSA是最爲廣泛的。理論上1024位以上的RSA是無法破解的(或者未公開)。
基本原理:
非對稱算法將密碼將密碼分爲公鑰和私鑰,公鑰發送給用戶(可以是多個),用戶用公鑰加密想要發送的數據,然後發送給服務器,服務器通過私鑰解密加密後的數據。
基本步驟:
生成公鑰和私鑰步驟:
- 隨機選擇兩個不相等的質數p和q
- 計算p和q的乘積n
- 計算n的歐拉數$$(歐拉數即爲小於等於n的所有質數
- 隨機選擇一個整數e,條件時1<e<ϕ(n),且e與ϕ(n)互質。
- 計算e對於的模反元素d(模反元素,就是指有個整數可以使得e×d/ϕ(n)的餘數是1。)
- (n,e)爲公鑰,(n,d)爲私鑰。
加密過程:
- 求需要加密數據m的e次方,然後用結果對n求膜,結果爲c。
解密過程:
- 對需要解密的數據c求d次方,然後用結果對n求膜,結果爲m。
RSA算法的限制:
首先一般的RSA算法膜長位1024位,也就是128個字節。加密的信息必須小於這個值。而且有時候會需要一些額外的信息比如java的實現還需要11個字節的額外信息,隱藏加密數據不能超過過117個字節。
解決方案有兩種一種是對信息分段加密,另一種是先選擇一種”對稱性加密算法”(比如DES),用這種算法的密鑰加密信息,再用RSA公鑰加密DES密鑰。
以上大部分內容來自
RSA算法原理(一)
RSA算法原理(二)
數字簽名和數字證書
如果說RSA解決的是信息加密傳輸的問題,那數字簽名解決的就是用戶驗證回送數據是否來自於服務器的問題,數字證書解決的是用戶驗證當前通信的服務器是否是用戶期望的服務器的問題。
數字簽名
服務器接收到用戶加密信息後需要給用戶回信,爲了使得用戶確認所發送的信息是來自服務器的,需要採用數字簽名。
步驟:
- 服務器先使用散列函數生成摘要。
- 服務器然後使用摘要和其他驗證信息,使用私鑰加密生成數字簽名。
- 服務器將數字簽名附在回送數據上一起發回給用戶
- 用戶使用公鑰解密數字簽名得到摘要和其他驗證信息,得以確定回送數據來自服務器。
- 用戶對回送數據使用相同的散列函數與摘要對比,如果相同則回送數據未被修改過。
數字證書
更爲複雜的情況:由於公鑰是很容易獲取的,如果一位用戶a將另一位用戶b的服務器公鑰給修改成爲用戶a自己生成的公鑰則用戶a可以冒充服務器域用戶b通信。
用戶b如何驗證當前通信的服務器是否是正確的服務器?
用戶b需要去CA(證書中心 certificate authority),爲公鑰做認證。
CA用自己的私鑰對服務器的公鑰和相關驗證信息進行加密,生成數字證書(Digital Certificate)
這樣服務器拿到數字證書,再給用戶回信的時候附上這個數字證書,用戶收到這個數字證書然後用的CA的公鑰解密,就可以拿到服務器的公鑰了,然後就能證明數字簽名是否是服務器的。
以上內容大部分來自:
數字簽名是什麼
BASE64算法:
BASE64是一種將二進制數轉換成ACSii碼的可打印字符的表示方式。一共需要表示64個可打印字符,大小寫字母52個加上10個數字0-9加上+號和斜槓/一共64字符,另外需要一個等號=作爲後綴。
BASE64將3個字符換成4個字符。3*8=4*6;
轉換後數據大小會變大三分之一左右
爲何需要進行BASE64的轉換?
BASE64轉換主要是爲了避免敏感字符。比如有一些%或者回車等等在網絡協議中有特殊的含義,爲了避免解析錯誤通常發送數據都會進行BASE64的轉換。
其次,有些數據時不看可以見的數據,有時候需要複製粘貼,使用BASE64編碼可以將不可見的數據轉成可見的數據,更容易複製粘貼或其他操作。
另外,進行轉換也可以避免數據直接暴露,算是一種非正式的加密算法。
參考資料:
BASE64的wiki
爲何要使用base64算法
RSA、數字簽名和base64的java demo
RSA和數字簽名的實現
我自己也寫了一個不過是參照上面的,就不列出來了。下面總結下實現的基本步驟。
RSA的java實現
密鑰的獲取
首先按照java面向對象的慣例,任何東西都有對象,也就是說公鑰和私鑰都是需要用類(或者接口)表示的。公鑰和私鑰的類型很多。java提供了一個接口RSAPrivateKey和RSAPublicKey。用於分別引用RSA類型的公鑰和私鑰接口。
通常來說密鑰都有兩個接口,一個接口說明時公鑰還是私鑰如PrivateKey和PublicKey,另一個接口說明了的是什麼算法如RSA或者DSA等等。類似多重繼承。
其次再來看獲得密鑰的方式有兩類,一類是自己生成,一類是從網絡中得到的字符串轉換。
自己生成密鑰:
自己生成密鑰使用的是KeyPairGenerator這是一個java自帶的密鑰生成類,生成步驟分爲三步:
- 通過
getInstance(算法名)
的方式獲得特點算法的生成類, - 然後用
initialize
初始化參數,對於RSA來說通常就是指定一個位數1024,然後指定一個隨機數生成器如new SecureRandom。 -
最後用
generateKeyPair()
生成KeyPair密鑰對,再對密鑰對KeyPair使用get方法獲得公鑰和私鑰。從網絡中獲取:
通常來說從網絡中獲取的都是字符串或者時byte[]格式,注意這裏通常會使用base編碼,使用時通常需要轉換。
這樣的數據都有固定的數據格式典型的如ASN.1格式。X509EncodedKeySpec是RSA的公鑰ASN.1格式,PKCS8EncodedKeySpec是RSA的私鑰的ASN.1的格式。這樣對不同的密鑰不同的格式使用對於的類進行轉換就可以得到PrivateKey和PublicKey的格式。
這裏還需要用到一個KeyFactory類用於得到轉換器。
轉換的步驟:
- 首先通過
KeyFactory.getInstance("RSA");
獲得一個RSA的工廠類。 - 通過X509EncodedKeySpec或者PKCS8EncodedKeySpec的帶參構造器(參數爲byte[]的密鑰數據)獲得一個指定表示特定格式的類。
- 最後通過
keyFactory.generatePublic(keySpec)
和(RSAPrivateCrtKey) keyFactory.generatePrivate(keySpec)
來獲得,記得類型轉換
密鑰的加密和解密
對於公鑰和私鑰,無論時那種類型的密鑰,無論是進行加密還是進行解密,過程是一致的。而Cipher是完成這個任務的核心類。
基本過程:
- 首先通過
Cipher.getInstance("RSA")
獲得一個Cipher。 - 初始化:
cipher.init(Cipher.ENCRYPT_MODE, privatekey);
只有兩個參數,第一個設定模式,加密爲ENCRYPT_MODE,解密爲DECRYPT_MODE,第二個爲公鑰或者私鑰。 - 最後通過
dofine
實現流程,輸出爲byte[];
數字簽名的java實現
數字簽名的過程和上述第二小節加密解密的過程時類似的,甚至更簡化,因爲數字簽名只用私鑰簽名,公鑰證人。而signature是完成這個任務的核心類:
基本過程:
- 獲得key,流程如上述:
Signature.getInstance("SHA256withRSA");
使用getInstance獲得Signature類。並指定簽名的算法。常用的算法java reference中有指定。- 使用signatur實例的
initSign(PriKey)
進行簽名初始化,或者initVerify(pubkey)
進行認證的初始化。 - 使用signatur實例的
update(bytes)
輸入需要進行簽名的數據。 - 使用signatur實例的
sign()
或者verify()
實現簽名或者認證。注意前者返回byte[]後者返回boolean。
base64
目前來說有三類方法
- Apache Commons Codec
- sun.misc
- java8之後自帶的BASE64.Decoder和Encoder。
這裏之介紹最後一個,其他版本的應用網上也很多教程。其實都很簡單。
基本步驟:
- 首先你要生成一編碼和解碼器通過,
Encoder encoder = Base64.getEncoder();
和Decoder decoder = Base64.getDecoder();
- 通過
encoder.encodeToString(bytes);
編碼通過ecoder.decode(string)
解碼