大容量数据安全传输系统设计与实现(具体)

a 夏天 提交于 2020-02-06 18:18:10

大容量数据安全传输系统设计与实现(具体)

一、需求

两个主体间实现远程大容量数据文件秘密传输(超过1G大小的容量)
1)设计并实现支持大容量数据秘密传输的系统;
2)该系统支持数据完整性和来源验证;
开发坏境需求:windows平台,语言平台不限。

二、原理

(1)hash函数

hash函数是将任意长度的输入变换成固定长度输出的函数,该输出称为散列值。MD5是一种被广泛使用的hash函数,可以产生出一个128位的散列值,主要用于确保信息传输完整一致。MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过一系列处理后,算法输出由四个32位分组组成的128位散列值。具体的步骤如下所示:
1、填充
如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余结果等于448,填充的方法是填充一个1和n个0。填充完成后,信息的长度为N* 512+448
2、记录信息长度
用64位内存来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为N* 512 + 448 + 64 = (N+1)*512
3、装入标准的幻数
A=0X67452301L,B=0XEFCDAB89L,C=0X98BADCFEL,D=0X10325476L。
4、循环运算
把消息分以512位为一分组进行处理;每一个分组进行4轮变换,以上面所说4个标准的幻数为起始变量进行计算,重新输出4个变量;以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。

(2)数字签名

数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过。RSA加密是一种非对称加密,可以在不直接传递密钥的情况下,完成解密,从而验证信息的完整性。

(3)对称加密

对称加密算法也就是加密和解密用相同的密钥,AES为最常见的对称加密算法。
AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,即每个分组为16个字节。具体步骤如下:
1、字节替换:通过S盒完成一个字节到另外一个字节的映射。
2、行移位:实现一个4x4矩阵内部字节之间的置换。移位的操作是:第一行保存不变,第二行循环左移1个字节,第三行循环左移2个字节,第四行循环左移3个字节。
3、列混淆:进行矩阵乘法,其中的加法是模2加法。
4、轮密钥加:加密过程中,每轮的输入与轮密钥异或一次(当前分组和扩展密钥的一部分进行按位异或)。因为二进制数连续异或一个数结果是不变的,所以在解密时再异或上该轮的密钥即可恢复输入。
5、密钥扩展:其复杂性是确保算法安全性的重要部分。当分组长度和密钥长度都是128位时,AES的加密算法共迭代10轮,需要10个子密钥。AES的密钥扩展的目的是将输入的128位密钥扩展成11个128位的子密钥。AES的密钥扩展算法是以字为一个基本单位(一个字为4个字节),刚好是密钥矩阵的一列。因此4个字(128位)密钥需要扩展成11个子密钥,共44个字。

三、代码

(1)完整性和来源验证

服务端:
	//计算文件md5
String md5=MD5Utils.fileMD5(directory+sourceFile);
	String destFile=md5Dir+md5;
	String tempFile=md5Dir+"temp";
	File source=new File(directory+sourceFile);
	File dest=new File(tempFile);
	copyFile(source,dest);
//使用自己的RSA密钥对信息摘要进行签名
RSAUtils serverRSA = new RSAUtils();
		serverRSA.generateKeyPair();
		output.writeUTF(serverRSA.getModulusAsHex());
		output.writeUTF(serverRSA.getPublicExponentAsHex());
		RSAPrivateKey serverPrivateKey = serverRSA.getPrivateKey();
		String signature = serverRSA.encryptStringByPrivateKey(md5, serverPrivateKey);
		System.out.println("signature:"+signature);

客户端:
//取出md5签名, 使用服务端RSA公钥解密
String signature = FileHandle.deleteLine(directory+destFile);
String md5 = serverRSA.decryptStringByPublicKey(signature);
System.out.println("md5:"+md5);
//对解密后文件计算md5
String fileMD5 = MD5Utils.fileMD5(directory+destFile);
	System.out.println("fileMD5:"+fileMD5);
	if(fileMD5.equals(md5)){
		System.out.println("文件数据完整");
	}
	else{
		System.out.println("文件数据损失");
	}

(2)对称加密与解密

服务端:
	//使用AES对原始文件和签名后的摘要信息一起加密
FileWriter writer = new FileWriter(tempFile, true);
	writer.write("\n"+signature);
	writer.close();
	AESUtils aesUtils = new AESUtils();
	byte[] secretKey = aesUtils.generateSecretKey();
	aesUtils.encryptFile(tempFile, destFile);
//使用接收方的公钥对AES密钥进行加密
	String clientModulus = input.readUTF();
	String clientExponent = input.readUTF();
	RSAUtils clientRSA = new RSAUtils(clientModulus, clientExponent);
	RSAPublicKey clientRSAPublicKey = clientRSA.getPublicKey();
	String hexSecretKey = aesUtils.getSecretyKey();
	String RSAEncryptAESKey = clientRSA.encryptStringByPublicKey(hexSecretKey, clientRSAPublicKey);
	//将上两步生成的合成在一个文件
	FileWriter fileWriter = new FileWriter(destFile, true);
	fileWriter.write("\n" + RSAEncryptAESKey);
	fileWriter.close();
客户端:
		//从文件中取出加密后的AES密钥
	String encryptedAESKey = FileHandle.deleteLine(directory+md5File);
	//用RSA私钥解密出AES密钥
	String aesSecretKey = clientRSA.decryptStringByPrivateKey (encryptedAESKey);
//使用AES密钥对文件进行解密
	AESUtils aesUtils = new AESUtils(AESUtils.asByte(aesSecretKey));
	aesUtils.decryptFile(directory+md5File, directory+destFile);
	File file = new File(directory+md5File);

(3)文件传送与接收

服务端:ServerSocket用来监听客户端的请求,使用accept方法的返回值获得一个socket对象
System.out.println("等待远程连接,端口为:" + serverSocket.getLocalPort());
server = serverSocket.accept();
System.out.println("远程主机地址:" + server.getRemoteSocketAddress());
input = new DataInputStream(server.getInputStream());
output = new DataOutputStream(server.getOutputStream());
CommandUtils commandUtils = new CommandUtils(directory);
output.writeUTF(commandUtils.getHelp());
String command;
while ((command = input.readUTF()) != null && (!"exit".equals(command))){
	System.out.println(command);
	String[] commandArr = commandUtils.parseCommand(command);
	if(commandArr[0].equals("help")){
		output.writeUTF(commandUtils.getHelp());
	}
	else if(commandArr[0].equals("ls")){
		output.writeUTF(commandUtils.getFileList());
	}
	else if(commandArr[0].equals("download")){
		sendCryptFile(commandArr[1]);
		break;
}
}
客户端:
Scanner scan = new Scanner(System.in);
	System.out.println("请输入要连接的主机地址:");
	String serverName = scan.nextLine();
	System.out.println("请输入该主机端口号:");
	int port = scan.nextInt();
	Socket client = new Socket(serverName, port);
	System.out.println("远程主机地址:" + client.getRemoteSocketAddress());
	input = new DataInputStream(client.getInputStream());
	output = new DataOutputStream(client.getOutputStream());
	String help = input.readUTF();
	System.out.println(help);

四、结果

(1)客户端与服务端连接

服务端输入监听的端口后,客户端开始连接,连接完成后,终端显示可用命令。
在这里插入图片描述

(2)下载大容量数据文件

输入ls指令后,可查看所有可下载的文件,使用download指令下载想要下载的大容量数据文件。
在这里插入图片描述

(3)显示存储地址和完整性检测结果

服务端显示进度条,客户端下载成功后可显示存储地址和完整性检测结果,再开启新一轮连接与下载的循环。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!