2018-2019-2 实验五 《网络编程与安全》实验报告
课程:Java2实用教程 | 班级: | 姓名: | 学号: |
---|---|---|---|
成绩: | 指导教师: | 实验日期:5月31日 | |
实验密级: | 预习程度: | 实验时间: | |
仪器组次: | 必修/选修:选修 | 实验序号: |
目录
实验名称:实验五 网络编程与安全
实验仪器:
名称 | 型号 | 数量 |
---|---|---|
PC端 | 1 |
实验内容、步骤与体会:
零、"两人一组"结对对象
一、实验五 网络编程与安全-1
两人一组结对编程:
0. 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA
- 结对实现中缀表达式转后缀表达式的功能 MyBC.java
- 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
- 上传测试代码运行结果截图和码云链接
代码
(代码已折叠)
MyBC.java
复制
MyDC.java
复制
import java.util.StringTokenizer;
import java.util.Stack;
public class MyDC {
/**
* constant for addition symbol
*/
private final char ADD = '+';
/**
* constant for subtraction symbol
*/
private final char SUBTRACT = '-';
/**
* constant for multiplication symbol
*/
private final char MULTIPLY = '*';
/**
* constant for division symbol
*/
private final char DIVIDE = '/';
/**
* the stack
*/
private Stack stack;
public MyDC() {
stack = new Stack ( );
}
public int evaluate(String expr) {
int op1, op2, result = 0;
String token;
StringTokenizer tokenizer = new StringTokenizer (expr);
while (tokenizer.hasMoreTokens ( )) {
token = tokenizer.nextToken ( );
//如果是运算符,调用isOperator
if (isOperator(token)==true) {
op2=stack.pop();
op1=stack.pop();
//从栈中弹出操作数2
//从栈中弹出操作数1
result=evalSingleOp(token.charAt(0),op1,op2);
//根据运算符和两个操作数调用evalSingleOp计算result;
stack.push(result);
//计算result入栈;
}
else//如果是操作数
{
stack.push(Integer.parseInt(token));
}
//操作数入栈;
}
return result;
}
private boolean isOperator(String token) {
return (token.equals ("+") || token.equals ("-") ||
token.equals ("*") || token.equals ("/"));
}
private int evalSingleOp(char operation, int op1, int op2) {
int result = 0;
switch (operation) {
case ADD:
result = op1 + op2;
break;
case SUBTRACT:
result = op1 - op2;
break;
case MULTIPLY:
result = op1 * op2;
break;
case DIVIDE:
result = op1 / op2;
break;
default:return 0;
}
return result;
}
}
Client.java
复制
过程
实现后缀表达式求值的功能:
复制MyDC evaluator = new MyDC ( );
//用 Scanner 输入 evaluator 的内容
String result = evaluator.evaluate (expression);
//输出 result
截图
二、实验五 网络编程与安全-2
结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
- 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
代码
(代码已折叠)
Client.java
复制
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String args[]) {
Scanner reader = new Scanner (System.in);
System.out.println ("客户输入一个中缀表达式: ");
String str = reader.nextLine ();
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try {
mysocket = new Socket ("127.0.0.1", 2010);
in = new DataInputStream (mysocket.getInputStream ( ));
out = new DataOutputStream (mysocket.getOutputStream ( ));
out.writeUTF (str);
//in读取信息,堵塞状态
String temp = in.readUTF ( );
System.out.println ("客户收到服务器的后缀表达式:\n" + temp);
String answer = in.readUTF ( );
System.out.println ("客户收到服务器的计算结果:\n" + answer);
Thread.sleep (500);
} catch (Exception e) {
System.out.println ("服务器已断开" + e);
}
}
}
Server.java
复制
import java.io.*;
import java.net.*;
public class Server {
public static void main(String args[]) {
String question, temp, answer;
MyDC mydc = new MyDC ();
ServerSocket serverForClient = null;
Socket socketOnServer = null;
DataOutputStream out = null;
DataInputStream in = null;
try {
serverForClient = new ServerSocket (2010);
} catch (IOException e1) {
System.out.println (e1);
}
try {
System.out.println ("等待客户呼叫");
//堵塞状态,除非有客户呼叫
socketOnServer = serverForClient.accept ( );
out = new DataOutputStream (socketOnServer.getOutputStream ( ));
in = new DataInputStream (socketOnServer.getInputStream ( ));
// in读取信息,堵塞状态
question = in.readUTF ( );
System.out.println ("服务器收到客户的中缀表达式:\n" + question);
temp = MyBC.toSuffix (question);
System.out.println ("服务器将中缀表达式变形为后缀表达式:\n" +temp);
out.writeUTF (temp);
answer = String.valueOf(mydc.evaluate(temp));
out.writeUTF (answer);
Thread.sleep (500);
} catch (Exception e) {
System.out.println ("客户已断开" + e);
}
}
}
过程
1.服务器建立链接。
复制ServerSocket serverForClient = new ServerSocket (2010);;
Socket socketOnServer = null;
socketOnServer = serverForClient.accept ( );
客户端建立对应链接相连。
Socket mysocket = new Socket ("127.0.0.1", 2010);
再创建 in , out 对象,使用 in.readUTF(); out.writeUTF();
进行数据交换。
2.服务器实现把中缀表达式转化为后缀表达式的功能:
String temp = MyBC.toSuffix (question);
注:由于 MyBC 类中的 toSuffix()
方法为 public static
静态方法,可以直接通过类名调用。
3.服务器实现后缀表达式求值的功能:
复制MyDC str = new MyDC ( );
String result = str.evaluate (expression);
4.服务器通过 out.writeUTF(result);
输出 result;客户端通过 in.readUTF();
接受后,打印输出。
截图
三、实验五 网络编程与安全-3
加密结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
- 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
代码
(代码已折叠)
AES.java
复制
ServerAES.java
复制
import java.io.*;
import java.net.*;
public class ServerAES {
public static void main(String args[]) {
String miwen, temp, answer;
MyDC mydc = new MyDC ( );
ServerSocket serverForClient = null;
Socket socketOnServer = null;
DataOutputStream out = null;
DataInputStream in = null;
try {
serverForClient = new ServerSocket (2010);
} catch (IOException e1) {
System.out.println (e1);
}
try {
System.out.println ("等待客户呼叫");
//堵塞状态,除非有客户呼叫
socketOnServer = serverForClient.accept ( );
out = new DataOutputStream (socketOnServer.getOutputStream ( ));
in = new DataInputStream (socketOnServer.getInputStream ( ));
// in读取信息,堵塞状态
miwen = in.readUTF ( );
System.out.println ("服务器收到客户的密文:\n" + miwen);
String key = "123123123";
String mingwen = AES.dcodes (miwen, key);
answer = String.valueOf (mydc.evaluate (mingwen));
out.writeUTF (answer);
System.out.println ("\n**计算结果由客户端打印输出**");
Thread.sleep (500);
} catch (Exception e) {
System.out.println ("客户已断开" + e);
}
}
}
ClientAES.java
复制
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Scanner;
public class ClientAES {
public static void main(String args[]) {
Scanner reader = new Scanner (System.in);
System.out.println ("客户输入一个中缀表达式: ");
String str = reader.nextLine ( );
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try {
mysocket = new Socket ("127.0.0.1", 2010);
in = new DataInputStream (mysocket.getInputStream ( ));
out = new DataOutputStream (mysocket.getOutputStream ( ));
String key = "123123123";
//将中缀表达式变形为后缀表达式
String temp = MyBC.toSuffix (str);
System.out.println ("服务器将中缀表达式变形为后缀表达式:\n" + temp);
//输入密文,32字符密钥
String miwen = AES.ecodes (temp, key);
System.out.println ("客户发往服务器的密文:\n" + miwen + "\n");
out.writeUTF (miwen);
//in读取信息,堵塞状态
String answer = in.readUTF ( );
System.out.println ("客户收到服务器的计算结果:\n" + answer);
Thread.sleep (500);
} catch (Exception e) {
System.out.println ("服务器已断开" + e);
}
}
}
过程
1.首先,服务器和客户端协商密钥为:String key = "123123123";
,各自存在本地;
AES算法及其相关方法通过 AES.java 实现。
2.客户端实现把中缀表达式转化为后缀表达式的功能(同 网络编程与安全-2
),将后缀表达式明文加密:
复制String = MyBC.toSuffix (str);
String miwen = AES.ecodes(mingwen,key);
再发往服务器。
3.服务器将接收到的密文用密钥解密,调用方法 mydc.evaluate()
计算后缀表达式:
复制String mingwen = AES.dcodes(miwen, key);
String answer = String.valueOf(mydc.evaluate(mingwen));
4.服务器将答案发往客户端,由客户端打印输出。
截图
四、实验五 网络编程与安全-4
密钥分发结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
代码
(代码已折叠)
DH.java
复制
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* 非对称加密算法DH算法组件
* 非对称算法一般是用来传送对称加密算法的密钥来使用的,所以这里我们用DH算法模拟密钥传送
* 对称加密AES算法继续做我们的数据加解密
* @author kongqz
* */
public class DH {
//非对称密钥算法
public static final String KEY_ALGORITHM="DH";
//本地密钥算法,即对称加密算法。可选des,aes,desede
public static final String SECRET_ALGORITHM="AES";
/**
* 密钥长度,DH算法的默认密钥长度是1024
* 密钥长度必须是64的倍数,在512到1024位之间
* */
private static final int KEY_SIZE=512;
//公钥
private static final String PUBLIC_KEY="DHPublicKey";
//私钥
private static final String PRIVATE_KEY="DHPrivateKey";
/**
* 初始化甲方密钥
* @return Map 甲方密钥的Map
* */
public static Map initKey() throws Exception{
//实例化密钥生成器
KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化密钥生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成密钥对
KeyPair keyPair=keyPairGenerator.generateKeyPair();
//甲方公钥
DHPublicKey publicKey=(DHPublicKey) keyPair.getPublic();
//甲方私钥
DHPrivateKey privateKey=(DHPrivateKey) keyPair.getPrivate();
//将密钥存储在map中
Map keyMap=new HashMap();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 初始化乙方密钥
* @param key 甲方密钥(这个密钥是通过第三方途径传递的)
* @return Map 乙方密钥的Map
* */
public static Map initKey(byte[] key) throws Exception{
//解析甲方的公钥
//转换公钥的材料
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//产生公钥
PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);
//由甲方的公钥构造乙方密钥
DHParameterSpec dhParamSpec=((DHPublicKey)pubKey).getParams();
//实例化密钥生成器
KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
//初始化密钥生成器
keyPairGenerator.initialize(dhParamSpec);
//产生密钥对
KeyPair keyPair=keyPairGenerator.genKeyPair();
//乙方公钥
DHPublicKey publicKey=(DHPublicKey)keyPair.getPublic();
//乙方私钥
DHPrivateKey privateKey=(DHPrivateKey)keyPair.getPrivate();
//将密钥存储在Map中
Map keyMap=new HashMap();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 加密
* @param data 待加密数据
* @param key 密钥
* @return byte[] 加密数据
* */
public static byte[] encrypt(byte[] data,byte[] key) throws Exception{
//生成本地密钥
SecretKey secretKey=new SecretKeySpec(key,SECRET_ALGORITHM);
//数据加密
Cipher cipher=Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
* */
public static byte[] decrypt(byte[] data,byte[] key) throws Exception{
//生成本地密钥
SecretKey secretKey=new SecretKeySpec(key,SECRET_ALGORITHM);
//数据解密
Cipher cipher=Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 构建密钥
* @param publicKey 公钥
* @param privateKey 私钥
* @return byte[] 本地密钥
* */
public static byte[] getSecretKey(byte[] publicKey,byte[] privateKey) throws Exception{
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(publicKey);
//产生公钥
PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);
//初始化私钥
//密钥材料转换
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(privateKey);
//产生私钥
PrivateKey priKey=keyFactory.generatePrivate(pkcs8KeySpec);
//实例化
KeyAgreement keyAgree=KeyAgreement.getInstance(keyFactory.getAlgorithm());
//初始化
keyAgree.init(priKey);
keyAgree.doPhase(pubKey, true);
//生成本地密钥
SecretKey secretKey=keyAgree.generateSecret(SECRET_ALGORITHM);
return secretKey.getEncoded();
}
/**
* 取得私钥
* @param keyMap 密钥map
* @return byte[] 私钥
* */
public static byte[] getPrivateKey(Map keyMap){
Key key=(Key)keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
* @param keyMap 密钥map
* @return byte[] 公钥
* */
public static byte[] getPublicKey(Map keyMap) throws Exception{
Key key=(Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//生成甲方的密钥对
Map keyMap1=DH.initKey();
//甲方的公钥
byte[] publicKey1=DH.getPublicKey(keyMap1);
//甲方的私钥
byte[] privateKey1=DH.getPrivateKey(keyMap1);
System.out.println("甲方公钥:/n"+Base64.encodeBase64String(publicKey1));
System.out.println("甲方私钥:/n"+Base64.encodeBase64String(privateKey1));
//由甲方的公钥产生的密钥对
Map keyMap2=DH.initKey(publicKey1);
byte[] publicKey2=DH.getPublicKey(keyMap2);
byte[] privateKey2=DH.getPrivateKey(keyMap2);
System.out.println("乙方公钥:/n"+Base64.encodeBase64String(publicKey2));
System.out.println("乙方私钥:/n"+Base64.encodeBase64String(privateKey2));
//组装甲方的本地加密密钥,由乙方的公钥和甲方的私钥组合而成
byte[] key1=DH.getSecretKey(publicKey2, privateKey1);
System.out.println("甲方的本地密钥:/n"+Base64.encodeBase64String(key1));
//组装乙方的本地加密密钥,由甲方的公钥和乙方的私钥组合而成
byte[] key2=DH.getSecretKey(publicKey1, privateKey2);
System.out.println("乙方的本地密钥:/n"+Base64.encodeBase64String(key2));
System.out.println("================密钥对构造完毕,开始进行加密数据的传输=============");
String str="密码交换算法";
System.out.println("/n===========甲方向乙方发送加密数据==============");
System.out.println("原文:"+str);
System.out.println("===========使用甲方本地密钥对进行数据加密==============");
//甲方进行数据的加密
byte[] code1=DH.encrypt(str.getBytes(), key1);
System.out.println("加密后的数据:"+Base64.encodeBase64String(code1));
System.out.println("===========使用乙方本地密钥对数据进行解密==============");
//乙方进行数据的解密
byte[] decode1=DH.decrypt(code1, key2);
System.out.println("乙方解密后的数据:"+new String(decode1)+"/n/n");
System.out.println("===========反向进行操作,乙方向甲方发送数据==============/n/n");
str="乙方向甲方发送数据DH";
System.out.println("原文:"+str);
//使用乙方本地密钥对数据进行加密
byte[] code2=DH.encrypt(str.getBytes(), key2);
System.out.println("===========使用乙方本地密钥对进行数据加密==============");
System.out.println("加密后的数据:"+Base64.encodeBase64String(code2));
System.out.println("=============乙方将数据传送给甲方======================");
System.out.println("===========使用甲方本地密钥对数据进行解密==============");
//甲方使用本地密钥对数据进行解密
byte[] decode2=DH.decrypt(code2, key1);
System.out.println("甲方解密后的数据:"+new String(decode2));
}
}
ClientDH.java
复制
import org.apache.commons.codec.binary.Base64;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Scanner;
public class ClientDH {
public static void main(String args[]) {
String cipherText, plainText, answer;
Scanner reader = new Scanner (System.in);
System.out.println ("客户输入一个中缀表达式: ");
String str = reader.nextLine ();
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try {
mysocket = new Socket ("127.0.0.1", 2010);
in = new DataInputStream (mysocket.getInputStream ( ));
out = new DataOutputStream (mysocket.getOutputStream ( ));
//设立AES算法的32字符密钥,输入密文与密钥
String AES_Key = "123123123";
//将中缀表达式变形为后缀表达式
plainText = MyBC.toSuffix (str);
System.out.println ("后缀表达式明文:\n" + plainText);
//将后缀表达式明文通过AES加密,并将后缀表达式密文发往客户端
cipherText = AES.ecodes (plainText, AES_Key);
System.out.println ("后缀表达式密文:\n" + cipherText + "\n");
//out发送信息
out.writeUTF (cipherText);
//对AES算法的32字符密钥进行DH算法加密
//生成客户端的密钥对
Map keyMap1 = DH.initKey ( );
//客户端的公钥
byte[] publicKey1 = DH.getPublicKey (keyMap1);
//客户端的私钥
byte[] privateKey1 = DH.getPrivateKey (keyMap1);
System.out.println ("客户端公钥:/n" + Base64.encodeBase64String (publicKey1));
System.out.println ("客户端私钥:/n" + Base64.encodeBase64String (privateKey1));
String tempKey1 = Base64.encodeBase64String (publicKey1);
//out发送信息
out.writeUTF (tempKey1);
//组装客户端的本地加密密钥,由服务器的公钥和客户端的私钥组合而成
//in读取信息,堵塞状态
String tempKey2 = in.readUTF ( );
byte[] publicKey2 = Base64.decodeBase64 (tempKey2);
System.out.println ("服务器公钥:/n" + Base64.encodeBase64String (publicKey1));
byte[] key1 = DH.getSecretKey (publicKey2, privateKey1);
System.out.println ("客户端的本地密钥:/n" + Base64.encodeBase64String (key1));
//客户端使用本地密钥对AES_Key进行消息加密,并发给服务器
byte[] code1 = DH.encrypt (AES_Key.getBytes ( ), key1);
System.out.println ("客户端使用本地密钥对AES_Key进行加密后的数据:" + Base64.encodeBase64String (code1));
//out发送信息,333333
out.writeUTF (Base64.encodeBase64String (code1));
//接受服务器的计算结果
answer = in.readUTF ( );
System.out.println ("\n**计算由服务器进行**\n\n客户收到服务器的计算结果:\n" + answer);
Thread.sleep (500);
} catch (Exception e) {
System.out.println ("服务器已断开" + e);
}
}
}
ServerDH.java
复制
过程
在 网络编程与安全-3
基础上,利用成熟的 DH 算法(实现于 DH.java)实现 AES 密钥客户端和服务器加解密,具体步骤如下:
复制//生成客户端的密钥对
Map<String,Object> keyMap1=DH.initKey();
//客户端的公钥
byte[] publicKey1=DH.getPublicKey(keyMap1);
//客户端的私钥
byte[] privateKey1=DH.getPrivateKey(keyMap1);
//由客户端的公钥产生的密钥对
Map<String,Object> keyMap2=DH.initKey(publicKey1);
byte[] publicKey2=DH.getPublicKey(keyMap2);
byte[] privateKey2=DH.getPrivateKey(keyMap2);
//组装客户端的本地加密密钥,由服务器的公钥和客户端的私钥组合而成
byte[] key1=DH.getSecretKey(publicKey2, privateKey1);
//组装服务器的本地加密密钥,由客户端的公钥和服务器的私钥组合而成
byte[] key2=DH.getSecretKey(publicKey1, privateKey2);
//客户端进行数据的加密
byte[] code1=DH.encrypt(str.getBytes(), key1);
//服务器进行数据的解密
byte[] decode1=DH.decrypt(code1, key2);
//使用服务器本地密钥对数据进行加密
byte[] code2=DH.encrypt(str.getBytes(), key2);
//客户端使用本地密钥对数据进行解密
byte[] decode2=DH.decrypt(code2, key1);
截图
五、实验四 Android程序设计-5
完整性校验结对编程:1人负责客户端,一人负责服务器
0. 注意责任归宿,要会通过测试证明自己没有问题
- 基于Java Socket实现客户端/服务器功能,传输方式用TCP
- 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
- 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
- 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
- 客户端显示服务器发送过来的结果
- 上传测试结果截图和码云链接
代码
(代码已折叠)
MD5.java
复制
import java.security.MessageDigest;
public class MD5 {
public static String numberMD5(String plainText) throws Exception {
String x = plainText;
MessageDigest m = MessageDigest.getInstance ("MD5");
m.update (x.getBytes ("UTF8"));
byte s[] = m.digest ( );
String result = "";
for (int i = 0; i < s.length; i++) {
result += Integer.toHexString ((0x000000ff & s[i]) | 0xffffff00).substring (6);
}
return result;
}
}
ClientMD5.java
复制
import org.apache.commons.codec.binary.Base64;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Scanner;
public class ClientMD5 {
public static void main(String args[]) {
String cipherText, plainText, answer;
Scanner reader = new Scanner (System.in);
System.out.println ("客户输入一个中缀表达式: ");
String str = reader.nextLine ();
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try {
mysocket = new Socket ("127.0.0.1", 2010);
in = new DataInputStream (mysocket.getInputStream ( ));
out = new DataOutputStream (mysocket.getOutputStream ( ));
//设立AES算法的32字符密钥,输入密文与密钥
String AES_Key = "123123123";
//将中缀表达式变形为后缀表达式
plainText = MyBC.toSuffix (str);
System.out.println ("后缀表达式明文:\n" + plainText);
//将后缀表达式明文通过AES加密,并将后缀表达式密文发往服务器
cipherText = AES.ecodes (plainText, AES_Key);
System.out.println ("后缀表达式密文:\n" + cipherText + "\n");
//out发送信息
out.writeUTF (cipherText);
//对AES算法的32字符密钥进行DH算法加密
//生成客户端的密钥对
Map keyMap1 = DH.initKey ( );
//客户端的公钥
byte[] publicKey1 = DH.getPublicKey (keyMap1);
//客户端的私钥
byte[] privateKey1 = DH.getPrivateKey (keyMap1);
System.out.println ("客户端公钥:/n" + Base64.encodeBase64String (publicKey1));
System.out.println ("客户端私钥:/n" + Base64.encodeBase64String (privateKey1));
String tempKey1 = Base64.encodeBase64String (publicKey1);
//out发送信息
out.writeUTF (tempKey1);
//组装客户端的本地加密密钥,由服务器的公钥和客户端的私钥组合而成
//in读取信息,堵塞状态
String tempKey2 = in.readUTF ( );
byte[] publicKey2 = Base64.decodeBase64 (tempKey2);
System.out.println ("服务器公钥:/n" + Base64.encodeBase64String (publicKey1));
byte[] key1 = DH.getSecretKey (publicKey2, privateKey1);
System.out.println ("客户端的本地密钥:/n" + Base64.encodeBase64String (key1));
//客户端使用本地密钥对AES_Key进行消息加密,并发给服务器
byte[] code1 = DH.encrypt (AES_Key.getBytes ( ), key1);
System.out.println ("客户端使用本地密钥对AES_Key进行加密后的数据:" + Base64.encodeBase64String (code1));
//out发送信息
out.writeUTF (Base64.encodeBase64String (code1));
//计算后缀表达式明文的MD5值,并发往服务器
String valueMD5 = MD5.numberMD5 (plainText);
System.out.println ("\n客户端计算的MD5值:" + valueMD5);
//out发送信息
out.writeUTF (valueMD5);
//接受服务器的计算结果
answer = in.readUTF ( );
System.out.println ("\n**计算由服务器进行**\n\n客户收到服务器的计算结果:\n" + answer);
Thread.sleep (500);
} catch (Exception e) {
System.out.println ("服务器已断开" + e);
}
}
}
ServerMD5.java
复制
import org.apache.commons.codec.binary.Base64;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
public class ServerMD5 {
public static void main(String args[]) {
String cipherText, plainText, answer;
MyDC mydc = new MyDC ( );
ServerSocket serverForClient = null;
Socket socketOnServer = null;
DataOutputStream out = null;
DataInputStream in = null;
try {
serverForClient = new ServerSocket (2010);
} catch (IOException e1) {
System.out.println (e1);
}
try {
System.out.println ("等待客户呼叫");
//堵塞状态,除非有客户呼叫
socketOnServer = serverForClient.accept ( );
out = new DataOutputStream (socketOnServer.getOutputStream ( ));
in = new DataInputStream (socketOnServer.getInputStream ( ));
// in读取信息,堵塞状态
cipherText = in.readUTF ( );
System.out.println ("服务器收到客户的后缀表达式密文:\n" + cipherText + "\n");
//由客户端的公钥产生的密钥对
// in读取信息,堵塞状态
String tempKey1 = in.readUTF ( );
byte[] publicKey1 = Base64.decodeBase64 (tempKey1);
System.out.println ("客户端公钥:/n" + Base64.encodeBase64String (publicKey1));
Map keyMap2 = DH.initKey (publicKey1);
byte[] publicKey2 = DH.getPublicKey (keyMap2);
byte[] privateKey2 = DH.getPrivateKey (keyMap2);
System.out.println ("服务器公钥:/n" + Base64.encodeBase64String (publicKey2));
System.out.println ("服务器私钥:/n" + Base64.encodeBase64String (privateKey2));
//为组装客户端的本地加密密钥,将服务器的公钥发给客户端
String tempKey2 = Base64.encodeBase64String (publicKey2);
//out发送信息
out.writeUTF (tempKey2);
//组装服务器的本地加密密钥,由客户端的公钥和服务器的私钥组合而成
byte[] key2 = DH.getSecretKey (publicKey1, privateKey2);
System.out.println ("服务器的本地密钥:/n" + Base64.encodeBase64String (key2))
//接受客户端的AES_Key的加密信息,对其解密
byte[] code1 = Base64.decodeBase64 (in.readUTF ( ));
byte[] decode1 = DH.decrypt (code1, key2);
String AES_Key = new String (decode1);
System.out.println ("\n服务器解密后的AES_Key数据:" + AES_Key);
//使用解密后的AES_Key对后缀表达式密文解密,并算出结果
plainText = AES.dcodes (cipherText, AES_Key);
System.out.println ("\nAES_Key对后缀表达式密文解密:" + plainText);
answer = String.valueOf (mydc.evaluate (plainText));
//接受客户端的MD5值,计算解密后后缀表达式的MD5值,并判断是否相同,不同则停止
String clicetValueMD5 = in.readUTF ();
String valueMD5 = MD5.numberMD5 (plainText);
if (!clicetValueMD5.equals (valueMD5)) {
return;
}
System.out.println ("\n服务器MD5值:"+valueMD5 +"\n**与客户端相同**");
//将计算结果发给客户端
out.writeUTF (answer);
System.out.println ("\n**结果由客户端进行输出**" );
Thread.sleep (500);
} catch (Exception e) {
System.out.println ("客户已断开" + e);
}
}
}
过程
1.实现 MD5 算法:
将参考代码 public static void main
改为可调用的静态方法(于上 MD5.java):
public static String numberMD5(String plainText)
参考代码:DigestPass.java
复制
import java.security.*;
public class DigestPass{
public static void main(String args[ ]) throws Exception{
String x=args[0];
MessageDigest m=MessageDigest.getInstance("MD5");
m.update(x.getBytes("UTF8"));
byte s[ ]=m.digest( );
String result="";
for (int i=0; i < s.length; i++) {
result+=Integer.toHexString((0x000000ff & s[i])|0xffffff00).substring(6);
}
System.out.println(result);
}
}
2.在 网络编程与安全-4
基础上,客户端将后缀表达式明文的MD5值算出,发往服务器:
String valueMD5 = MD5.numberMD5 (plainText);
。
3.服务器接受客户端MD5值,与自身算出的后缀表达式明文的MD5值比较,若相同则继续:
复制String clicetValueMD5 = in.readUTF ();
String valueMD5 = MD5.numberMD5 (plainText);
if (!clicetValueMD5.equals (valueMD5)) {
return;
}
截图
六、实验过程中遇到的问题以及解决方案
1. 问题:实际运用 DH 算法时,使用密钥超过 JDK 默认密钥大小。
复制---使用甲方本地密钥对数据进行加密---
Exception in thread "main" java.security.InvalidKeyException: Illegal key size or default parameters
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1026)
at javax.crypto.Cipher.implInit(Cipher.java:801)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1249)
at javax.crypto.Cipher.init(Cipher.java:1186)
at DHCoder.encrypt(DHCoder.java:101)
at DHTest.main(DHTest.java:56)
Process finished with exit code 1
解决方案:
因为某些国家的进口管制限制,Java发布的运行环境包中的加解密有一定的限制。比如默认不允许256位密钥的AES加解密,解决方法就是修改策略文件。
下载与JDK或JRE对应版本的jce文件包,当前机器的jdk为1.8,所以下载jce_policy-8.zip。
将解压得到的两个jar文件 local_policy.jar
和 US_export_policy.jar
也放到 %JDK_HOME%\jre\lib\security
下,进行替换。
附 jce_policy-8.zip下载链接
2. 问题:通过 out.writeUTF();
传输后无法正确接收,变为乱码
客户端:
复制//通过 `Base64` 的方法 `encodeBase64String ();` 将数组转化为字符串类型
String tempKey1 = Base64.encodeBase64String (publicKey1);
//out发送信息
out.writeUTF (tempKey1);
服务器:
复制......
//由客户端的公钥产生的密钥对
String tempKey1 = in.readUTF ();
byte [] publicKey1 = tempKey1.getBytes ();
System.out.println("客户端公钥:/n"+Base64.encodeBase64String(publicKey1));
Map<String,Object> keyMap2=DH.initKey(publicKey1);
......
报错:密钥格式不对。
解决方案:
因为 out.writeUTF(); in.readUTF ();
传输的数据类型为 String
,而不是 byte []
数组,所以不可用 getBytes ();
方法来提取。
要使用 Base64
方法 encodeBase64String ();
的对应的方法 decodeBase64 (tempKey1);
来转换数据类型。
复制String tempKey1 = in.readUTF ( );
byte[] publicKey1 = Base64.decodeBase64 (tempKey1);
System.out.println ("客户端公钥:/n" + Base64.encodeBase64String (publicKey1));
Map <String, Object> keyMap2 = DH.initKey (publicKey1);
七、代码链接
八、 PSP
步骤 | 耗时 | 百分比 |
---|---|---|
需求分析 | 10min | 7.7% |
设计 | 30min | 23.1% |
代码实现 | 50min | 38.5% |
测试 | 30min | 23.1% |
分析总结 | 10min | 7.6% |
作者:Yogile
出处:https://www.cnblogs.com/Yogile/p/10938944.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构