实验五:实验报告
课程:程序设计与数据结构
实验内容
任务一
编写MyBC.java实现中缀表达式转后缀表达式的功能
编写MyDC.java实现从上面功能中获取的表达式中实现后缀表达式求值的功能
package lib;
import java.util.ArrayList;
import java.util.Stack;
/**
* Created by dell on 2017/6/6.
*/
public class MyDC {
public static String main(String exp) {
ArrayList<String> expLst = getStringList(exp);
ArrayList<String> DClst = getRPN(expLst);
StringBuffer result = new StringBuffer();
for(String i:DClst)result.append(i + " ");
return result.toString();
}
private static ArrayList<String> getStringList(String str) {
ArrayList<String> result = new ArrayList<String>();
String num = "";
for (String item : str.split(" ")) result.add(item);
return result;
}
private static ArrayList<String> getRPN(ArrayList<String> inOrderList) {
ArrayList<String> RPN = new ArrayList<String>();
Stack<String> stack = new Stack<String>();
for (String item : inOrderList) {
if (Character.isDigit(item.charAt(0))) RPN.add(item);
else {
while (!stack.isEmpty() && compare(stack.peek(), item)) RPN.add(stack.pop());
stack.push(item);
}
}
while (!stack.isEmpty()) RPN.add(stack.pop());
return RPN;
}
private static boolean compare(String peek, String cur) {
if ("*".equals(peek) && ("/".equals(cur) || "*".equals(cur) || "+".equals(cur) || "-".equals(cur))) {
return true;
} else if ("/".equals(peek) && ("/".equals(cur) || "*".equals(cur) || "+".equals(cur) || "-".equals(cur))) {
return true;
} else if ("+".equals(peek) && ("+".equals(cur) || "-".equals(cur))) {
return true;
} else if ("-".equals(peek) && ("+".equals(cur) || "-".equals(cur))) {
return true;
}
return false;
}
}
任务二
结对编程:一人负责客户端,另一人负责服务器
注意责任归宿,要会通过测试证明自己没有问题
基于Java Socket实现客户端/服务器功能,传输方式用TCP
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
客户端显示服务器发送过来的结果
任务三
客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
其他要求同任务二
Crypt.java
package aes;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
/**
* Created by dell on 2017/6/6.
*/
public class Crypt {
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");// 创建密码器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
byte[] result = cipher.doFinal(byteContent);
return result; // 加密
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] result = cipher.doFinal(content);
return result;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length()/2];
for (int i = 0;i< hexStr.length()/2; i++) {
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
Client.java
package aes;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/*
* 客户端
*/
public class Client {
public static void main(String[] args) {
try {
String psw = "ncaiuscibabc";
Scanner sc = new Scanner(System.in);
Socket socket=new Socket("localhost", 8765);
OutputStream os=socket.getOutputStream();
PrintWriter pw=new PrintWriter(os);
String encryption = Crypt.parseByte2HexStr(Crypt.encrypt(sc.nextLine(),psw));
pw.write(encryption);
pw.flush();
socket.shutdownOutput();
InputStream is=socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String info=null;
while((info=br.readLine())!=null){
System.out.println(info);
}
br.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Server.java
package aes;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8765);
Socket socket = null;
System.out.println("Init Successfully");
while(true){
socket=serverSocket.accept();
ServerThread serverThread=new ServerThread(socket);
serverThread.start();
InetAddress address=socket.getInetAddress();
System.out.println("Connect IP:"+address.getHostAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
ServerThread.java
package aes;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import lib.MyDC;
/*
* 服务器线程处理类
*/
public class ServerThread extends Thread {
// 和本线程相关的Socket
Socket socket = null;
final String psw = "ncaiuscibabc";
public ServerThread(Socket socket) {
this.socket = socket;
}
//线程执行的操作,响应客户端的请求
public void run(){
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try {
//获取输入流,并读取客户端信息
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String exp = MyDC.main(new String(Crypt.decrypt(Crypt.parseHexStr2Byte(br.readLine()),psw)));
socket.shutdownInput();//关闭输入流
//获取输出流,响应客户端的请求
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write(exp);
pw.flush();//调用flush()方法将缓冲输出
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//关闭资源
try {
if(pw!=null)
pw.close();
if(os!=null)
os.close();
if(br!=null)
br.close();
if(isr!=null)
isr.close();
if(is!=null)
is.close();
if(socket!=null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
任务四
客户端和服务器用DH算法进行3DES或AES算法的密钥交换
其他要求同任务三
任务五
服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
package dh;
import sun.misc.BASE64Encoder;
import java.io.*;
import java.math.BigInteger;
import java.net.*;
import java.security.*;
import java.security.spec.*;
import java.util.*;
/*
* 客户端
*/
public class Client {
public static String connect(String msg){
try{
Socket socket=new Socket("localhost", 8766);
OutputStream os=socket.getOutputStream();
PrintWriter pw=new PrintWriter(os);
pw.write(msg);
pw.flush();
socket.shutdownOutput();
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String result = br.readLine();
br.close();
is.close();
pw.close();
os.close();
socket.close();
return result;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args){
String headers[] = {"Key&&","Exp&&","&&"};
String content;
byte[] publicKey1 = null,privateKey1 = null,key=null;
try {
Map<String, Object> keyMap1 = DHCoder.initKey();
publicKey1 = DHCoder.getPublicKey(keyMap1);
privateKey1 = DHCoder.getPrivateKey(keyMap1);
}catch (Exception e){
e.printStackTrace();
}
content = headers[0] + Parser.parseByte2HexStr(publicKey1);
byte[] publicKey2 = Parser.parseHexStr2Byte(connect(content));
try {
key = DHCoder.getSecretKey(publicKey2,privateKey1);
}catch (Exception e){
e.printStackTrace();
}
try {
Scanner sc = new Scanner(System.in);
String exp = sc.nextLine();
MessageDigest md = MessageDigest.getInstance("MD5");
BASE64Encoder base64en = new BASE64Encoder();
byte[] encrypt = DHCoder.encrypt(exp.getBytes(),key);
content = headers[1] + Parser.parseByte2HexStr(encrypt) + headers[2] + base64en.encode(md.digest(exp.getBytes("utf-8")));;
String en_result = connect(content);
String result = new String(DHCoder.decrypt(Parser.parseHexStr2Byte(en_result),key));
System.out.println(result);
}catch (Exception e){
e.printStackTrace();
}
}
}
package dh;
import java.security.*;
import java.security.spec.*;
import java.util.*;
import javax.crypto.*;
import javax.crypto.interfaces.*;
import javax.crypto.spec.*;
public abstract class DHCoder {
/**
* 非对称加密密钥算法
*/
private static final String KEY_ALGORITHM = "DH";
/**
* 本地密钥算法,即对称加密密钥算法
* 可选DES、DESede或者AES
*/
private static final String SELECT_ALGORITHM = "DESede";
/**
* 密钥长度
*/
private static final int KEY_SIZE = 512;
//公钥
private static final String PUBLIC_KEY = "DHPublicKey";
//私钥
private static final String PRIVATE_KEY = "DHPrivateKey";
/**
* 初始化甲方密钥
* @return Map 甲方密钥Map
* @throws Exception
*/
public static Map<String, Object> 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<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 初始化乙方密钥
* @param key 甲方公钥
* @return Map 乙方密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey(byte[] key) throws Exception{
//解析甲方公钥
//转换公钥材料
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
//实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
//由甲方公钥构建乙方密钥
DHParameterSpec dhParameterSpec = ((DHPublicKey)pubKey).getParams();
//实例化密钥对生成器
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<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 加密
* @param data 待加密数据
* @param key 密钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception{
//生成本地密钥
SecretKey secretKey = new SecretKeySpec(key, SELECT_ALGORITHM);
//数据加密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception{
//生成本地密钥
SecretKey secretKey = new SecretKeySpec(key, SELECT_ALGORITHM);
//数据揭秘
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 构建密钥
* @param publicKey 公钥
* @param privateKey 私钥
* @return byte[] 本地密钥
* @throws Exception
*/
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(SELECT_ALGORITHM);
return secretKey.getEncoded();
}
/**
* 取得私钥
* @param keyMap 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception{
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
* @param keyMap 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception{
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
}
package dh;
/**
* Created by dell on 2017/6/7.
*/
public class Parser {
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length()/2];
for (int i = 0;i< hexStr.length()/2; i++) {
int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
package dh;
import lib.MyDC;
import sun.misc.BASE64Encoder;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.util.Map;
public class Server {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8766);
Socket socket = null;
System.out.println("Init Successfully");
byte[] key = null;
while(true){
socket = serverSocket.accept();
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
byte[] publicKey1 = null,privateKey1 = null;
try {
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String content = br.readLine();
if(content.charAt(0)=='K'){
String publicKey = content.split("&&")[1];
System.out.println(publicKey);
Map<String, Object> keyMap1 = DHCoder.initKey();
publicKey1 = DHCoder.getPublicKey(keyMap1);
privateKey1 = DHCoder.getPrivateKey(keyMap1);
key = DHCoder.getSecretKey(Parser.parseHexStr2Byte(publicKey),privateKey1);
socket.shutdownInput();
os = socket.getOutputStream();
pw = new PrintWriter(os);
if(publicKey1!= null)pw.write(Parser.parseByte2HexStr(publicKey1));
}
else {
byte[] en_exp = Parser.parseHexStr2Byte(content.split("&&")[1]);
String exp = new String(DHCoder.decrypt(en_exp,key));
MessageDigest md = MessageDigest.getInstance("MD5");
BASE64Encoder base64en = new BASE64Encoder();
String check = content.split("&&")[2];
String hash = base64en.encode(md.digest(exp.getBytes("utf-8")));
String ans = MyDC.main(exp);
socket.shutdownInput();
os = socket.getOutputStream();
pw = new PrintWriter(os);
if(check.equals(hash) && ans!= null)pw.write(Parser.parseByte2HexStr(DHCoder.encrypt(ans.getBytes(),key)));
else pw.write("Hash Failed");
}
pw.flush();
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(pw!=null)
pw.close();
if(os!=null)
os.close();
if(br!=null)
br.close();
if(isr!=null)
isr.close();
if(is!=null)
is.close();
if(socket!=null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
InetAddress address=socket.getInetAddress();
System.out.println("Connect IP:"+address.getHostAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
实验中遇到的问题
DH算法中客户端和服务端需要两次通信才能生成结果,但是示例代码都是进行无状态通信,在一个循环体内接受两次通信再进行下一次循环来解决。但是实际上可以把Key存储在数据库中来解决,因为这种方法是不支持多客户端的。
实验心得
学习了Java网络编程和密码学的相关知识。