最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Android使用Socket對大文件進行加密傳輸

 更新時間:2017年01月19日 16:24:01   作者:咖枯  
這篇文章主要介紹了詳解Android使用Socket對大文件進行加密傳輸,使用Socket進行文件傳輸過程時,需要先進行加密,有興趣的可以了解一下。

前言

數(shù)據(jù)加密,是一門歷史悠久的技術(shù),指通過加密算法和加密密鑰將明文轉(zhuǎn)變?yōu)槊芪?,而解密則是通過解密算法和解密密鑰將密文恢復(fù)為明文。它的核心是密碼學(xué)。

數(shù)據(jù)加密目前仍是計算機系統(tǒng)對信息進行保護的一種最可靠的辦法。它利用密碼技術(shù)對信息進行加密,實現(xiàn)信息隱蔽從而起到保護信息的安全的作用。

項目中使用Socket進行文件傳輸過程時,需要先進行加密。實現(xiàn)的過程中踏了一些坑,下面對實現(xiàn)過程進行一下總結(jié)。

DES加密

由于加密過程中使用的是DES加密算法,下面貼一下DES加密代碼:

 //秘鑰算法
 private static final String KEY_ALGORITHM = "DES";
 //加密算法:algorithm/mode/padding 算法/工作模式/填充模式
 private static final String CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";
 //秘鑰
 private static final String KEY = "12345678";//DES秘鑰長度必須是8位

 public static void main(String args[]) {
  String data = "加密解密";
  KLog.d("加密數(shù)據(jù):" + data);
  byte[] encryptData = encrypt(data.getBytes());
  KLog.d("加密后的數(shù)據(jù):" + new String(encryptData));
  byte[] decryptData = decrypt(encryptData);
  KLog.d("解密后的數(shù)據(jù):" + new String(decryptData));
 }

 public static byte[] encrypt(byte[] data) {
  //初始化秘鑰
  SecretKey secretKey = new SecretKeySpec(KEY.getBytes(), KEY_ALGORITHM);

  try {
   Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
   cipher.init(Cipher.ENCRYPT_MODE, secretKey);
   byte[] result = cipher.doFinal(data);
   return Base64.getEncoder().encode(result);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }

 public static byte[] decrypt(byte[] data) {
  byte[] resultBase64 = Base64.getDecoder().decode(data);
  SecretKey secretKey = new SecretKeySpec(KEY.getBytes(), KEY_ALGORITHM);

  try {
   Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
   cipher.init(Cipher.DECRYPT_MODE, secretKey);
   byte[] result = cipher.doFinal(resultBase64);
   return result;
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }

輸出:

加密數(shù)據(jù):加密解密

加密后的數(shù)據(jù):rt6XE06pElmLZMaVxrbfCQ==

解密后的數(shù)據(jù):加密解密

Socket客戶端部分代碼:

 Socket socket = new Socket(ApiConstants.HOST, ApiConstants.PORT);
 OutputStream outStream = socket.getOutputStream();

 InputStream inStream = socket.getInputStream();
 RandomAccessFile fileOutStream = new RandomAccessFile(file, "r");
 fileOutStream.seek(0);
 byte[] buffer = new byte[1024];
 int len = -1;
 while (((len = fileOutStream.read(buffer)) != -1)) {
  outStream.write(buffer, 0, len); // 這里進行字節(jié)流的傳輸
 }

 fileOutStream.close();
 outStream.close();
 inStream.close();
 socket.close();

Socket服務(wù)端部分代碼:

 Socket socket = server.accept();
 InputStream inStream = socket.getInputStream();
 OutputStream outStream = socket.getOutputStream();
 outStream.write(response.getBytes("UTF-8"));
 RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");
 fileOutStream.seek(0);
 byte[] buffer = new byte[1024];
 int len;
 while ((len = inStream.read(buffer)) != -1) { // 從字節(jié)輸入流中讀取數(shù)據(jù)寫入到文件中
  fileOutStream.write(buffer, 0, len);
 }

 fileOutStream.close();
 inStream.close();
 outStream.close();
 socket.close();

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

下面對傳輸數(shù)據(jù)進行加密解密

方案一:直接對io流進行加密解密

客戶端變更如下:

 while (((len = fileOutStream.read(buffer)) != -1)) {
  outStream.write(DesUtil.encrypt(buffer) ,0, len); // 對字節(jié)數(shù)組進行加密
 }

服務(wù)端變更代碼:

 while ((len = inStream.read(buffer)) != -1) {
  fileOutStream.write(DesUtil.decrypt(buffer), 0, len); // 對字節(jié)數(shù)組進行解密
 }

執(zhí)行代碼后,服務(wù)端解密時會報如下異常:

javax.crypto.BadPaddingException: pad block corrupted

猜測錯誤原因是加密過程會對數(shù)據(jù)進行填充處理,然后在io流傳輸?shù)倪^程中,數(shù)據(jù)有丟包現(xiàn)象發(fā)生,所以解密會報異常。

加密后的結(jié)果是字節(jié)數(shù)組,這些被加密后的字節(jié)在碼表(例如UTF-8 碼表)上找不到對應(yīng)字符,會出現(xiàn)亂碼,當(dāng)亂碼字符串再次轉(zhuǎn)換為字節(jié)數(shù)組時,長度會變化,導(dǎo)致解密失敗,所以轉(zhuǎn)換后的數(shù)據(jù)是不安全的。

于是嘗試了使用NOPadding填充模式,這樣雖然可以成功解密,測試中發(fā)現(xiàn)對于一般文件,如.txt文件可以正常顯示內(nèi)容,但是.apk等文件則會有解析包出現(xiàn)異常等錯誤提示。

方案二:使用字符流

使用Base64 對字節(jié)數(shù)組進行編碼,任何字節(jié)都能映射成對應(yīng)的Base64 字符,之后能恢復(fù)到字節(jié)數(shù)組,利于加密后數(shù)據(jù)的保存于傳輸,所以轉(zhuǎn)換是安全的。同樣,字節(jié)數(shù)組轉(zhuǎn)換成16 進制字符串也是安全的。

由于客戶端從輸入文件中讀取的是字節(jié)流,需要先將字節(jié)流轉(zhuǎn)換成字符流,而服務(wù)端接收到字符流后需要先轉(zhuǎn)換成字節(jié)流,再將其寫入到文件。測試中發(fā)現(xiàn)可以對字符流成功解密,但是將文件轉(zhuǎn)化成字符流進行傳輸是個連續(xù)的過程,而文件的寫出和寫入又比較繁瑣,操作過程中會出現(xiàn)很多問題。

方案三:使用CipherInputStream、CipherOutputStream

使用過程中發(fā)現(xiàn)只有當(dāng)CipherOutputStream流close時,CipherInputStream才會接收到數(shù)據(jù),顯然這個方案也只好pass掉。

方案四:使用SSLSocket

在Android上使用SSLSocket的會稍顯復(fù)雜,首先客戶端和服務(wù)端需要生成秘鑰和證書。Android證書的格式還必須是bks格式(Java使用jks格式)。一般來說,我們使用jdk的keytool只能生成jks的證書庫,如果生成bks的則需要下載BouncyCastle庫。

當(dāng)以上所有的一切都準(zhǔn)備完畢后,如果在Android6.0以上使用你會悲催的發(fā)現(xiàn)下面這個異常:

javax.net.ssl.SSLHandshakeException: Handshake failed

異常原因:SSLSocket簽名算法默認(rèn)為DSA,Android6.0(API 23)以后KeyStore發(fā)生更改,不再支持DSA,但仍支持ECDSA。

所以如果想在Android6.0以上使用SSLSocket,需要將DSA改成ECDSA...org感覺坑越入越深看不到底呀...于是決定換個思路來解決socket加密這個問題。既然對文件邊傳邊加密解密不好使,那能不能客戶端傳輸文件前先對文件進行加密,然后進行傳輸,服務(wù)端成功接收文件后,再對文件進行解密呢。于是就有了下面這個方案。

方案五:先對文件進行加密,然后傳輸,服務(wù)端成功接收文件后再對文件進行解密

對文件進行加密解密代碼如下:

public class FileDesUtil {
 //秘鑰算法
 private static final String KEY_ALGORITHM = "DES";
 //加密算法:algorithm/mode/padding 算法/工作模式/填充模式
 private static final String CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";
 private static final byte[] KEY = {56, 57, 58, 59, 60, 61, 62, 63};//DES 秘鑰長度必須是8 位或以上

 /**
  * 文件進行加密并保存加密后的文件到指定目錄
  *
  * @param fromFile 要加密的文件 如c:/test/待加密文件.txt
  * @param toFile 加密后存放的文件 如c:/加密后文件.txt
  */
 public static void encrypt(String fromFilePath, String toFilePath) {
  KLog.i("encrypting...");

  File fromFile = new File(fromFilePath);
  if (!fromFile.exists()) {
   KLog.e("to be encrypt file no exist!");
   return;
  }
  File toFile = getFile(toFilePath);

  SecretKey secretKey = new SecretKeySpec(KEY, KEY_ALGORITHM);
  InputStream is = null;
  OutputStream out = null;
  CipherInputStream cis = null;
  try {
   Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
   cipher.init(Cipher.ENCRYPT_MODE, secretKey);
   is = new FileInputStream(fromFile);
   out = new FileOutputStream(toFile);
   cis = new CipherInputStream(is, cipher);
   byte[] buffer = new byte[1024];
   int r;
   while ((r = cis.read(buffer)) > 0) {
    out.write(buffer, 0, r);
   }
  } catch (Exception e) {
   KLog.e(e.toString());
  } finally {
   try {
    if (cis != null) {
     cis.close();
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
   try {
    if (is != null) {
     is.close();
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
   try {
    if (out != null) {
     out.close();
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
  }

  KLog.i("encrypt completed");
 }

 @NonNull
 private static File getFile(String filePath) {
  File fromFile = new File(filePath);
  if (!fromFile.getParentFile().exists()) {
   fromFile.getParentFile().mkdirs();
  }
  return fromFile;
 }

 /**
  * 文件進行解密并保存解密后的文件到指定目錄
  *
  * @param fromFilePath 已加密的文件 如c:/加密后文件.txt
  * @param toFilePath 解密后存放的文件 如c:/ test/解密后文件.txt
  */
 public static void decrypt(String fromFilePath, String toFilePath) {
  KLog.i("decrypting...");

  File fromFile = new File(fromFilePath);
  if (!fromFile.exists()) {
   KLog.e("to be decrypt file no exist!");
   return;
  }
  File toFile = getFile(toFilePath);

  SecretKey secretKey = new SecretKeySpec(KEY, KEY_ALGORITHM);

  InputStream is = null;
  OutputStream out = null;
  CipherOutputStream cos = null;
  try {
   Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
   cipher.init(Cipher.DECRYPT_MODE, secretKey);
   is = new FileInputStream(fromFile);
   out = new FileOutputStream(toFile);
   cos = new CipherOutputStream(out, cipher);
   byte[] buffer = new byte[1024];
   int r;
   while ((r = is.read(buffer)) >= 0) {
    cos.write(buffer, 0, r);
   }
  } catch (Exception e) {
   KLog.e(e.toString());
  } finally {
   try {
    if (cos != null) {
     cos.close();
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
   try {
    if (out != null) {
     out.close();
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
   try {
    if (is != null) {
     is.close();
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
  }

  KLog.i("decrypt completed");
 }
}


使用如上這個方案就完美的繞開了上面提到的一些問題,成功的實現(xiàn)了使用Socket對文件進行加密傳輸。

總結(jié)

對于任何技術(shù)的使用,底層原理的理解還是很有必要的。不然遇到問題很容易就是一頭霧水不知道Why!

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android中的HTextView庫實現(xiàn)TextView動畫效果

    Android中的HTextView庫實現(xiàn)TextView動畫效果

    HTextView是一個用來給TextView里的文字做各種轉(zhuǎn)換動畫的開源庫,不僅提供了多種動畫選擇,而且還有重復(fù)字符的位移動畫,雖然并沒有多么復(fù)雜,但是它使用的這些典型的設(shè)計模式以及各種動畫的實現(xiàn)確實可以從中讓我們學(xué)到不少知識
    2023-12-12
  • Android 仿高德地圖可拉伸的BottomSheet的示例代碼

    Android 仿高德地圖可拉伸的BottomSheet的示例代碼

    這篇文章主要介紹了Android 仿高德地圖可拉伸的BottomSheet的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • Android中activity的啟動模式

    Android中activity的啟動模式

    本文主要介紹了Android中activity的啟動模式。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • RxJava構(gòu)建流基本原理示例解析

    RxJava構(gòu)建流基本原理示例解析

    這篇文章主要為大家介紹了RxJava構(gòu)建流基本原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Kotlin對象比較注意點示例詳解

    Kotlin對象比較注意點示例詳解

    你知道在kotlin中,如何來比較對象相等嗎?下面這篇文章主要給大家介紹了關(guān)于Kotlin對象比較注意點的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • Android捕捉錯誤try catch 的簡單使用教程

    Android捕捉錯誤try catch 的簡單使用教程

    這篇文章主要介紹了Android捕捉錯誤try catch 的簡單使用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Android 模擬器的使用詳細(xì)介紹

    Android 模擬器的使用詳細(xì)介紹

    本文主要介紹Android 模擬器,在開發(fā)Android應(yīng)用過程中大家都要使用模擬器對應(yīng)用的測試,這里對模擬器的資料做了詳細(xì)整理,有興趣的小伙伴可以參考下
    2016-08-08
  • Android  Handler 機制實現(xiàn)原理分析

    Android Handler 機制實現(xiàn)原理分析

    本文主要介紹 Android Handle機制實現(xiàn)的原理,這里整理了詳細(xì)的關(guān)于Handler的資料以及工作流程和實際應(yīng)用,有興趣的小伙伴可以參考下
    2016-08-08
  • Android 彩色Toast的實現(xiàn)代碼

    Android 彩色Toast的實現(xiàn)代碼

    這篇文章主要介紹了Android 彩色Toast的實現(xiàn)代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-10-10
  • Android 中 MD5 的幾種生成方式(小結(jié))

    Android 中 MD5 的幾種生成方式(小結(jié))

    這篇文章主要介紹了Android 中 MD5 的幾種生成方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03

最新評論

贵港市| 绍兴市| 都江堰市| 庄河市| 谷城县| 永昌县| 全州县| 丰都县| 郎溪县| 东乌珠穆沁旗| 大港区| 乡宁县| 安平县| 乐亭县| 叶城县| 遂平县| 五大连池市| 凤城市| 江油市| 柳林县| 胶南市| 台东市| 章丘市| 南江县| 三都| 铁力市| 德庆县| 饶平县| 保山市| 方山县| 文水县| 连云港市| 谷城县| 夏邑县| 正安县| 成都市| 肇源县| 都江堰市| 察雅县| 雷州市| 阿拉尔市|