數(shù)據(jù)傳輸加密:RSA+AES

轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/chay_chan/article/details/58605605

數(shù)據(jù)傳輸加密

在開發(fā)應(yīng)用過(guò)程中,客戶端與服務(wù)端經(jīng)常需要進(jìn)行數(shù)據(jù)傳輸,涉及到重要隱私信息時(shí),開發(fā)者自然會(huì)想到對(duì)其進(jìn)行加密,即使傳輸過(guò)程中被“有心人”截取,也不會(huì)將信息泄露。對(duì)于加密算法,相信不少開發(fā)者也有所耳聞,比如MD5加密,Base64加密,DES加密,AES加密,RSA加密等等。在這里我主要向大家介紹一下我在開發(fā)過(guò)程中使用到的加密算法,RSA加密算法+AES加密算法。簡(jiǎn)單地介紹一下這兩種算法吧。

RSA

之所以叫RSA算法,是因?yàn)樗惴ǖ娜话l(fā)明者RSA是目前最有影響力的公鑰加密算法,它能夠抵抗到目前為止已知的絕大多數(shù)密碼攻擊,已被ISO推薦為公鑰數(shù)據(jù)加密標(biāo)準(zhǔn),主要的算法原理就不多加介紹,如果對(duì)此感興趣的話,建議去百度一下RSA算法。需要了解的是RSA算法屬于非對(duì)稱加密算法,非對(duì)稱加密算法需要兩個(gè)密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對(duì),如果用公開密鑰對(duì)數(shù)據(jù)進(jìn)行加密,只有用對(duì)應(yīng)的私有密鑰才能解密;如果用私有密鑰對(duì)數(shù)據(jù)進(jìn)行加密,那么只有用對(duì)應(yīng)的公開密鑰才能解密。因?yàn)榧用芎徒饷苁褂玫氖莾蓚€(gè)不同的密鑰,所以這種算法叫作非對(duì)稱加密算法。簡(jiǎn)單的說(shuō)是“公鑰加密,私鑰解密;私鑰加密,公鑰解密”。

AES

高級(jí)加密標(biāo)準(zhǔn)(英語(yǔ):Advanced Encryption Standard,縮寫:AES),在密碼學(xué)中又稱Rijndael加密法,是美國(guó)聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)。這個(gè)標(biāo)準(zhǔn)用來(lái)替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用。經(jīng)過(guò)五年的甄選流程,高級(jí)加密標(biāo)準(zhǔn)由美國(guó)國(guó)家標(biāo)準(zhǔn)與技術(shù)研究院(NIST)于2001年11月26日發(fā)布于FIPS PUB 197,并在2002年5月26日成為有效的標(biāo)準(zhǔn)。2006年,高級(jí)加密標(biāo)準(zhǔn)已然成為對(duì)稱密鑰加密中最流行的算法之一。

為什么要結(jié)合使用這兩種算法

如果不清楚非對(duì)稱算法和對(duì)稱算法,也許你會(huì)問(wèn),為什么要結(jié)合使用這兩種算法,單純使用一種算法不行嗎?這就要結(jié)合不同的場(chǎng)景和需求了。

客戶端傳輸重要信息給服務(wù)端,服務(wù)端返回的信息不需加密的情況

客戶端傳輸重要信息給服務(wù)端,服務(wù)端返回的信息不需加密,例如綁定銀行卡的時(shí)候,需要傳遞用戶的銀行卡號(hào),手機(jī)號(hào)等重要信息,客戶端這邊就需要對(duì)這些重要信息進(jìn)行加密,使用RSA公鑰加密,服務(wù)端使用RSA解密,然后返回一些普通信息,比如狀態(tài)碼code,提示信息msg,提示操作是成功還是失敗。這種場(chǎng)景下,僅僅使用RSA加密是可以的。

客戶端傳輸重要信息給服務(wù)端,服務(wù)端返回的信息需加密的情況

客戶端傳輸重要信息給服務(wù)端,服務(wù)端返回的信息需加密,例如客戶端登錄的時(shí)候,傳遞用戶名和密碼等資料,需要進(jìn)行加密,服務(wù)端驗(yàn)證登錄信息后,返回令牌token需要進(jìn)行加密,客戶端解密后保存。此時(shí)就需要結(jié)合這兩種算法了。至于整個(gè)流程是怎樣的,在下面會(huì)慢慢通過(guò)例子向你介紹,因?yàn)槿绻婚_始就這么多文字類的操作,可能會(huì)讓讀者感到一頭霧水。

使用RSA加密和解密

產(chǎn)生公鑰和私鑰

