天天看點

資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

本篇文章已授權微信公衆号 guolin_blog (郭霖)獨家釋出

轉載請注明出處:http://blog.csdn.net/chay_chan/article/details/58605605

資料傳輸加密

  在開發應用過程中,用戶端與服務端經常需要進行資料傳輸,涉及到重要隐私資訊時,開發者自然會想到對其進行加密,即使傳輸過程中被“有心人”截取,也不會将資訊洩露。對于加密算法,相信不少開發者也有所耳聞,比如MD5加密,Base64加密,DES加密,AES加密,RSA加密等等。在這裡我主要向大家介紹一下我在開發過程中使用到的加密算法,RSA加密算法+AES加密算法。簡單地介紹一下這兩種算法吧。

RSA

  之是以叫RSA算法,是因為算法的三位發明者RSA是目前最有影響力的公鑰加密算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰資料加密标準,主要的算法原理就不多加介紹,如果對此感興趣的話,建議去百度一下RSA算法。需要了解的是RSA算法屬于非對稱加密算法,非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對,如果用公開密鑰對資料進行加密,隻有用對應的私有密鑰才能解密;如果用私有密鑰對資料進行加密,那麼隻有用對應的公開密鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,是以這種算法叫作非對稱加密算法。簡單的說是“公鑰加密,私鑰解密;私鑰加密,公鑰解密”。

