1-1 假设Tom和Jerry利用Java UDP进行聊天,请为他们编写程序。具体如下:
(1)、Tom和Jerry聊天的双方都应该具有发送端和接收端;
(2)、利用DatagramSocket与DatagramPacket;
(3)、实现 java.lang.Runnable类,重写 run()方法。
(4)、Tom对发送的内容(发送的内容为 学生本人的名字+学生本人的学号)使用对称/非对称加密算法(密钥可以自主分配,不需要权威机构来分配)来加密,然后将加密后的密文发送给Jerry;Jerry对收到的密文进行解密,还原成明文内容(即 学生本人的名字+学生本人的学号)
死亡实验,整了半天才整出一个对称加密。纯新手,可能有很多错误,希望大佬在评论区及时指出!
首先,关于AES算法和对称加密的概念部分,这里不多赘述,详细可以参考这位大佬的博客。https://www.cnblogs.com/wushaopei/p/11979154.html
环境配置
首先要下载Apache Commons Codec jar包,在下列地址,下载bin文件就可以了。
http://commons.apache.org/proper/commons-codec/download_codec.cgi
然后是导入Eclipse,我在这里导入之后疯狂报错,注意要把codc的jar包导入classpath中才行。
配置如下图
接下来具体实现
AES算法加密
我用了一个AES类来实现密钥生成,得到密钥,加密和解密的过程。
在实现过程中遇到的最大问题就是发送线程随机生成密钥后,如何将生成的密钥给接收线程。
在看别人的博客的过程中发现有很多大佬用的是在初始化的同时传入一个随一个初始种子。按道理来说传入的初始值相同,生成的密钥就应该相同。
然而实际操作的时候并没有成功,解密的代码中这一行会一直报一个Given final block not properly padded的错
报错的原因应该是生成的密钥不同,然而具体什么原因导致并不清楚,所以只能换一种方法,我选择将生成的密钥存入一个pkey.txt文件,解密的时候直接读进来就OK了。
AES类代码如下:
package Chat_UDP;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
//import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
//import org.bouncycastle.util.encoders.Base64;
public class AES {
static String string = "杨丽冰 201831064402";
//key
KeyGenerator keyGenerator;
//密钥对象
SecretKey secretKey;
//密钥
byte[] keyBytes;
//key
Key key;
//生成密钥
public Key AESInit() throws NoSuchAlgorithmException, IOException {
//生成key
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
//设置密钥长度
keyGenerator.init(128);
//生成密钥对象
SecretKey secretKey = keyGenerator.generateKey();
//获取密钥
byte[] keyBytes = secretKey.getEncoded();
//key转换
key = new SecretKeySpec(keyBytes,"AES");
//将密钥存入文件pkey.txt
File file=new File("pkey.txt");
OutputStream outputStream=new FileOutputStream(file);
outputStream.write(keyBytes);
outputStream.close();
return key;
}
//得到密钥
public Key getKey() throws NoSuchAlgorithmException, IOException {
//从文件中取出密钥
File file=new File("pkey.txt");
InputStream inputStream = new FileInputStream(file);
keyBytes = inputStream.readAllBytes();
key = new SecretKeySpec(keyBytes,"AES");
return key;
}
public String encryptAES(byte[] s,Key key){
byte[] result = null;
try {
//加密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
//初始化,设置为加密
cipher.init(Cipher.ENCRYPT_MODE, key);
result = cipher.doFinal(s);
System.out.println("AES对称算法加密后的密文为: " + Base64.encodeBase64String(result));
} catch (Exception e) {
e.printStackTrace();
}
return Base64.encodeBase64String(result);
}
public String decryptAES(byte[] s,Key key){
byte[] result = null;
try {
//加密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
//初始化,设置为解密
cipher.init(Cipher.DECRYPT_MODE, key);
result = cipher.doFinal(s);
System.out.println("AES对称算法解密后的明文为:" + new String(result));
} catch (Exception e) {
e.printStackTrace();
}
return new String(result);
}
}
通过下列代码Test我们可以测试一下AES的加解密功能,就能看到已经成功实现JAVA 用对称加密AES算法加密,接下来只要将加解密写入UDP通讯的过程就可以啦。
测试类Test.java
package Chat_UDP;
import java.io.IOException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
public class Test {
public static void main(String []args) throws NoSuchAlgorithmException, IOException {
AES aes = new AES();
Key key = aes.AESInit();
Key key1 = aes.getKey();
if(key.equals(key1))
System.out.println("YES!密钥一致!" );
String s ="201831064402 杨丽冰";
String result = null;
//加密
result = aes.encryptAES(s.getBytes(), key);
//解密
s= aes.decryptAES(Base64.decodeBase64(result),key1 );
}
}
对UDP通讯加密
接下来只需要调用AES的方法,对于数据进行加密,在服务器或客户端初始化密钥,发送线程中对数据加密,再将将密文发出,在接收线程再把密文解密为明文就可以啦。
发送线程
```java
package Chat_UDP;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
public class Send_Thread extends Thread{
//发送的socket端
private DatagramSocket sender = null;
//待发送的目标地址
private InetSocketAddress address = null;
//从键盘输入
Scanner scan = new Scanner(System.in);
public Send_Thread(DatagramSocket sender,InetSocketAddress address)
{
this.sender = sender;
this.address = address;
}
@Override
public void run() {
// TODO Auto-generated method stub
try
{
while(true)
{
//输入待发送的内容
String input = scan.nextLine();
if(input.equals("exit"))
break;
AES aes = new AES();
Key key = aes.getKey();
System.out.println("生成的密钥key是" + key);
//对输入数据进行加密
input=aes.encryptAES(input.getBytes(), key);
byte[] data = input.getBytes();
data = input.getBytes("UTF-8");
//创建UDP数据报,发送的是加密后的信息
DatagramPacket pack = new DatagramPacket(data, data.length,address);
sender.send(pack);
}
System.out.println("Exit!");
}catch(IOException | NoSuchAlgorithmException e)
{
System.out.println("IOException");
}
}
}
接收线程
package Chat_UDP;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Date;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
public class Receive_Thread extends Thread {
private static final int MAX_RECEIVE_BUFFER = 1024;
private DatagramSocket server;
private DatagramPacket packet;
byte[] buffer = new byte[MAX_RECEIVE_BUFFER];
public Receive_Thread(DatagramSocket server)
{
this.server = server;
packet = new DatagramPacket(buffer, buffer.length);
}
@Override
public void run() {
try
{
while(true)
{
//接收数据包
server.receive(packet);
AES aes = new AES();
Key key = aes.getKey();
System.out.println("密钥key是" + key);
String s = new String(packet.getData(),packet.getOffset(),packet.getLength(),"UTF-8");
System.out.println("接收到的信息为: " + s);
//对接收的数据解密
s = aes.decryptAES(Base64.decodeBase64(s), key);
Date day=new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if(packet.getPort() == 10001)
System.out.println("Tom"+packet.getAddress()+" 说:"+s+"\t"+df.format(day));
else{
System.out.println("Jerry"+packet.getAddress()+" 说 :"+s+"\t"+df.format(day));
}
packet.setLength(buffer.length);
}
}
catch(IOException | NoSuchAlgorithmException e)
{
System.out.println("IOException");
}
}
}
服务器(TOM)
package Chat_UDP;
//杨丽冰 201831064402
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Chat_Server {
//Tom
private static final int DEST_PORT = 8888;
private static final int SEND_PORT = 10001;
private static final int RECE_PORT = 9000;
private static final String IP = "127.0.0.1";
public static void main(String[] args)
{
try{
System.out.println("TOM");
Send_Thread send_thread = null;
Receive_Thread rece_thread = null;
InetSocketAddress address = null;
//生成密钥
AES aes = new AES();
aes.AESInit();
//创建待接受数据包的目的机的端口号和IP地址
address = new InetSocketAddress(IP, DEST_PORT);
//创建发送的Socket端
DatagramSocket sendsocket = new DatagramSocket(SEND_PORT);
//创建接受的Socket端
DatagramSocket recesocket = new DatagramSocket(RECE_PORT);
//发送线程建立
send_thread = new Send_Thread(sendsocket, address);
//接受线程的建立
rece_thread = new Receive_Thread(recesocket);
send_thread.start();
rece_thread.start();
}catch(Exception e)
{
System.out.println("Exception!");
}
}
}
客户端(Jerry)
package Chat_UDP;
//杨丽冰 201831064402
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Chat_Client {
//Jerry
//声明端口号
//201831064402 杨丽冰
private static final int DEST_PORT = 9000;
private static final int SEND_PORT = 10000;
private static final int RECE_PORT = 8888;
private static final String IP = "127.0.0.1";
public static void main(String[] args)
{
try{
System.out.println("Jerry");
Send_Thread send_thread = null;
Receive_Thread rece_thread = null;
InetSocketAddress address = null;
//创建待接受数据包的目的机的端口号和IP地址
address = new InetSocketAddress(IP, DEST_PORT);
//创建发送的Socket端
DatagramSocket sendsocket = new DatagramSocket(SEND_PORT);
//创建接受的Socket端
DatagramSocket recesocket = new DatagramSocket(RECE_PORT);
//发送线程建立
send_thread = new Send_Thread(sendsocket, address);
//接受线程的建立
rece_thread = new Receive_Thread(recesocket);
send_thread.start();
rece_thread.start();
}catch(Exception e)
{
System.out.println("Exception!");
}
}
}