產(chǎn)生RSA公鑰和密鑰的方法有很多,在這里我直接使用我封裝好的方法產(chǎn)生,都最后我會(huì)將兩個(gè)算法的工具類贈(zèng)送給大家。

/**
 * 生成公鑰和私鑰
 * 
 * @throws Exception
 * 
 */
public static void getKeys() throws Exception {
    KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
    keyPairGen.initialize(1024);
    KeyPair keyPair = keyPairGen.generateKeyPair();
    RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
    RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

    String publicKeyStr = getPublicKeyStr(publicKey);
    String privateKeyStr = getPrivateKeyStr(privateKey);

    System.out.println("公鑰\r\n" + publicKeyStr);
    System.out.println("私鑰\r\n" + privateKeyStr);
}

public static String getPrivateKeyStr(PrivateKey privateKey)
        throws Exception {
    return new String(Base64Utils.encode(privateKey.getEncoded()));
}

public static String getPublicKeyStr(PublicKey publicKey) throws Exception {
    return new String(Base64Utils.encode(publicKey.getEncoded()));
}

公鑰

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCRQZ5O/AOAjeYAaSFf6Rjhqovws78I716I9oGF7WxCIPmcaUa1YuyLOncCCuPsaw69+RMWjdbOBp8hd4PPM/d4mKTOVEYUE0SfxhhDTZaM5CzQEUXUyXy7icQTGR5wBjrbjU1yHCKOf5PJJZZQWB06husSFZ40TdL7FdlBpZ1u1QIDAQAB

私鑰

MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJFBnk78A4CN5gBpIV/pGOGqi/CzvwjvXoj2gYXtbEIg+ZxpRrVi7Is6dwIK4+xrDr35ExaN1s4GnyF3g88z93iYpM5URhQTRJ/GGENNlozkLNARRdTJfLuJxBMZHnAGOtuNTXIcIo5/k8klllBYHTqG6xIVnjRN0vsV2UGlnW7VAgMBAAECgYBMoT9xD8aRNUrXgJ7YyFIWCzEUZN8tSYqn2tPt4ZkxMdA9UdS5sFx1/vv1meUwPjJiylnlliJyQlAFCdYBo7qzmib8+3Q8EU3MDP9bNlpxxC1go57/q/TbaymWyOk3pK2VXaX+8vQmllgRZMQRi2JFBHVoep1f1x7lSsf2TpipgQJBANJlO+UDmync9X/1YdrVaDOi4o7g3w9u1eVq9B01+WklAP3bvxIoBRI97HlDPKHx+CZXeODx1xj0xPOK3HUz5FECQQCwvdagPPtWHhHx0boPF/s4ZrTUIH04afuePUuwKTQQRijnl0eb2idBe0z2VAH1utPps/p4SpuT3HI3PJJ8MlVFAkAFypuXdj3zLQ3k89A5wd4Ybcdmv3HkbtyccBFALJgs+MPKOR5NVaSuF95GiD9HBe4awBWnu4B8Q2CYg54F6+PBAkBKNgvukGyARnQGc6eKOumTTxzSjSnHDElIsjgbqdFgm/UE+TJqMHmXNyyjqbaA9YeRc67R35HfzgpvQxHG8GN5AkEAxSKOlfACUCQ/CZJovETMmaUDas463hbrUznp71uRMk8RP7DY/lBnGGMeUeeZLIVK5X2Ngcp9nJQSKWCGtpnfLQ==

很明顯,公鑰字符串長(zhǎng)度比較短,私鑰的比較長(zhǎng)。生成完密鑰后,公鑰可以存放在客戶端,即使被別人知道公鑰,也是沒(méi)有問(wèn)題的;私鑰則一定要保存在服務(wù)端。如果到時(shí)公司面臨人事變動(dòng),避免私鑰被離職人員泄露,可以重新生成公鑰和密鑰。

使用公鑰加密,私鑰解密
image

這里在客戶端模擬加密的情況,對(duì)字符串”Beyond黃家駒”使用RSA加密,調(diào)用RSAUtils的encryptByPublicKey()方法,輸出結(jié)果為:

密文: BRFjf3tUqRqlwuP5JtzxZinf7lp+AHuHM9JSabM5BNFDxuUe9+uuO6RpCHVH5PibifqQHzGNsyZn1G9QcIENT9Tbm+PZwAbNUlMPZRDBU1FSnOtY8dBdeW/lJdnY9sJVwNvIBnOLQk66hxRh6R2149dwlgdsGUpWMOMBzcP3vsU=