AES

  進階加密标準(英語:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法,是美國聯邦政府采用的一種區塊加密标準。這個标準用來替代原先的DES,已經被多方分析且廣為全世界所使用。經過五年的甄選流程,進階加密标準由美國國家标準與技術研究院(NIST)于2001年11月26日釋出于FIPS PUB 197,并在2002年5月26日成為有效的标準。2006年,進階加密标準已然成為對稱密鑰加密中最流行的算法之一。

為什麼要結合使用這兩種算法

  如果不清楚非對稱算法和對稱算法,也許你會問,為什麼要結合使用這兩種算法,單純使用一種算法不行嗎?這就要結合不同的場景和需求了。

用戶端傳輸重要資訊給服務端,服務端傳回的資訊不需加密的情況

  用戶端傳輸重要資訊給服務端,服務端傳回的資訊不需加密,例如綁定銀行卡的時候,需要傳遞使用者的銀行卡号,手機号等重要資訊,用戶端這邊就需要對這些重要資訊進行加密,使用RSA公鑰加密,服務端使用RSA解密,然後傳回一些普通資訊,比如狀态碼code,提示資訊msg,提示操作是成功還是失敗。這種場景下,僅僅使用RSA加密是可以的。

用戶端傳輸重要資訊給服務端,服務端傳回的資訊需加密的情況

  用戶端傳輸重要資訊給服務端,服務端傳回的資訊需加密,例如用戶端登入的時候,傳遞使用者名和密碼等資料,需要進行加密,服務端驗證登入資訊後,傳回令牌token需要進行加密,用戶端解密後儲存。此時就需要結合這兩種算法了。至于整個流程是怎樣的,在下面會慢慢通過例子向你介紹,因為如果一開始就這麼多文字類的操作,可能會讓讀者感到一頭霧水。

使用RSA加密和解密

産生公鑰和私鑰

  産生RSA公鑰和密鑰的方法有很多,在這裡我直接使用我封裝好的方法産生,都最後我會将兩個算法的工具類贈送給大家。

/**
 * 生成公鑰和私鑰
 * 
 * @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==
           

  很明顯,公鑰字元串長度比較短,私鑰的比較長。生成完密鑰後,公鑰可以存放在用戶端,即使被别人知道公鑰,也是沒有問題的;私鑰則一定要儲存在服務端。如果到時公司面臨人事變動,避免私鑰被離職人員洩露,可以重新生成公鑰和密鑰。

使用公鑰加密,私鑰解密
資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

  這裡在用戶端模拟加密的情況,對字元串"Beyond黃家駒"使用RSA加密,調用RSAUtils的encryptByPublicKey()方法,輸出結果為:

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

  在服務端,可以使用RSAUtils的decryptByPrivateKey()方法進行解密,現在模拟服務端解密

資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

  在這裡雖然沒有完全模拟資料傳輸過程,比如說用戶端發起一個網絡請求,傳遞參數給服務端,服務端接收參數并進行處理,也是為了讓大家可以更加容易明白,是以這裡隻是進行簡單的模拟。可以看到android用戶端端和java服務端的RSA加密解密算法是可以互通的,原因是他們所使用到的base64加密類是一緻的,是以才可以實作加密和解密的算法互通。

資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)
資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

  使用到的jar包都是javabase64-1.3.1.jar,相信不少人都知道,java中有自帶的Base64算法類,但是安卓中卻沒有,之前出現的情況是,使用的Base64類不統一,比如在安卓用戶端開發使用的Base64算法是使用第三方提供的jar包,而java服務端中使用的是JDK自帶的Base64,導緻從用戶端傳過來的密文,服務端解析出錯。

  上面的例子展示了用戶端使用公鑰加密,服務端使用私鑰解密的過程。也許你會這麼想,既然可以如此,那服務端那邊資訊也可以通過RSA加密後,傳遞加密資訊過來,用戶端進行解密。但是,這樣做,顯示是不安全的。原因是,由于用戶端并沒有儲存私鑰,隻有公鑰,隻可以服務端進行私鑰加密,用戶端進行公鑰解密,但由于公鑰是公開,别人也可以擷取到公鑰,如果資訊被他們截取,他們同樣可以通過公鑰進行解密,那麼這樣子加密,就毫無意義了,是以這個時候,就要結合對稱算法,實作用戶端與服務端之前的安全通信了。

使用AES加密解密
加密
資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

  模拟用戶端進行AES加密,我們通過調用AESUtils中的generateKey()方法,随機産生一個密鑰,用于對資料進行加密。輸出的結果為:

密鑰: 6446c69c0f914a57
密文: GECDQOsc22yV48hdJENTMg==
           
解密

  模拟服務端進行AES解密,由于AES屬于對稱算法,加密和解密需要使用同一把密鑰,是以,服務端要解密傳遞過來的内容,就需要密鑰 + 密文。這裡模拟一下服務端解密。

資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

  到這裡也許你會問,用戶端使用AES進行加密,服務端要進行解密的話,需要用到産生的密鑰,那密鑰必須從用戶端傳輸到服務端,如果不對密鑰進行加密,那加密就沒有意義了。是以這裡終于談到了重點,RSA算法+AES算法結合使用。

RSA算法+AES算法的使用

  舉一個簡單的例子來說明一下吧,例如實名認證功能,需要傳遞使用者真實姓名和身份證号,對于這種重要資訊,需要進行加密處理。

用戶端使用RSA + AES對重要資訊進行加密

用戶端加密過程主要分為以下三個步驟:

1.用戶端随機産生AES的密鑰;

2.對身份證資訊(重要資訊)進行AES加密;

3.通過使用RSA對AES密鑰進行公鑰加密。

資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

  這樣在傳輸的過程中,即時加密後的AES密鑰被别人截取,對其也無濟于事,因為他并不知道RSA的私鑰,無法解密得到原本的AES密鑰,就無法解密用AES加密後的重要資訊。

服務端使用RSA + AES對重要資訊進行解密

服務端解密過程主要分為以下兩個步驟:

1.對加密後的AES密鑰進行RSA私鑰解密,拿到密鑰原文;

2.對加密後的重要資訊進行AES解密,拿到原始内容。

資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

  現實開發中,服務端有時也需要向用戶端傳遞重要資訊,比如登入的時候,傳回token給用戶端,作為令牌,這個令牌就需要進行加密,原理也是差不多的,比上面多一個步驟而已,就是将解密後的AES密鑰,對将要傳遞給用戶端的資料token進行AES加密,傳回給用戶端,由于用戶端和服務端都已經拿到同一把AES鑰匙,是以用戶端可以解密服務端傳回的加密後的資料。如果用戶端想要将令牌進行儲存,則需要使用自己定義的預設的AES密鑰進行加密後儲存,需要使用的時候傳入預設密鑰和密文,解密後得到原token。

  上面提及到用戶端加密,服務端傳回資料不加密的情況,上面說到僅僅使用RSA是可以,但是還是建議同時使用這兩種算法,即産生一個AES密鑰,使用RSA對該密鑰進行公鑰加密,對重要資訊進行AES加密,服務端通過RSA私鑰解密拿到AES密鑰,再對加密後的重要資訊進行解密。如果僅僅使用RSA,服務端隻通過RSA解密,這樣會對于性能會有所影響,原因是RSA的解密耗時約等于AES解密資料的100倍,是以如果每個重要資訊都隻通過RSA加密和解密,則會影響服務端系統的性能,是以建議兩種算法一起使用。

同時還有相應的JS版RSA和AES算法,使用方式也差不多,在這裡簡單示範一下:

下面是一個html頁面的代碼,引入了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();//随機産生AES密鑰
        var encryptKey = RSA(key);//對AES密鑰進行RSA加密
        console.log("encryptKey: " + encryptKey);
        
        //測試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>
           

打開頁面後,檢視控制台輸出:

資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

同時,模拟服務端解密,運作結果如下:

資料傳輸加密——非對稱加密算法RSA+對稱算法AES(适用于java,android和Web)

  在這裡将我自己封裝的RSAUtils、AESUtils以及使用第三方jar包的Base64Utils還有JS版的RSAHE AES分享給大家,希望可以幫助到大家,由于剛注冊部落格不久,沒有多少積分,下載下傳一些資料的時候需要積分,是以收取大家1積分,謝謝了。

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

需要注意的是:

1.RSAUtils中配置公鑰和密鑰,可以使用getKeys()方法産生。如果是用戶端,則無須配置私鑰,把沒有私鑰的RSAUtils放到用戶端,因為僅需要用到公鑰加密的方法。

2.AESUtils中配置偏移量IV_STRING;

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

4.aes.js中的底部var iv = CryptoJS.enc.Utf8.parse(“16-Bytes–String”); //加密向量中,替換裡面的字元串,加密向量須和

是上面的AESUtils中的偏移量一緻。

  為了完成這篇部落格,花費了接近半天的時間,相當于總結自己在資料傳輸這一方面的經驗,希望可以幫助到更多的開發者,一起交流學習,互相提升和進步。