热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

详解Android使用Socket对大文件进行加密传输

这篇文章主要介绍了详解Android使用Socket对大文件进行加密传输,使用Socket进行文件传输过程时,需要先进行加密,有兴趣的可以了解一下。

前言

数据加密,是一门历史悠久的技术,指通过加密算法和加密密钥将明文转变为密文,而解密则是通过解密算法和解密密钥将密文恢复为明文。它的核心是密码学。

数据加密目前仍是计算机系统对信息进行保护的一种最可靠的办法。它利用密码技术对信息进行加密,实现信息隐蔽从而起到保护信息的安全的作用。

项目中使用Socket进行文件传输过程时,需要先进行加密。实现的过程中踏了一些坑,下面对实现过程进行一下总结。

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("加密数据:" + data);
  byte[] encryptData = encrypt(data.getBytes());
  KLog.d("加密后的数据:" + new String(encryptData));
  byte[] decryptData = decrypt(encryptData);
  KLog.d("解密后的数据:" + 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;
 }

输出:

加密数据:加密解密

加密后的数据:rt6XE06pElmLZMaVxrbfCQ==

解密后的数据:加密解密

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); // 这里进行字节流的传输
 }

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

Socket服务端部分代码:

 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) { // 从字节输入流中读取数据写入到文件中
  fileOutStream.write(buffer, 0, len);
 }

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

数据加密传输

下面对传输数据进行加密解密

方案一:直接对io流进行加密解密

客户端变更如下:

 while (((len = fileOutStream.read(buffer)) != -1)) {
  outStream.write(DesUtil.encrypt(buffer) ,0, len); // 对字节数组进行加密
 }

服务端变更代码:

 while ((len = inStream.read(buffer)) != -1) {
  fileOutStream.write(DesUtil.decrypt(buffer), 0, len); // 对字节数组进行解密
 }

执行代码后,服务端解密时会报如下异常:

javax.crypto.BadPaddingException: pad block corrupted

猜测错误原因是加密过程会对数据进行填充处理,然后在io流传输的过程中,数据有丢包现象发生,所以解密会报异常。

加密后的结果是字节数组,这些被加密后的字节在码表(例如UTF-8 码表)上找不到对应字符,会出现乱码,当乱码字符串再次转换为字节数组时,长度会变化,导致解密失败,所以转换后的数据是不安全的。

于是尝试了使用NOPadding填充模式,这样虽然可以成功解密,测试中发现对于一般文件,如.txt文件可以正常显示内容,但是.apk等文件则会有解析包出现异常等错误提示。

方案二:使用字符流

使用Base64 对字节数组进行编码,任何字节都能映射成对应的Base64 字符,之后能恢复到字节数组,利于加密后数据的保存于传输,所以转换是安全的。同样,字节数组转换成16 进制字符串也是安全的。

由于客户端从输入文件中读取的是字节流,需要先将字节流转换成字符流,而服务端接收到字符流后需要先转换成字节流,再将其写入到文件。测试中发现可以对字符流成功解密,但是将文件转化成字符流进行传输是个连续的过程,而文件的写出和写入又比较繁琐,操作过程中会出现很多问题。

方案三:使用CipherInputStream、CipherOutputStream

使用过程中发现只有当CipherOutputStream流close时,CipherInputStream才会接收到数据,显然这个方案也只好pass掉。

方案四:使用SSLSocket

在Android上使用SSLSocket的会稍显复杂,首先客户端和服务端需要生成秘钥和证书。Android证书的格式还必须是bks格式(Java使用jks格式)。一般来说,我们使用jdk的keytool只能生成jks的证书库,如果生成bks的则需要下载BouncyCastle库。

当以上所有的一切都准备完毕后,如果在Android6.0以上使用你会悲催的发现下面这个异常:

javax.net.ssl.SSLHandshakeException: Handshake failed

异常原因:SSLSocket签名算法默认为DSA,Android6.0(API 23)以后KeyStore发生更改,不再支持DSA,但仍支持ECDSA。

所以如果想在Android6.0以上使用SSLSocket,需要将DSA改成ECDSA...org感觉坑越入越深看不到底呀...于是决定换个思路来解决socket加密这个问题。既然对文件边传边加密解密不好使,那能不能客户端传输文件前先对文件进行加密,然后进行传输,服务端成功接收文件后,再对文件进行解密呢。于是就有了下面这个方案。

方案五:先对文件进行加密,然后传输,服务端成功接收文件后再对文件进行解密

对文件进行加密解密代码如下:

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");
 }
}


使用如上这个方案就完美的绕开了上面提到的一些问题,成功的实现了使用Socket对文件进行加密传输。

总结

对于任何技术的使用,底层原理的理解还是很有必要的。不然遇到问题很容易就是一头雾水不知道Why!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 学习SLAM的女生,很酷
    本文介绍了学习SLAM的女生的故事,她们选择SLAM作为研究方向,面临各种学习挑战,但坚持不懈,最终获得成功。文章鼓励未来想走科研道路的女生勇敢追求自己的梦想,同时提到了一位正在英国攻读硕士学位的女生与SLAM结缘的经历。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • “你永远都不知道明天和‘公司的意外’哪个先来。”疫情期间,这是我们最战战兢兢的心情。但是显然,有些人体会不了。这份行业数据,让笔者“柠檬” ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
author-avatar
双鱼獒主
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有