好久没更新博客了,最近在帮老师整理项目,本来对socket接触的不多。本次不多说废话,直接说项目
项目要求:1.实现服务端和客户端的传输文件加解密,我这边实现的是服务端传输加密后的文件,客户端收到文件后解密,为了展示方便,我此次采用了AES加密方式,填充方式采用AES/CBC/PKCS5Padding。
2.实现客户端验证文件是否为服务端发送的文件,我这边采用的是md5码加密,将服务端的md5码和文件一并发送给客户端,客户端对收到的文件进行md5加密,再将收到的md5和文件加密的md5进行对比。
由于代码比较长,为了方便看,此次,我仅介绍C/S端加解密,代码部分我会拆分了写。
在这里我就不给出import了,都是java的基本库
1.C/S加解密传输
1.1客户端
public class FileTransferClient{
public static void main(String [] args){
try{
Socket s=new Socket("127.0.0.1",9500);//本机地址、端口号
DataInputStream so=new DataInputStream(s.getInputStream());//输入流创建,接收服务端发送信息
System.out.println(so.readUTF());
FileOutputStream f=new FileOutputStream("D:\\test.txt");//文件存储地址
int p;
System.out.println("please wait........");
//写文件
while((p=so.read())!=-1){
f.write(p);
}
//打开文件,并解密其中内容,再重写文件
InputStream fi=new FileInputStream("D:/test.txt");
//并解密其中内容
byte[] bytes=MyAESUtils.toByteArray(fi);
byte[] debytes=MyAESUtils.decrypt(bytes);
String temp=new String(debytes);
fi.close();
System.out.println("解密内容:"+temp);
//重写并解密文件
FileOutputStream fos=new FileOutputStream("D:/test.txt");
fos.write(debytes);
s.close();
so.close();
f.flush();
f.close();
System.out.println("complete!");
}catch(IOException e){
System.out.println("file transfer failed!");
}
}
}
说明:客户端采用的是先接收服务端传送的文件,然后由于接收的文件是密文,所以提取文件中的内容进行解密,解密后重写文件。
注意:如果非txt文件,mp3格式等,由于涉及到文件转换,要讲内容转成byte,所以请自行添加len测量文件的字节数,不然会出现解密后的mp3等文件无法播放的问题。
1.2服务端
public class FileTransferServer{
private static int port=9500;
public static void main(String [] args)throws Exception{
ServerSocket s=new ServerSocket(port);
try {
//监听
Socket s1=s.accept();
System.out.println("Client connection success!");
//构建文件输出流
DataOutputStream so=new DataOutputStream(s1.getOutputStream());
so.writeUTF("FileName:1111.txt"+"\n"+"ServerIp:"+s.getInetAddress()+"\n"+"ServerPort:"+s.getLocalPort());
//读文件
FileInputStream f=new FileInputStream("E:/1111.txt");
//提取文件内容转成密文
String list=FileUtils.readFileToString(new File("E:/1111.txt"), "UTF-8");
//System.out.println("原文byte:" + list.length());
//int len=f.available();
byte[] bytes=new byte[list.length()];
bytes=MyAESUtils.encrypt(list);
so.write(bytes);
s.close();
f.close();
s1.close();
so.flush();
so.close();
} catch (Exception e) {
e.printStackTrace();
}{
}
}
}
说明:服务端需要进行文件的读取,明文转换,发送密文三个步骤,比较简单,因此不多赘述。
注意:如果是大型文件进行加解密,切记要切分文件块,然后再加解密,具体的百度一下,这类切分蛮多的。
1.3 AES加解密
public class MyAESUtils {
//static SecretKeySpec key = new SecretKeySpec(null, "AES");
private static final String ENCODING = "UTF-8";
static String ivstr="abcdefghijklmnop";
static byte[] iv=ivstr.getBytes();
public static byte[] encrypt(String content) {
try {
//byte[] iv=new byte[10];
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128,new SecureRandom("1234567890123456".getBytes()));
SecretKey secretKey = kgen.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, secretKey,new IvParameterSpec(iv));
byte[] result = cipher.doFinal(byteContent);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] content) {
try {
KeyGenerator kgen=KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom("1234567890123456".getBytes()));
SecretKey secretKey = kgen.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey,new IvParameterSpec(iv));
byte[] result = cipher.doFinal(content);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 4];
int n = 0;
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
}
注意:直接上代码了,AES不需要理解原理,你只需要知道,你要提供一个key通过随机种子生成密钥。在这部分我遇到了个挺麻烦的问题,就是一开始代码是这个样子的
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
解密的时候一直提示我解密的密钥错误,我明明把随机种子的key给锁定了的,加解密的密钥却还是不同,最后,查了半天, 才发现原来是我没有锁定初始化变量iv。导致两个端初始化变量iv不同,所以cipher,init()的结果也不同。
工作总结:虽然这是老师的结题项目,但想想确实有点累,做这个花了3天时间,还加了2个通宵,现在仔细想想,其实cocket通信也不难,其实很多东西都是细节问题,就比如刚刚的初始化变量iv,就这个问题卡了我将近2天。好了,问题解决了,可以结题了,也祝大家学习愉快!