當(dāng)柳上原的風(fēng)吹向天際的時(shí)候...

          真正的快樂(lè)來(lái)源于創(chuàng)造

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks
          按:下面的文字涉及早已在工程中廣泛采用的混合加密方式,對(duì)此熟知者就不用往下看了,以免浪費(fèi)時(shí)間。

          我們知道,現(xiàn)代加密方式有兩大類(lèi):一類(lèi)是對(duì)稱(chēng)加密方式,其優(yōu)點(diǎn)是加密速度快,缺點(diǎn)是密鑰不便于傳遞,其中典型代表是AES;一類(lèi)是非對(duì)稱(chēng)加密方式,優(yōu)點(diǎn)是交換鑰匙方便,缺點(diǎn)是加密時(shí)間長(zhǎng),代表是RSA。在實(shí)際應(yīng)用,我們可以取其所長(zhǎng),棄其所短,這就是混合加密方式,有的場(chǎng)合也成為Hybrid方式。

          具體來(lái)說(shuō)混合加密方式的工作過(guò)程大體是這樣:首先,客戶(hù)端將明文用本地的AES鑰匙加密,然后從服務(wù)器端得到服務(wù)器端的RSA公鑰,用它來(lái)對(duì)本地的AES鑰匙加密,然后把兩端密文拼合在一起送給服務(wù)器端;服務(wù)器端得到密文后,將其拆分成密鑰文和密文兩段,然后,用本地的RSA私鑰對(duì)密鑰文進(jìn)行解密,得到加密密文的AES鑰匙,然后用AES鑰匙對(duì)密文解密,得到明文。在此過(guò)程中,對(duì)明文加密和對(duì)密文解密都采用了對(duì)稱(chēng)加密解密方式,速度快,且都在服務(wù)器客戶(hù)機(jī)的一側(cè)進(jìn)行,沒(méi)有通過(guò)網(wǎng)絡(luò)傳輸,安全性高;而網(wǎng)絡(luò)傳輸?shù)氖欠?wù)器的RSA公鑰和經(jīng)其加密的AES鑰匙,即使被截獲也沒(méi)有什么好擔(dān)心的。如果要雙向傳遞則把這個(gè)過(guò)程反過(guò)來(lái)就可以了。

          以上過(guò)程的示意UML SEQUENCE圖如下:



          下面用代碼來(lái)輔助說(shuō)明一下。

          客戶(hù)端進(jìn)行加密并傳輸密文到服務(wù)器端的代碼,其中,服務(wù)器端的RSA公鑰已經(jīng)用別的方法得到了,下面serverPublicKey變量就存儲(chǔ)了它:
                  Socket s=new Socket("127.0.0.1",8888);
                  
                  InputStream  inStram
          =s.getInputStream();
                  OutputStream outStream
          =s.getOutputStream();
                  
                  
          // 輸出
                  PrintWriter out=new PrintWriter(outStream,true);
                  
                  
          // 待加密的明文
                  StringBuilder sb1=new StringBuilder();
                  sb1.append(
          "<request>");
                  sb1.append(
          "<command>register</command>");
                  sb1.append(
          "<username>何楊</username>");
                  sb1.append(
          "<password>123456</password>");
                  sb1.append(
          "</request>");
                  String plainText
          =sb1.toString();
                  
                  
          // 對(duì)明文進(jìn)行AES加密
                  byte[] aesArr=aesCoder.getEncryptByteArray(plainText); // 對(duì)明文進(jìn)行AES加密
                  String cipherText=Base64.encodeBase64String(aesArr);// 得到AES加密后的密文
                  
                  
          // 使用RSA對(duì)AES密鑰進(jìn)行加密
                  String key=aesCoder.getAesKey();// 取得AES的密鑰
                  byte[] rsaArr=rsaCoder.getEncryptArray(key, serverPublicKey);
                  String encryptedKey
          =Base64.encodeBase64String(rsaArr);
                  
                  
          // 在發(fā)出的密文前附帶經(jīng)服務(wù)器RSA公鑰加密的AES密鑰
                  String request="<key>"+encryptedKey+"</key>"+cipherText;
                  
                  out.print(request);
                  out.flush();
                  s.shutdownOutput();
          // 輸出結(jié)束

          從上面這段代碼可以看出,想發(fā)送到服務(wù)器端的明文是:
          <request><command>register</command><username>何楊</username><password>123456</password></request>

          通過(guò)這段代碼的處理后,最終發(fā)送到服務(wù)器端的密文是,
          <key>1B2FM07HS4iB+vjeehb/RqHTnEXAr1cj/CR6z+SDPI58ZG5TK54iEoi8cvdIL0oj60X7axrAL3YO
          b6PMzQxKHzipSYw3ishH/3KxoYF8bkQGn2PkMNsn+xL1Gz6XgJcQ+B700hYvVT2FFPfelVz3VNlB
          KhwVIE6h8LyD4w/SxhE=
          </key>J4TsMoB3l8Cy91a9v6O0TADXZvKEkDPZ3E5noeu2dImfdsM55urhEY7lFAAsXm0AB4/jUL1h1lNP
          cafz9srORh7h8NCb4760XnrBA5Q2JQrqwr1TGsB3oGq2Ha+FOLoFcI2Ab/wjEiAhe/kB6ZTgTA==
          其中key節(jié)點(diǎn)的內(nèi)容是加密的AES密鑰,后面是AES加密后的密文。如果這段文字在網(wǎng)絡(luò)上被截獲,截獲者可能會(huì)猜測(cè)出key節(jié)點(diǎn)是密鑰,后半段是密文,但密鑰部分是被服務(wù)器的公鑰進(jìn)行RSA加密的,只有用服務(wù)器的私鑰來(lái)解密;而密鑰文解不出來(lái)的話(huà),截獲者對(duì)后端密文也是無(wú)能為力。這就可以讓人放心了,如果服務(wù)器端沒(méi)有潛伏一個(gè)余則成和截獲者里應(yīng)外合的話(huà)。這里還可以把整段文字用Base64加密一下,到服務(wù)器再解密。

          服務(wù)器端的處理代碼:
                  String cipheredAesKey="";// 經(jīng)服務(wù)器RSA公鑰加密的客戶(hù)端AES鑰匙密文
                  String cipherText="";// 經(jīng)客戶(hù)端AES加密的密文
                  
                  
          // 用正則表達(dá)式得到密鑰文和密文
                  String regex="<key>(.+)</key>(.+)";
                  Pattern pattern
          =Pattern.compile(regex);
                  Matcher matcher
          =pattern.matcher(request);
                      
                  
          while(matcher.find()){
                      cipheredAesKey
          =matcher.group(1);
                      cipherText
          =matcher.group(2);
                      
          break;
                  }

                  
          // 得到經(jīng)過(guò)服務(wù)器RSA私鑰解密后的AES密鑰
                  String plainAesKey="";
                  
          try {
                      
          byte[] cipheredAesKeyArr=Base64.decodeBase64(cipheredAesKey);
                      plainAesKey
          =model.getRsaCoder().getDecryptString(cipheredAesKeyArr);
                  } 
          catch (Exception e) {
                      e.printStackTrace();
                      
          return null;
                  }
                  
                  
          // 使用AES密鑰解密出明文
                  byte[] cipherTextArr=Base64.decodeBase64(cipherText);
                  String plainText
          =model.getAesCoder().getDecryptString(cipherTextArr, plainAesKey);

          這段代碼的輸入是:
          <key>P9SQ2DtWqrdH3hJbQNWRb51OEs9c7KpsgjRg0yPT5LZJoqJBeYmq3r/1T050n136OelvTh+XtaZaXbCJAvfnF4fvtAKdXqPp+lzUNgPYk8R0OaVDUIi8pNi1rb/+GvtY2ZucFYL1BOwO8ARwvXf8f52Cl+Vdu5TdinXVjmwSPZY=</key>u0ube9sy7bsIy8aaUSJofoswY+R3WXD8yJbOzEZWiDniyXNNyrHNiygfRHj3TKwVQXRck/OVPXptMvUjCVqmg118TN0tc4sKoOKHaSmUtvGC2WW3K5anxlFzdUIZMIhvpDF1nWoaTXvEJ1nOuwhIig==
          它和客戶(hù)端傳過(guò)來(lái)的內(nèi)容是一樣的。

          而經(jīng)過(guò)拆分和解密后,AES密鑰是:
          83aeacfa1b59eb2dc557a9f3d5df6af83ee9a1646652f1d2b55ea6ec76a95bde

          用得到的AES密鑰解密后,最終得到的明文部分是:
          <request><command>register</command><username>何楊</username><password>123456</password></request>

          到這里,密文的還原工作就完成了。如果服務(wù)器端要向客戶(hù)端發(fā)回處理后的結(jié)果,把上述過(guò)程再做一遍就可以了,注意一點(diǎn),客戶(hù)端要把自己的RSA公鑰發(fā)過(guò)來(lái),也就是說(shuō)傳遞的文本中還要增加一個(gè)節(jié)點(diǎn),這樣服務(wù)器端就有了客戶(hù)端的RSA公鑰對(duì)服務(wù)器端的AES鑰匙進(jìn)行加密。(完整混合加密客戶(hù)機(jī)服務(wù)器通訊過(guò)程請(qǐng)參看:http://www.aygfsteel.com/heyang/archive/2010/12/26/341556.html

          這種方式看似比純RSA方式和AES方式都復(fù)雜了一點(diǎn),但考慮到網(wǎng)絡(luò)傳輸?shù)陌踩院退俣龋鄬?xiě)一些代碼是完全值得的。

          上文中用到的AESSecurityCoder類(lèi)代碼如下:
          package com.heyang.common.code;

          import java.security.Key;
          import java.security.NoSuchAlgorithmException;

          import javax.crypto.Cipher;
          import javax.crypto.KeyGenerator;
          import javax.crypto.SecretKey;
          import javax.crypto.spec.SecretKeySpec;

          import org.apache.commons.codec.binary.Hex;


          /**
           * AES加密解密類(lèi)
           * 說(shuō)明:
           * 作者:何楊(heyang78@gmail.com)
           * 創(chuàng)建時(shí)間:2010-12-25 下午12:19:12
           * 修改時(shí)間:2010-12-25 下午12:19:12
           
          */
          public class AESSecurityCoder{
              
          // 加密方法
              private static final String Algorithm="AES";
              
              
          // 進(jìn)行加密解密的密鑰
              private String aesKey="";
              
              
          /**
               * 構(gòu)造函數(shù)
               * 
          @throws NoSuchAlgorithmException 
               
          */
              
          public AESSecurityCoder() throws NoSuchAlgorithmException{
                  KeyGenerator kg
          =KeyGenerator.getInstance(Algorithm);
                  kg.init(
          256);
                  SecretKey sk
          =kg.generateKey();
                  
          byte[] arr=sk.getEncoded();
                  
                  aesKey
          =new String(Hex.encodeHex(arr));
              }
              
              
          /**
               * 取得解密后的字符串
               * 
               * 說(shuō)明:
               * 
          @param encryptArr
               * 
          @return
               * 創(chuàng)建時(shí)間:2010-12-1 下午03:33:31
               
          */
              
          public String getDecryptString(byte[] encryptArr){
                  
          try{
                      Cipher cp
          =Cipher.getInstance(Algorithm);
                      cp.init(Cipher.DECRYPT_MODE, getKey());
                      
          byte[] arr=cp.doFinal(encryptArr);
                      
                      
          return new String(arr);
                  }
                  
          catch(Exception ex){
                      System.out.println(
          "無(wú)法進(jìn)行解密,原因是"+ex.getMessage());
                      
          return null;
                  }
              }
              
              
          /**
               * 傳入密鑰,得到解密后的字符串
               * 
               * 說(shuō)明:
               * 
          @param encryptArr
               * 
          @param aesKey
               * 
          @return
               * 創(chuàng)建時(shí)間:2010-12-25 下午01:55:42
               
          */
              
          public String getDecryptString(byte[] encryptArr,String aesKeyIn){
                  
          try{
                      Cipher cp
          =Cipher.getInstance(Algorithm);
                      
                      
          byte[] arr1=Hex.decodeHex(aesKeyIn.toCharArray());
                      cp.init(Cipher.DECRYPT_MODE, 
          new SecretKeySpec(arr1,Algorithm));
                      
          byte[] arr=cp.doFinal(encryptArr);
                      
                      
          return new String(arr);
                  }
                  
          catch(Exception ex){
                      System.out.println(
          "無(wú)法進(jìn)行解密,原因是"+ex.getMessage());
                      
          return null;
                  }
              }
              
              
          /**
               * 取得加密后的字節(jié)數(shù)組
               * 
               * 說(shuō)明:
               * 
          @param originalString
               * 
          @return
               * 創(chuàng)建時(shí)間:2010-12-1 下午03:33:49
               
          */
              
          public byte[] getEncryptByteArray(String originalString){
                  
          try{
                      Cipher cp
          =Cipher.getInstance(Algorithm);
                      cp.init(Cipher.ENCRYPT_MODE, getKey());
                      
          return cp.doFinal(originalString.getBytes());
                  }
                  
          catch(Exception ex){
                      System.out.println(
          "無(wú)法進(jìn)行加密,原因是"+ex.getMessage());
                      
          return null;
                  }
              }
              
              
          /**
               * 取得密鑰
               * 
               * 說(shuō)明:
               * 
          @return
               * 
          @throws Exception
               * 創(chuàng)建時(shí)間:2010-12-1 下午03:33:17
               
          */
              
          private Key getKey() throws Exception{
                  
          byte[] arr=Hex.decodeHex(aesKey.toCharArray());
                  
                  
          return new SecretKeySpec(arr,Algorithm);
              }

              
          /**
               * 取得AES加密鑰匙
               * 
               * 說(shuō)明:
               * 
          @return
               * 創(chuàng)建時(shí)間:2010-12-25 下午12:27:16
               
          */
              
          public String getAesKey() {
                  
          return aesKey;
              }
          }

          上文中用到的RSASecurityCoder類(lèi)代碼如下:
          package com.heyang.common.code;

          import java.security.KeyFactory;
          import java.security.KeyPair;
          import java.security.KeyPairGenerator;
          import java.security.PrivateKey;
          import java.security.PublicKey;
          import java.security.interfaces.RSAPrivateKey;
          import java.security.interfaces.RSAPublicKey;
          import java.security.spec.PKCS8EncodedKeySpec;
          import java.security.spec.X509EncodedKeySpec;

          import javax.crypto.Cipher;

          import org.apache.commons.codec.binary.Base64;

          /**
           * RSA加密解密類(lèi)
           * 說(shuō)明:
           * 作者:何楊(heyang78@gmail.com)
           * 創(chuàng)建時(shí)間:2010-12-1 下午06:14:38
           * 修改時(shí)間:2010-12-1 下午06:14:38
           
          */
          public class RSASecurityCoder{
              
          // 非對(duì)稱(chēng)加密密鑰算法
              private static final String Algorithm="RSA";
              
              
          // 密鑰長(zhǎng)度,用來(lái)初始化
              private static final int Key_Size=1024;
              
              
          // 公鑰
              private byte[] publicKey;
              
              
          // 私鑰
              private byte[] privateKey;
              
              
          /**
               * 構(gòu)造函數(shù),在其中生成公鑰和私鑰
               * 
          @throws Exception
               
          */
              
          public RSASecurityCoder() throws Exception{
                  
          // 得到密鑰對(duì)生成器
                  KeyPairGenerator kpg=KeyPairGenerator.getInstance(Algorithm);
                  kpg.initialize(Key_Size);
                  
                  
          // 得到密鑰對(duì)
                  KeyPair kp=kpg.generateKeyPair();
                  
                  
          // 得到公鑰
                  RSAPublicKey keyPublic=(RSAPublicKey)kp.getPublic();
                  publicKey
          =keyPublic.getEncoded();
                  
                  
          // 得到私鑰
                  RSAPrivateKey keyPrivate=(RSAPrivateKey)kp.getPrivate();
                  privateKey
          =keyPrivate.getEncoded();
              }
              
              
          /**
               * 用公鑰對(duì)字符串進(jìn)行加密
               * 
               * 說(shuō)明:
               * 
          @param originalString
               * 
          @param publicKeyArray
               * 
          @return
               * 
          @throws Exception
               * 創(chuàng)建時(shí)間:2010-12-1 下午06:29:51
               
          */
              
          public byte[] getEncryptArray(String originalString,byte[] publicKeyArray) throws Exception{
                  
          // 得到公鑰
                  X509EncodedKeySpec keySpec=new X509EncodedKeySpec(publicKeyArray);
                  KeyFactory kf
          =KeyFactory.getInstance(Algorithm);
                  PublicKey keyPublic
          =kf.generatePublic(keySpec);
                  
                  
          // 加密數(shù)據(jù)
                  Cipher cp=Cipher.getInstance(Algorithm);
                  cp.init(Cipher.ENCRYPT_MODE, keyPublic);
                  
          return cp.doFinal(originalString.getBytes());
              }
              
              
              
          /**
               * 使用私鑰進(jìn)行解密
               * 
               * 說(shuō)明:
               * 
          @param encryptedDataArray
               * 
          @return
               * 
          @throws Exception
               * 創(chuàng)建時(shí)間:2010-12-1 下午06:35:28
               
          */
              
          public String getDecryptString(byte[] encryptedDataArray) throws Exception{
                  
          // 得到私鑰
                  PKCS8EncodedKeySpec keySpec=new PKCS8EncodedKeySpec(privateKey);
                  KeyFactory kf
          =KeyFactory.getInstance(Algorithm);
                  PrivateKey keyPrivate
          =kf.generatePrivate(keySpec);
                  
                  
          // 解密數(shù)據(jù)
                  Cipher cp=Cipher.getInstance(Algorithm);
                  cp.init(Cipher.DECRYPT_MODE, keyPrivate);
                  
          byte[] arr=cp.doFinal(encryptedDataArray);
                  
                  
          // 得到解密后的字符串
                  return new String(arr);
              }

              
          /**
               * 取得數(shù)組形式的公鑰
               * 
               * 說(shuō)明:
               * 
          @return
               * 創(chuàng)建時(shí)間:2010-12-25 上午07:50:04
               
          */
              
          public byte[] getPublicKey() {
                  
          return publicKey;
              }
              
              
          /**
               * 取得字符串形式的公鑰
               * 
               * 說(shuō)明:
               * 
          @return
               * 創(chuàng)建時(shí)間:2010-12-25 上午07:51:11
               
          */
              
          public String getPublicKeyString() {
                  
          return  Base64.encodeBase64String(getPublicKey());
              }
              
              
          public static void main(String[] arr) throws Exception{
                  String str
          ="你好,世界! Hello,world!";
                  System.out.println(
          "準(zhǔn)備用公鑰加密的字符串為:"+str);
                  
                  
          // 用公鑰加密
                  RSASecurityCoder rsaCoder=new RSASecurityCoder();
                  
          byte[] publicKey=rsaCoder.getPublicKey();        
                  
          byte[] encryptArray=rsaCoder.getEncryptArray(str, publicKey);
                  
                  System.out.print(
          "用公鑰加密后的結(jié)果為:");
                  
          for(byte b:encryptArray){
                      System.out.print(b);
                  }
                  System.out.println();
                  
                  
          // 用私鑰解密
                  String str1=rsaCoder.getDecryptString(encryptArray);
                  System.out.println(
          "用私鑰解密后的字符串為:"+str1);
              }
          }

          好了,感謝您看到這里,希望它沒(méi)有太多耽誤您的寶貴時(shí)間。
          posted on 2010-12-25 16:02 何楊 閱讀(4327) 評(píng)論(6)  編輯  收藏

          Feedback

          # re: 在網(wǎng)絡(luò)通訊中采用混合方式對(duì)信息進(jìn)行加密 2010-12-25 21:20 Michaelsong
          加密解密,大學(xué)專(zhuān)業(yè)是信息安全,要好好學(xué)學(xué)……  回復(fù)  更多評(píng)論
            

          # re: 在網(wǎng)絡(luò)通訊中采用混合方式對(duì)信息進(jìn)行加密[未登錄](méi) 2012-06-11 00:29 Tim
          對(duì)我有用。。十分感謝。謝謝  回復(fù)  更多評(píng)論
            

          # re: 在網(wǎng)絡(luò)通訊中采用混合方式對(duì)信息進(jìn)行加密 2014-01-17 11:26 無(wú)敵3
          請(qǐng)問(wèn)樓主,服務(wù)器存儲(chǔ)的公鑰密鑰對(duì)只有一個(gè),還是根據(jù)客戶(hù)端發(fā)來(lái)的隨機(jī)數(shù)生成針對(duì)這個(gè)用戶(hù)的公鑰密鑰對(duì)  回復(fù)  更多評(píng)論
            

          # re: 在網(wǎng)絡(luò)通訊中采用混合方式對(duì)信息進(jìn)行加密 2014-11-29 20:59 Zagfai
          還不是跟https一個(gè)原理  回復(fù)  更多評(píng)論
            

          # re: 在網(wǎng)絡(luò)通訊中采用混合方式對(duì)信息進(jìn)行加密 2016-07-04 12:58 #123
          還在 不
          ?
            回復(fù)  更多評(píng)論
            

          # re: 在網(wǎng)絡(luò)通訊中采用混合方式對(duì)信息進(jìn)行加密 2016-07-04 12:59 #123
          你給的那個(gè)程序 代碼鏈接 打不開(kāi)了
          是不是 失效了 能不能再給個(gè)   回復(fù)  更多評(píng)論
            


          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 饶阳县| 江门市| 抚宁县| 勃利县| 日土县| 双辽市| 白山市| 萨迦县| 山东| 收藏| 莆田市| 武汉市| 隆德县| 兴安盟| 丰台区| 镇原县| 伊宁市| 陆良县| 芒康县| 垦利县| 新竹市| 屏山县| 谷城县| 馆陶县| 新乡县| 拜城县| 霍山县| 尼玛县| 宣武区| 原阳县| 和田县| 苏尼特右旗| 邓州市| 丽江市| 洪洞县| 新余市| 安徽省| 吉水县| 呼玛县| 庆安县| 紫云|