數據信息安全對咱們每一個人都有很重要的意義,特別是一些敏感信息,可能一些相似於收貨地址、手機號還沒引發你們的注意。可是最直白的,銀行卡、姓名、手機號、身份證號,若是這些信息被黑客攔截到,他就能夠假裝成你,把你的錢都取走。那咱們該怎麼防止這樣的事情發生?報文加密解密,加簽驗籤。java
我懼怕轉帳的時候,報文被黑客攔截到,篡改信息轉到別人的帳戶。git
作一筆遊戲充值,半個小時就收到各類遊戲廣告,我並不能抵擋誘惑算法
交易報文不被篡改數組
防止報文被篡改,須要對報文進行驗籤操做。安全
敏感信息不被讀取網絡
防止報文被讀取,則須要將敏感信息加密。函數
公鑰和私鑰,加密解密和加簽驗籤。加解密用來保證數據安全,加簽驗籤用來證實身份。編碼
商戶生成一對公私鑰(商公,商私),商戶會把公鑰給銀行;銀行也會生成一對公私鑰(銀公,銀私),銀行會把公鑰給商戶。也就是說:商戶有銀行的公鑰,本身的公鑰和私鑰。銀行有商戶的公鑰,本身的公鑰和私鑰加密
兩對SHA1withRSA公私鑰+DES會話密鑰。結構以下:spa
加密加簽步驟:
解密驗籤步驟:
使用KeyGenerator隨機生成一個會話密鑰desKey
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
複製代碼
報文明文+會話密鑰明文desKey對稱加密獲得加密後的密文message。
public static String encryptContext(String context, byte[] desKey) throws Exception {
byte[] encryptResult = des(context.getBytes("UTF-8"), desKey, 1);
return Hex.encodeHexString(encryptResult);
}
private static byte[] des(byte[] inputBytes, byte[] keyBytes, int mode) throws Exception {
DESKeySpec desKeySpec = new DESKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
IvParameterSpec iv = new IvParameterSpec(keyBytes);
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(mode, secretKey, iv);
return cipher.doFinal(inputBytes);
}
複製代碼
銀行的公鑰+會話密鑰desKey非對稱加密獲得加密後的會話密鑰key
public byte[] encryptRSA(byte[] plainBytes, boolean useBase64Code, String charset)
throws Exception {
String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; // 加密block須要預留11字節
int KEYBIT = 2048;
int RESERVEBYTES = 11;
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
int decryptBlock = KEYBIT / 8; // 256 bytes
int encryptBlock = decryptBlock - RESERVEBYTES; // 245 bytes
// 計算分段加密的block數 (向上取整)
int nBlock = (plainBytes.length / encryptBlock);
if ((plainBytes.length % encryptBlock) != 0) { // 餘數非0,block數再加1
nBlock += 1;
}
// 輸出buffer, 大小爲nBlock個decryptBlock
ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlock);
cipher.init(Cipher.ENCRYPT_MODE, peerPubKey);
// 分段加密
for (int offset = 0; offset < plainBytes.length; offset += encryptBlock) {
// block大小: encryptBlock 或 剩餘字節數
int inputLen = (plainBytes.length - offset);
if (inputLen > encryptBlock) {
inputLen = encryptBlock;
}
// 獲得分段加密結果
byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
// 追加結果到輸出buffer中
outbuf.write(encryptedBlock);
}
// 若是是Base64編碼,則返回Base64編碼後的數組
if (useBase64Code) {
return Base64.encodeBase64String(outbuf.toByteArray()).getBytes(
charset);
} else {
return outbuf.toByteArray(); // ciphertext
}
}
複製代碼
報文明文+商戶的私鑰非對稱加密獲得報文數字簽名sign。
public byte[] signRSA(byte[] plainBytes, boolean useBase64Code, String charset) throws Exception {
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(localPrivKey);
signature.update(plainBytes);
// 若是是Base64編碼的話,須要對簽名後的數組以Base64編碼
if (useBase64Code) {
return Base64.encodeBase64String(signature.sign()).getBytes(charset);
} else {
return signature.sign();
}
}
複製代碼
加密後的會話密鑰key+銀行的私鑰解密獲得會話密鑰明文desKey;對稱加密獲得的密文message+會話密鑰明文desKey解密獲得報文明文
public byte[] decryptRSA(byte[] cryptedBytes, boolean useBase64Code,
String charset) throws Exception {
String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; // 加密block須要預留11字節
byte[] data = null;
// 若是是Base64編碼的話,則要Base64解碼
if (useBase64Code) {
data = Base64.decodeBase64(new String(cryptedBytes, charset));
} else {
data = cryptedBytes;
}
int KEYBIT = 2048;
int RESERVEBYTES = 11;
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
int decryptBlock = KEYBIT / 8; // 256 bytes
int encryptBlock = decryptBlock - RESERVEBYTES; // 245 bytes
// 計算分段解密的block數 (理論上應該能整除)
int nBlock = (data.length / decryptBlock);
// 輸出buffer, , 大小爲nBlock個encryptBlock
ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * encryptBlock);
cipher.init(Cipher.DECRYPT_MODE, localPrivKey);
// 分段解密
for (int offset = 0; offset < data.length; offset += decryptBlock) {
// block大小: decryptBlock 或 剩餘字節數
int inputLen = (data.length - offset);
if (inputLen > decryptBlock) {
inputLen = decryptBlock;
}
// 獲得分段解密結果
byte[] decryptedBlock = cipher.doFinal(data, offset, inputLen);
// 追加結果到輸出buffer中
outbuf.write(decryptedBlock);
}
outbuf.flush();
outbuf.close();
return outbuf.toByteArray();
}
複製代碼
獲得的明文+商戶的公鑰驗籤,獲得報文是否被中途篡改過
public boolean verifyRSA(byte[] plainBytes, byte[] signBytes, boolean useBase64Code, String charset) throws Exception {
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(peerPubKey);
signature.update(plainBytes);
// 若是是Base64編碼的話,須要對驗籤的數組以Base64解碼
if (useBase64Code) {
return signature.verify(Base64.decodeBase64(new String(signBytes, charset)));
} else {
return signature.verify(signBytes);
}
}
複製代碼
代碼只給出了一部分重要的加解密,加驗籤邏輯。還有一些邏輯都貼出來有點亂,就放在倉庫裏了,具體用法查看README便可,更詳細的參考放在demo裏
其實RSA的原理很簡單,運用了數學的一個難題:兩個大的質數相乘,難以在短期內將其因式分解。原理很簡單,但實際上操做真的很難。
咱們都知道計算機的計算速度很是快,計算幾十位數的加減法都是秒出。
然而,雖然計算機很快,但再快也是有上限的。
好比我電腦的CPU主頻是2.30GHz,也就是說個人電腦每秒能夠進行2300000000次最基本的運行。
計算機的計算能力有限,就算是超級計算機「天河二號」,每秒也只能算3.39億億(這裏多了個億 ,給大佬跪了orz)次。
對應的,咱們有一個參數來衡量一個程序的耗時,叫作時間複雜度:
多項式量級 | 不嚴格的通俗例子(輸入規模 n=10^9) |
---|---|
常量階 O(1) | 只用1次運算,普通電腦 10^-9秒就能算完。 |
對數階 O(log n) | 大約會用30次計算,普通電腦 10^-8 秒算完 |
線性階 O(n) | 10^9 次計算,普通電腦須要一秒左右 |
線性對數階 O(n log n) | |
平方階 O(n^2) | 大約是 10^ 18次計算,普通電腦大概要30年。 |
非多項式量級 | 不嚴格的通俗例子(輸入規模 n=10^9) |
---|---|
指數階 O(2^n) | 大約2^1000000000次計算,心態崩了 |
階乘階 O(n!) | 人類全部電腦加在一塊兒,等太陽炸了都算不完 |
算法複雜度有各類各樣的,非多項式量級要比多項式量級耗費多得多時間,上述幾個複雜度的算法一個比一個慢。通俗的講,大O後面括號裏面函數的增加速度越快,算法越耗時。
總的來講,RSA之因此理論上很是安全,是由於破解RSA所要付出地計算成本遠遠高於使用RSA進行加密的計算成本。
使用RSA的私鑰進行解密,耗用的時間複雜度是多項式級。
不使用RSA私鑰,暴力破解,須要分解質因數,他的時間複雜度是非多項式級的指數級。
也就是有私鑰解密只要一秒,暴力破解出結果時,人類可能已經毀滅了(不嚴格)。
商戶隨機生成了一些很是很是大的整數,並用Miller-Rabin算法檢測它們是否是質數,直到找到兩個大質數——p1 和 p2 。(隨機數生成:多項式時間;Miller-Rabin: 多項式時間)
商戶計算兩個質數的乘積 n = p1*p2(乘法: 多項式時間)
商戶計算 φ(n) = (p1 - 1)(p2 - 1) (乘法: 多項式時間),這一步難以被破解,由於n太大了,分解質因數須要指數級時間複雜度。人類毀滅前是根據n推算出φ(n)可能性極小。
歐拉函數:φ(n)表示:小於n的正整數中與n互質的數的數目。(互質表示公因數爲1)
好比想要知道φ(10)的話,咱們就能夠看[1, 10)中和10互質的整數,也就是一、三、七、9這四個數。(二、四、六、8和10有公因數2,而5和10有公因數10)。因此φ(10)=4。
好比想要知道φ(21)的話,咱們就能夠看[1, 21)中和21互質的整數,也就是一、二、四、五、八、十、十一、1三、1六、1七、1九、20這12個數。(三、六、九、十二、1五、18和21有公因數3,而七、14和21有公因數7)。因此φ(21)=12。
商戶構造了一個比1大、比φ(n)小、不等於 p1 或 p2 的整數e。(隨機數:多項式時間)
商戶求出了e對於φ(n)的乘法逆元d,也就是說ed ≡ 1(mod φ(n)),也就是說ed=kφ(n)+1 (擴展歐幾里得,多項式時間)
請注意!如今神奇的事情發生了!對於一個與n互質的數a:
由於 a^φ(n) 恆等於 1 (mod n)
因此 a^kφ(n) 恆等於 1(mod n)
因此 a^(kφ(n) +1) 恆等於 a(mod n)
因此 a^ed 恆等於 a(mod n)
因此,若 c 恆等於 a^e (mod n),則 c^d恆等於 a^ed 橫等於 a(mod n)
到這裏,兩把鑰匙構造完成!ㄟ(≧◇≦)ㄏ
公鑰:(n, e)
密鑰:(n, d)
商戶想要生成一對公私鑰的時候:
商戶進行加密的時候:
假設商戶想給銀行送一個消息m,他知道銀行的公鑰,換句話說是銀行公鑰的N和e。他使用起先與銀行約好的格式將m轉換爲一個小於N的整數n,好比他能夠將每個字轉換爲這個字的Unicode碼,而後將這些數字連在一塊兒組成一個數字。假如他的信息很是長的話,他能夠將這個信息分爲幾段,而後將每一段轉換爲n。用下面這個公式他能夠將n加密爲c:
ne ≡ c (mod N) 計算c並不複雜。商戶算出c後就能夠將它傳遞給銀行,也就是密文啦。
銀行想要解密的時候:
素數又稱質數,指在一個大於1的天然數中,除了1和此整數自身外,不能被其餘天然數整除的數
互質,又稱互素。若N個整數的最大公因子是1,則稱這N個整數互質。
指數運算又稱乘方計算,計算結果稱爲冪。nm指將n自乘m次。把nm看做乘方的結果,叫作」n的m次冪」或」n的m次方」。其中,n稱爲「底數」,m稱爲「指數」。
模運算即求餘運算。
當兩個整數除以同一個正整數,若得相同餘數,則二整數同餘。
前提:對稱加密速度要比非對稱加密快速。會話密鑰是一個隨機生成的對稱式加密密鑰,舉個例子:A和B交互,A隨機挑了一個字符串,用B的公鑰加密發給了B,告訴B這個隨機字符串就是他們之間用來交流的密鑰了,以後A和B的報文就能夠不用公私鑰非對稱加密,直接用這個密鑰對稱加密便可。對稱式加密算法有不少:AES/DES等。SSH通訊的數據就是用AES之類的對稱式加密算法加密的。(在SSH協商密鑰的過程當中,還會使用專門的密鑰協商算法(Key Exchange Algorithm),確保竊聽者沒法偷聽到密鑰的內容)
即當商戶發送公鑰給銀行的時候,黑客截取了商戶的公鑰,同時把本身公鑰發給銀行,這樣一直在與銀行通訊的並非商戶。
專門提供網絡身份認證服務的機構或團體
數學的魅力在於將這個世界變得層次分明,試想當計算機的運行速度愈來愈快,RSA會被破解嗎?不見得,1999年N(兩個大質數的乘積)位數是512,後面發展成了位數是1024和2048位,計算機速度變快以後,每臺電腦能處理的位數也會愈來愈大,我相信咱們會見到更長位數的N,十萬,甚至百萬....
浩瀚世界,本身真眇小