數(shù)據(jù)信息安全對(duì)我們每個(gè)人都有很重要的意義牺汤,特別是一些敏感信息,可能一些類(lèi)似于收貨地址浩嫌、手機(jī)號(hào)還沒(méi)引起大家的注意檐迟。但是最直白的补胚,銀行卡、姓名追迟、手機(jī)號(hào)溶其、身份證號(hào),如果這些信息被黑客攔截到怔匣,他就可以偽裝成你握联,把你的錢(qián)都取走。那我們?cè)撛趺捶乐惯@樣的事情發(fā)生每瞒?報(bào)文加密解密金闽,加簽驗(yàn)簽。
我害怕什么
我害怕卡里的錢(qián)被別人取走
我害怕轉(zhuǎn)賬的時(shí)候剿骨,報(bào)文被黑客攔截到代芜,篡改信息轉(zhuǎn)到別人的賬戶(hù)。
我害怕我的敏感信息被有心人獲取
做一筆游戲充值浓利,半個(gè)小時(shí)就收到各種游戲廣告挤庇,我并不能抵擋誘惑
我要做什么
-
交易報(bào)文不被篡改
防止報(bào)文被篡改,需要對(duì)報(bào)文進(jìn)行驗(yàn)簽操作贷掖。
-
敏感信息不被讀取
防止報(bào)文被讀取嫡秕,則需要將敏感信息加密。
公鑰和私鑰
公鑰和私鑰苹威,加密解密和加簽驗(yàn)簽昆咽。加解密用來(lái)保證數(shù)據(jù)安全,加簽驗(yàn)簽用來(lái)證明身份牙甫。
商戶(hù)生成一對(duì)公私鑰(商公掷酗,商私),商戶(hù)會(huì)把公鑰給銀行窟哺;銀行也會(huì)生成一對(duì)公私鑰(銀公泻轰,銀私),銀行會(huì)把公鑰給商戶(hù)且轨。也就是說(shuō):商戶(hù)有銀行的公鑰浮声,自己的公鑰和私鑰。銀行有商戶(hù)的公鑰旋奢,自己的公鑰和私鑰
- 加密解密保證數(shù)據(jù)安全:
- 商戶(hù)使用自己公鑰加密泳挥,銀行沒(méi)有商戶(hù)私鑰解不開(kāi)報(bào)文,排除
- 商戶(hù)使用自己的私鑰加密黄绩,銀行使用商戶(hù)公鑰解密羡洁。理論上可行,然而會(huì)出現(xiàn)這種情況爽丹,商戶(hù)和銀行1筑煮,2辛蚊,3都使用相同的公私鑰,那么自己私鑰加密后發(fā)送給銀行1的報(bào)文真仲,被銀行2截取到也可以被解密開(kāi)袋马,違背了我們加密的目的--保證數(shù)據(jù)安全,排除秸应。
- 商戶(hù)使用銀行的公鑰加密虑凛,讓銀行用自己的私鑰解密。理論上可行软啼,然而會(huì)出現(xiàn)這種情況桑谍,銀行會(huì)和商戶(hù)A,B祸挪,C都使用相同的公私鑰锣披,那么商戶(hù)A和商戶(hù)B發(fā)送過(guò)去的報(bào)文,銀行都能解開(kāi)贿条,而且只有此銀行的私鑰可以解開(kāi)雹仿,達(dá)成了我們的目的。但是新的問(wèn)題出現(xiàn)了整以,這種情況假如商戶(hù)A模擬商戶(hù)B的報(bào)文把商戶(hù)B的錢(qián)轉(zhuǎn)移走該怎么辦胧辽?所以除了加密解密,還需要加簽驗(yàn)簽公黑。
- 加簽驗(yàn)簽證明身份:
- 加密已經(jīng)完成邑商,現(xiàn)在的問(wèn)題只有怎么讓銀行區(qū)分這筆請(qǐng)求是商戶(hù)A發(fā)的,還是商戶(hù)B發(fā)的帆调。想讓銀行區(qū)分出各個(gè)商戶(hù)奠骄,拿出各個(gè)商戶(hù)最獨(dú)特的私鑰加簽即可豆同,銀行拿出對(duì)應(yīng)的商戶(hù)公鑰驗(yàn)簽即可番刊。
- 到此,報(bào)文交互形成了一個(gè)穩(wěn)定且安全的循環(huán)
帶上代碼設(shè)計(jì)一套加解密
結(jié)構(gòu)
兩對(duì)SHA1withRSA公私鑰+DES會(huì)話(huà)密鑰影锈。結(jié)構(gòu)如下:
加密加簽步驟:
- 使用KeyGenerator隨機(jī)生成一個(gè)會(huì)話(huà)密鑰desKey
- 報(bào)文明文+會(huì)話(huà)密鑰明文desKey對(duì)稱(chēng)加密得到加密后的密文message芹务。
- 銀行的公鑰+會(huì)話(huà)密鑰desKey非對(duì)稱(chēng)加密得到加密后的會(huì)話(huà)密鑰key。
- 報(bào)文明文+商戶(hù)的私鑰非對(duì)稱(chēng)加密得到報(bào)文數(shù)字簽名sign鸭廷。
- 將sign和key和message傳遞給銀行枣抱。
解密驗(yàn)簽步驟:
- 加密后的會(huì)話(huà)密鑰key+銀行的私鑰解密得到會(huì)話(huà)密鑰明文desKey
- 對(duì)稱(chēng)加密得到的密文message+會(huì)話(huà)密鑰明文desKey解密得到報(bào)文明文
- 得到的明文+商戶(hù)的公鑰驗(yàn)簽,得到報(bào)文是否被中途篡改過(guò)
代碼
-
使用KeyGenerator隨機(jī)生成一個(gè)會(huì)話(huà)密鑰desKey
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES"); SecretKey secretKey = keyGenerator.generateKey(); return secretKey.getEncoded();
-
報(bào)文明文+會(huì)話(huà)密鑰明文desKey對(duì)稱(chēng)加密得到加密后的密文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); }
-
銀行的公鑰+會(huì)話(huà)密鑰desKey非對(duì)稱(chēng)加密得到加密后的會(huì)話(huà)密鑰key
public byte[] encryptRSA(byte[] plainBytes, boolean useBase64Code, String charset) throws Exception { String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; // 加密block需要預(yù)留11字節(jié) int KEYBIT = 2048; int RESERVEBYTES = 11; Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); int decryptBlock = KEYBIT / 8; // 256 bytes int encryptBlock = decryptBlock - RESERVEBYTES; // 245 bytes // 計(jì)算分段加密的block數(shù) (向上取整) int nBlock = (plainBytes.length / encryptBlock); if ((plainBytes.length % encryptBlock) != 0) { // 余數(shù)非0佳晶,block數(shù)再加1 nBlock += 1; } // 輸出buffer, 大小為nBlock個(gè)decryptBlock ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * decryptBlock); cipher.init(Cipher.ENCRYPT_MODE, peerPubKey); // 分段加密 for (int offset = 0; offset < plainBytes.length; offset += encryptBlock) { // block大小: encryptBlock 或 剩余字節(jié)數(shù) int inputLen = (plainBytes.length - offset); if (inputLen > encryptBlock) { inputLen = encryptBlock; } // 得到分段加密結(jié)果 byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen); // 追加結(jié)果到輸出buffer中 outbuf.write(encryptedBlock); } // 如果是Base64編碼,則返回Base64編碼后的數(shù)組 if (useBase64Code) { return Base64.encodeBase64String(outbuf.toByteArray()).getBytes( charset); } else { return outbuf.toByteArray(); // ciphertext } }
-
報(bào)文明文+商戶(hù)的私鑰非對(duì)稱(chēng)加密得到報(bào)文數(shù)字簽名sign讼载。
public byte[] signRSA(byte[] plainBytes, boolean useBase64Code, String charset) throws Exception { Signature signature = Signature.getInstance("SHA1withRSA"); signature.initSign(localPrivKey); signature.update(plainBytes); // 如果是Base64編碼的話(huà)轿秧,需要對(duì)簽名后的數(shù)組以Base64編碼 if (useBase64Code) { return Base64.encodeBase64String(signature.sign()).getBytes(charset); } else { return signature.sign(); } }
-
加密后的會(huì)話(huà)密鑰key+銀行的私鑰解密得到會(huì)話(huà)密鑰明文desKey中跌;對(duì)稱(chēng)加密得到的密文message+會(huì)話(huà)密鑰明文desKey解密得到報(bào)文明文
public byte[] decryptRSA(byte[] cryptedBytes, boolean useBase64Code, String charset) throws Exception { String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding"; // 加密block需要預(yù)留11字節(jié) byte[] data = null; // 如果是Base64編碼的話(huà),則要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 // 計(jì)算分段解密的block數(shù) (理論上應(yīng)該能整除) int nBlock = (data.length / decryptBlock); // 輸出buffer, , 大小為nBlock個(gè)encryptBlock ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * encryptBlock); cipher.init(Cipher.DECRYPT_MODE, localPrivKey); // 分段解密 for (int offset = 0; offset < data.length; offset += decryptBlock) { // block大小: decryptBlock 或 剩余字節(jié)數(shù) int inputLen = (data.length - offset); if (inputLen > decryptBlock) { inputLen = decryptBlock; } // 得到分段解密結(jié)果 byte[] decryptedBlock = cipher.doFinal(data, offset, inputLen); // 追加結(jié)果到輸出buffer中 outbuf.write(decryptedBlock); } outbuf.flush(); outbuf.close(); return outbuf.toByteArray(); }
-
得到的明文+商戶(hù)的公鑰驗(yàn)簽菇篡,得到報(bào)文是否被中途篡改過(guò)
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編碼的話(huà)漩符,需要對(duì)驗(yàn)簽的數(shù)組以Base64解碼 if (useBase64Code) { return signature.verify(Base64.decodeBase64(new String(signBytes, charset))); } else { return signature.verify(signBytes); } }
代碼只給出了一部分重要的加解密,加驗(yàn)簽邏輯驱还。還有一些邏輯都貼出來(lái)有點(diǎn)亂嗜暴,就放在倉(cāng)庫(kù)里了,具體用法查看README即可议蟆,更詳細(xì)的參考放在demo里
思考:為什么RSA公鑰加密的值一定只有私鑰才能解開(kāi)闷沥,不能暴力破解?咐容?
其實(shí)RSA的原理很簡(jiǎn)單狐赡,運(yùn)用了數(shù)學(xué)的一個(gè)難題:兩個(gè)大的質(zhì)數(shù)相乘,難以在短時(shí)間內(nèi)將其因式分解疟丙。原理很簡(jiǎn)單颖侄,但實(shí)際上操作真的很難。
時(shí)間復(fù)雜度--O
我們都知道計(jì)算機(jī)的計(jì)算速度非诚斫迹快览祖,計(jì)算幾十位數(shù)的加減法都是秒出。
然而炊琉,雖然計(jì)算機(jī)很快展蒂,但再快也是有上限的。
比如我電腦的CPU主頻是2.30GHz苔咪,也就是說(shuō)我的電腦每秒可以進(jìn)行2300000000次最基本的運(yùn)行锰悼。
計(jì)算機(jī)的計(jì)算能力有限,就算是超級(jí)計(jì)算機(jī)“天河二號(hào)”团赏,每秒也只能算3.39億億(這里多了個(gè)億 箕般,給大佬跪了orz)次。
對(duì)應(yīng)的舔清,我們有一個(gè)參數(shù)來(lái)衡量一個(gè)程序的耗時(shí)丝里,叫做時(shí)間復(fù)雜度:
多項(xiàng)式量級(jí) | 不嚴(yán)格的通俗例子(輸入規(guī)模 n=10^9) |
---|---|
常量階 O(1) | 只用1次運(yùn)算,普通電腦 10^-9秒就能算完。 |
對(duì)數(shù)階 O(log n) | 大約會(huì)用30次計(jì)算体谒,普通電腦 10^-8 秒算完 |
線(xiàn)性階 O(n) | 10^9 次計(jì)算杯聚,普通電腦需要一秒左右 |
線(xiàn)性對(duì)數(shù)階 O(n log n) | |
平方階 O(n^2) | 大約是 10^ 18次計(jì)算,普通電腦大概要30年抒痒。 |
非多項(xiàng)式量級(jí) | 不嚴(yán)格的通俗例子(輸入規(guī)模 n=10^9) |
---|---|
指數(shù)階 O(2^n) | 大約2^1000000000次計(jì)算幌绍,心態(tài)崩了 |
階乘階 O(n!) | 人類(lèi)所有電腦加在一起,等太陽(yáng)炸了都算不完 |
算法復(fù)雜度有各種各樣的,非多項(xiàng)式量級(jí)要比多項(xiàng)式量級(jí)耗費(fèi)多得多時(shí)間傀广,上述幾個(gè)復(fù)雜度的算法一個(gè)比一個(gè)慢痢虹。通俗的講,大O后面括號(hào)里面函數(shù)的增長(zhǎng)速度越快主儡,算法越耗時(shí)奖唯。
總的來(lái)說(shuō),RSA之所以理論上非常安全糜值,是因?yàn)槠平釸SA所要付出地計(jì)算成本遠(yuǎn)遠(yuǎn)高于使用RSA進(jìn)行加密的計(jì)算成本丰捷。
使用RSA的私鑰進(jìn)行解密,耗用的時(shí)間復(fù)雜度是多項(xiàng)式級(jí)寂汇。
不使用RSA私鑰病往,暴力破解,需要分解質(zhì)因數(shù)骄瓣,他的時(shí)間復(fù)雜度是非多項(xiàng)式級(jí)的指數(shù)級(jí)停巷。
也就是有私鑰解密只要一秒,暴力破解出結(jié)果時(shí)榕栏,人類(lèi)可能已經(jīng)毀滅了(不嚴(yán)格)畔勤。
RSA生成公私鑰數(shù)學(xué)計(jì)算流程:
商戶(hù)隨機(jī)生成了一些非常非常大的整數(shù),并用Miller-Rabin算法檢測(cè)它們是不是質(zhì)數(shù)扒磁,直到找到兩個(gè)大質(zhì)數(shù)——p1 和 p2 庆揪。(隨機(jī)數(shù)生成:多項(xiàng)式時(shí)間;Miller-Rabin: 多項(xiàng)式時(shí)間)
商戶(hù)計(jì)算兩個(gè)質(zhì)數(shù)的乘積 n = p1*p2(乘法: 多項(xiàng)式時(shí)間)
-
商戶(hù)計(jì)算 φ(n) = (p1 - 1)(p2 - 1) (乘法: 多項(xiàng)式時(shí)間)妨托,這一步難以被破解缸榛,因?yàn)閚太大了,分解質(zhì)因數(shù)需要指數(shù)級(jí)時(shí)間復(fù)雜度兰伤。人類(lèi)毀滅前是根據(jù)n推算出φ(n)可能性極小内颗。
-
歐拉函數(shù):φ(n)表示:小于n的正整數(shù)中與n互質(zhì)的數(shù)的數(shù)目。(互質(zhì)表示公因數(shù)為1)
比如想要知道φ(10)的話(huà)敦腔,我們就可以看[1, 10)中和10互質(zhì)的整數(shù)均澳,也就是1、3会烙、7负懦、9這四個(gè)數(shù)筒捺。(2柏腻、4、6系吭、8和10有公因數(shù)2五嫂,而5和10有公因數(shù)10)。所以φ(10)=4。
比如想要知道φ(21)的話(huà)沃缘,我們就可以看[1, 21)中和21互質(zhì)的整數(shù)躯枢,也就是1、2槐臀、4锄蹂、5、8水慨、10得糜、11、13晰洒、16朝抖、17、19谍珊、20這12個(gè)數(shù)治宣。(3、6砌滞、9侮邀、12、15贝润、18和21有公因數(shù)3豌拙,而7、14和21有公因數(shù)7)题暖。所以φ(21)=12按傅。
-
商戶(hù)構(gòu)造了一個(gè)比1大、比φ(n)小胧卤、不等于 p1 或 p2 的整數(shù)e唯绍。(隨機(jī)數(shù):多項(xiàng)式時(shí)間)
商戶(hù)求出了e對(duì)于φ(n)的乘法逆元d,也就是說(shuō)ed ≡ 1(mod φ(n))枝誊,也就是說(shuō)ed=kφ(n)+1 (擴(kuò)展歐幾里得况芒,多項(xiàng)式時(shí)間)
-
請(qǐng)注意!現(xiàn)在神奇的事情發(fā)生了叶撒!對(duì)于一個(gè)與n互質(zhì)的數(shù)a:
因?yàn)?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)
到這里祠够,兩把鑰匙構(gòu)造完成压汪!ㄟ(≧◇≦)ㄏ
公鑰:(n, e)
密鑰:(n, d)
RSA公私鑰加密解密
商戶(hù)想要生成一對(duì)公私鑰的時(shí)候:
- 首先隨意選擇兩個(gè)大的質(zhì)數(shù)p和q,p不等于q古瓤,計(jì)算N=pq止剖。
- 根據(jù)歐拉函數(shù)腺阳,求得r = (p-1)(q-1)
- 選擇一個(gè)小于 r 的整數(shù) e,求得 e 關(guān)于模 r 的模反元素穿香,命名為d亭引。(模反元素存在,當(dāng)且僅當(dāng)e與r互質(zhì))
- 將 p 和 q 的記錄銷(xiāo)毀皮获。
- (N,e)是公鑰焙蚓,(N,d)是私鑰。商戶(hù)將她的公鑰(N,e)傳給銀行洒宝,而將自己的私鑰(N,d)藏起來(lái)主届。
商戶(hù)進(jìn)行加密的時(shí)候:
-
假設(shè)商戶(hù)想給銀行送一個(gè)消息m,他知道銀行的公鑰待德,換句話(huà)說(shuō)是銀行公鑰的N和e君丁。他使用起先與銀行約好的格式將m轉(zhuǎn)換為一個(gè)小于N的整數(shù)n,比如他可以將每一個(gè)字轉(zhuǎn)換為這個(gè)字的Unicode碼将宪,然后將這些數(shù)字連在一起組成一個(gè)數(shù)字绘闷。假如他的信息非常長(zhǎng)的話(huà),他可以將這個(gè)信息分為幾段较坛,然后將每一段轉(zhuǎn)換為n印蔗。用下面這個(gè)公式他可以將n加密為c:
? ne ≡ c (mod N)
計(jì)算c并不復(fù)雜。商戶(hù)算出c后就可以將它傳遞給銀行丑勤,也就是密文啦华嘹。
銀行想要解密的時(shí)候:
- 銀行得到商戶(hù)的密文消息c(商戶(hù)使用銀行公鑰加密后的密文)后就可以利用他的私鑰d來(lái)解碼。他可以用以下這個(gè)公式來(lái)將c轉(zhuǎn)換為n:
cd ≡ n (mod N)
得到n后法竞,他可以將原來(lái)的信息m重新復(fù)原耙厚。
其他的概念
素?cái)?shù)
素?cái)?shù)又稱(chēng)質(zhì)數(shù),指在一個(gè)大于1的自然數(shù)中岔霸,除了1和此整數(shù)自身外薛躬,不能被其他自然數(shù)整除的數(shù)
互質(zhì)數(shù)
互質(zhì),又稱(chēng)互素呆细。若N個(gè)整數(shù)的最大公因子是1型宝,則稱(chēng)這N個(gè)整數(shù)互質(zhì)。
指數(shù)運(yùn)算
指數(shù)運(yùn)算又稱(chēng)乘方計(jì)算絮爷,計(jì)算結(jié)果稱(chēng)為冪趴酣。nm指將n自乘m次。把nm看作乘方的結(jié)果坑夯,叫做”n的m次冪”或”n的m次方”岖寞。其中,n稱(chēng)為“底數(shù)”渊涝,m稱(chēng)為“指數(shù)”慎璧。
模運(yùn)算
模運(yùn)算即求余運(yùn)算床嫌。
同余
當(dāng)兩個(gè)整數(shù)除以同一個(gè)正整數(shù)跨释,若得相同余數(shù)胸私,則二整數(shù)同余。
會(huì)話(huà)密鑰
前提:對(duì)稱(chēng)加密速度要比非對(duì)稱(chēng)加密快速鳖谈。會(huì)話(huà)密鑰是一個(gè)隨機(jī)生成的對(duì)稱(chēng)式加密密鑰岁疼,舉個(gè)例子:A和B交互,A隨機(jī)挑了一個(gè)字符串缆娃,用B的公鑰加密發(fā)給了B捷绒,告訴B這個(gè)隨機(jī)字符串就是他們之間用來(lái)交流的密鑰了,之后A和B的報(bào)文就可以不用公私鑰非對(duì)稱(chēng)加密贯要,直接用這個(gè)密鑰對(duì)稱(chēng)加密即可暖侨。對(duì)稱(chēng)式加密算法有很多:AES/DES等。SSH通信的數(shù)據(jù)就是用AES之類(lèi)的對(duì)稱(chēng)式加密算法加密的崇渗。(在SSH協(xié)商密鑰的過(guò)程中字逗,還會(huì)使用專(zhuān)門(mén)的密鑰協(xié)商算法(Key Exchange Algorithm),確保竊聽(tīng)者無(wú)法偷聽(tīng)到密鑰的內(nèi)容)
中間人攻擊
即當(dāng)商戶(hù)發(fā)送公鑰給銀行的時(shí)候宅广,黑客截取了商戶(hù)的公鑰葫掉,同時(shí)把自己公鑰發(fā)給銀行,這樣一直在與銀行通信的并不是商戶(hù)跟狱。
CA認(rèn)證中心
專(zhuān)門(mén)提供網(wǎng)絡(luò)身份認(rèn)證服務(wù)的機(jī)構(gòu)或團(tuán)體
總結(jié)
數(shù)學(xué)的魅力在于將這個(gè)世界變得井井有條俭厚,試想當(dāng)計(jì)算機(jī)的運(yùn)行速度越來(lái)越快,RSA會(huì)被破解嗎驶臊?不見(jiàn)得挪挤,1999年N(兩個(gè)大質(zhì)數(shù)的乘積)位數(shù)是512,后面發(fā)展成了位數(shù)是1024和2048位关翎,計(jì)算機(jī)速度變快之后电禀,每臺(tái)電腦能處理的位數(shù)也會(huì)越來(lái)越大,我相信我們會(huì)見(jiàn)到更長(zhǎng)位數(shù)的N笤休,十萬(wàn)尖飞,甚至百萬(wàn)....
浩瀚世界,自己真渺小