详解Android使用Socket对大文件进行加密传输
发布时间 - 2026-01-10 22:39:19 点击率:次前言

数据加密,是一门历史悠久的技术,指通过加密算法和加密密钥将明文转变为密文,而解密则是通过解密算法和解密密钥将密文恢复为明文。它的核心是密码学。
数据加密目前仍是计算机系统对信息进行保护的一种最可靠的办法。它利用密码技术对信息进行加密,实现信息隐蔽从而起到保护信息的安全的作用。
项目中使用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!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# android
# 文件加密
# socket加密上传
# socket
# 加密传输
# 详解Android 基于TCP和UDP协议的Socket通信
# Android使用WebSocket实现多人游戏
# 详解OkSocket与Android的简单使用
# Android开发之Socket通信传输简单示例
# android基于socket的局域网内服务器与客户端加密通信
# android socket聊天室功能实现
# SpringBoot webSocket实现发送广播、点对点消息和Android接收
# Android中Socket大文件断点上传示例
# android Socket实现简单聊天功能以及文件传输
# 详解Android 通过Socket 和服务器通讯(附demo)
# Android Socket接口实现即时通讯实例代码
# Android完整Socket解决方案
# 服务端
# 加密解密
# 客户端
# 过程中
# 流进
# 的是
# 会报
# 转换成
# 再对
# 并保存
# 是个
# 你会
# 都能
# 则是
# 才会
# 找不到
# 很有
# 一切都
# 这个问题
# 很容易
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
开心动漫网站制作软件下载,十分开心动画为何停播?
制作电商网页,电商供应链怎么做?
Laravel项目如何进行性能优化_Laravel应用性能分析与优化技巧大全
常州企业网站制作公司,全国继续教育网怎么登录?
js实现点击每个li节点,都弹出其文本值及修改
如何生成腾讯云建站专用兑换码?
Android 常见的图片加载框架详细介绍
装修招标网站设计制作流程,装修招标流程?
原生JS实现图片轮播切换效果
厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?
Laravel怎么使用Intervention Image库处理图片上传和缩放
详解Android——蓝牙技术 带你实现终端间数据传输
ai格式如何转html_将AI设计稿转换为HTML页面流程【页面】
创业网站制作流程,创业网站可靠吗?
Laravel如何创建自定义中间件?(Middleware代码示例)
JavaScript中如何操作剪贴板_ClipboardAPI怎么用
Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】
高性能网站服务器配置指南:安全稳定与高效建站核心方案
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
如何用西部建站助手快速创建专业网站?
Mybatis 中的insertOrUpdate操作
Zeus浏览器网页版官网入口 宙斯浏览器官网在线通道
Laravel如何创建自定义Facades?(详细步骤)
Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置
Laravel策略(Policy)如何控制权限_Laravel Gates与Policies实现用户授权
如何挑选最适合建站的高性能VPS主机?
Laravel API资源类怎么用_Laravel API Resource数据转换
Laravel如何使用模型观察者?(Observer代码示例)
极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?
免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?
简单实现Android验证码
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
如何在 React 中条件性地遍历数组并渲染元素
如何挑选优质建站一级代理提升网站排名?
Laravel项目怎么部署到Linux_Laravel Nginx配置详解
android nfc常用标签读取总结
Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理
Laravel Asset编译怎么配置_Laravel Vite前端构建工具使用
Laravel Sail是什么_基于Docker的Laravel本地开发环境Sail入门
Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】
JS碰撞运动实现方法详解
如何正确下载安装西数主机建站助手?
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
Laravel安装步骤详细教程_Laravel环境搭建指南
php静态变量怎么调试_php静态变量作用域调试技巧【解答】
Swift开发中switch语句值绑定模式
Laravel任务队列怎么用_Laravel Queues异步处理任务提升应用性能
Internet Explorer官网直接进入 IE浏览器在线体验版网址
如何用PHP工具快速搭建高效网站?
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法