在服務(wù)端,可以使用RSAUtils的decryptByPrivateKey()方法進(jìn)行解密,現(xiàn)在模擬服務(wù)端解密

image

在這里雖然沒(méi)有完全模擬數(shù)據(jù)傳輸過(guò)程,比如說(shuō)客戶端發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求,傳遞參數(shù)給服務(wù)端,服務(wù)端接收參數(shù)并進(jìn)行處理,也是為了讓大家可以更加容易明白,所以這里只是進(jìn)行簡(jiǎn)單的模擬??梢钥吹絘ndroid客戶端端和java服務(wù)端的RSA加密解密算法是可以互通的,原因是他們所使用到的base64加密類是一致的,所以才可以實(shí)現(xiàn)加密和解密的算法互通。

image
image

使用到的jar包都是javabase64-1.3.1.jar,相信不少人都知道,java中有自帶的Base64算法類,但是安卓中卻沒(méi)有,之前出現(xiàn)的情況是,使用的Base64類不統(tǒng)一,比如在安卓客戶端開發(fā)使用的Base64算法是使用第三方提供的jar包,而java服務(wù)端中使用的是JDK自帶的Base64,導(dǎo)致從客戶端傳過(guò)來(lái)的密文,服務(wù)端解析出錯(cuò)。

上面的例子展示了客戶端使用公鑰加密,服務(wù)端使用私鑰解密的過(guò)程。也許你會(huì)這么想,既然可以如此,那服務(wù)端那邊信息也可以通過(guò)RSA加密后,傳遞加密信息過(guò)來(lái),客戶端進(jìn)行解密。但是,這樣做,顯示是不安全的。原因是,由于客戶端并沒(méi)有保存私鑰,只有公鑰,只可以服務(wù)端進(jìn)行私鑰加密,客戶端進(jìn)行公鑰解密,但由于公鑰是公開,別人也可以獲取到公鑰,如果信息被他們截取,他們同樣可以通過(guò)公鑰進(jìn)行解密,那么這樣子加密,就毫無(wú)意義了,所以這個(gè)時(shí)候,就要結(jié)合對(duì)稱算法,實(shí)現(xiàn)客戶端與服務(wù)端之前的安全通信了。

使用AES加密解密
加密
image

模擬客戶端進(jìn)行AES加密,我們通過(guò)調(diào)用AESUtils中的generateKey()方法,隨機(jī)產(chǎn)生一個(gè)密鑰,用于對(duì)數(shù)據(jù)進(jìn)行加密。輸出的結(jié)果為:

密鑰: 6446c69c0f914a57
密文: GECDQOsc22yV48hdJENTMg==

解密

模擬服務(wù)端進(jìn)行AES解密,由于AES屬于對(duì)稱算法,加密和解密需要使用同一把密鑰,所以,服務(wù)端要解密傳遞過(guò)來(lái)的內(nèi)容,就需要密鑰 + 密文。這里模擬一下服務(wù)端解密。

image

到這里也許你會(huì)問(wèn),客戶端使用AES進(jìn)行加密,服務(wù)端要進(jìn)行解密的話,需要用到產(chǎn)生的密鑰,那密鑰必須從客戶端傳輸?shù)椒?wù)端,如果不對(duì)密鑰進(jìn)行加密,那加密就沒(méi)有意義了。所以這里終于談到了重點(diǎn),RSA算法+AES算法結(jié)合使用。

RSA算法+AES算法的使用

舉一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明一下吧,例如實(shí)名認(rèn)證功能,需要傳遞用戶真實(shí)姓名和身份證號(hào),對(duì)于這種重要信息,需要進(jìn)行加密處理。

客戶端使用RSA + AES對(duì)重要信息進(jìn)行加密

客戶端加密過(guò)程主要分為以下三個(gè)步驟:

1.客戶端隨機(jī)產(chǎn)生AES的密鑰;

2.對(duì)身份證信息(重要信息)進(jìn)行AES加密;

3.通過(guò)使用RSA對(duì)AES密鑰進(jìn)行公鑰加密。

image

這樣在傳輸?shù)倪^(guò)程中,即時(shí)加密后的AES密鑰被別人截取,對(duì)其也無(wú)濟(jì)于事,因?yàn)樗⒉恢繰SA的私鑰,無(wú)法解密得到原本的AES密鑰,就無(wú)法解密用AES加密后的重要信息。

服務(wù)端使用RSA + AES對(duì)重要信息進(jìn)行解密

服務(wù)端解密過(guò)程主要分為以下兩個(gè)步驟:

1.對(duì)加密后的AES密鑰進(jìn)行RSA私鑰解密,拿到密鑰原文;

2.對(duì)加密后的重要信息進(jìn)行AES解密,拿到原始內(nèi)容。

image

現(xiàn)實(shí)開發(fā)中,服務(wù)端有時(shí)也需要向客戶端傳遞重要信息,比如登錄的時(shí)候,返回token給客戶端,作為令牌,這個(gè)令牌就需要進(jìn)行加密,原理也是差不多的,比上面多一個(gè)步驟而已,就是將解密后的AES密鑰,對(duì)將要傳遞給客戶端的數(shù)據(jù)token進(jìn)行AES加密,返回給客戶端,由于客戶端和服務(wù)端都已經(jīng)拿到同一把AES鑰匙,所以客戶端可以解密服務(wù)端返回的加密后的數(shù)據(jù)。如果客戶端想要將令牌進(jìn)行保存,則需要使用自己定義的默認(rèn)的AES密鑰進(jìn)行加密后保存,需要使用的時(shí)候傳入默認(rèn)密鑰和密文,解密后得到原token。

上面提及到客戶端加密,服務(wù)端返回?cái)?shù)據(jù)不加密的情況,上面說(shuō)到僅僅使用RSA是可以,但是還是建議同時(shí)使用這兩種算法,即產(chǎn)生一個(gè)AES密鑰,使用RSA對(duì)該密鑰進(jìn)行公鑰加密,對(duì)重要信息進(jìn)行AES加密,服務(wù)端通過(guò)RSA私鑰解密拿到AES密鑰,再對(duì)加密后的重要信息進(jìn)行解密。如果僅僅使用RSA,服務(wù)端只通過(guò)RSA解密,這樣會(huì)對(duì)于性能會(huì)有所影響,原因是RSA的解密耗時(shí)約等于AES解密數(shù)據(jù)的100倍,所以如果每個(gè)重要信息都只通過(guò)RSA加密和解密,則會(huì)影響服務(wù)端系統(tǒng)的性能,所以建議兩種算法一起使用。

同時(shí)還有相應(yīng)的JS版RSA和AES算法,使用方式也差不多,在這里簡(jiǎn)單演示一下:

下面是一個(gè)html頁(yè)面的代碼,引入了rsa.js和aes.js

<!DOCTYPE html>
<html>
  <head>
    <title>RSA+AES.html</title>

    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="./js/rsa.js"></script>
    <script type="text/javascript" src="./js/aes.js"></script>
    <script type="text/javascript">
        var key = getKey();//隨機(jī)產(chǎn)生AES密鑰
        var encryptKey = RSA(key);//對(duì)AES密鑰進(jìn)行RSA加密
        console.log("encryptKey: " + encryptKey);

        //測(cè)試AES加密和解密
        var cipherText = AESEnc(key,"123456");
        var plainText = AESDec(key,cipherText);
        console.log("密文: " + cipherText);
        console.log("明文: " + plainText);
    </script>
  </head>

  <body>
    This is my HTML page. <br>
  </body>
</html>

打開頁(yè)面后,查看控制臺(tái)輸出:

image

同時(shí),模擬服務(wù)端解密,運(yùn)行結(jié)果如下:

image

在這里將我自己封裝的RSAUtils、AESUtils以及使用第三方j(luò)ar包的Base64Utils還有JS版的RSAHE AES分享給大家,希望可以幫助到大家,由于剛注冊(cè)博客不久,沒(méi)有多少積分,下載一些資料的時(shí)候需要積分,所以收取大家1積分,謝謝了。

http://download.csdn.net/detail/chay_chan/9766486

需要注意的是:

1.RSAUtils中配置公鑰和密鑰,可以使用getKeys()方法產(chǎn)生。如果是客戶端,則無(wú)須配置私鑰,把沒(méi)有私鑰的RSAUtils放到客戶端,因?yàn)閮H需要用到公鑰加密的方法。

2.AESUtils中配置偏移量IV_STRING;

3.rsa.js中最底部配置公鑰,須和上面RSAUtils配置的公鑰一致;

4.aes.js中的底部var iv = CryptoJS.enc.Utf8.parse(“16-Bytes–String”); //加密向量中,替換里面的字符串,加密向量須和
是上面的AESUtils中的偏移量一致。

為了完成這篇博客,花費(fèi)了接近半天的時(shí)間,相當(dāng)于總結(jié)自己在數(shù)據(jù)傳輸這一方面的經(jīng)驗(yàn),希望可以幫助到更多的開發(fā)者,一起交流學(xué)習(xí),互相提升和進(jìn)步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容