任务1:实现中缀表达式转后缀表达式,后缀表达式求值java
任务2:经过客户端、服务器实现任务1git
任务3:在任务2的基础上,将客户端产生的后缀表达式加密后传给服务器正则表达式
任务4:在任务3的基础上,将用来加、解密的密钥经过DH算法进行交换算法
任务5:在任务4的基础上,将后缀表达式的摘要值传给服务器,服务器经过解密后计算后缀表达式的MD5值,比对是否与客户端传来的值相同编程
任务6:将上述过程用Android实现,更多详情请参见学号 20175313 《实验五 网络编程与安全》实验报告番外篇数组
本次实验有5个小步,每一步都是创建在前一步的基础上进行的拓展。安全
任务1:中缀转后缀,用后缀表达式规则进行计算服务器
中缀转后缀网络
while (tokenizer.hasMoreTokens()){ token=tokenizer.nextToken(); if (isOperator(token)){ if (!OpStack.empty()){ if(judgeValue(token)>judgeValue(OpStack.peek()) && !token.equals(")") || token.equals("(")) OpStack.push(token); else if (token.equals(")")){ //若是遇到一个右括号则将栈元素弹出,将弹出的操做符输出直到遇到左括号为止 while (!OpStack.peek().equals("(")) output=output.concat(OpStack.pop()+" ");//弹出左括号上面的全部东西 OpStack.pop();//弹出左括号 } else { while (!OpStack.empty() && judgeValue(token)<=judgeValue(OpStack.peek())){ ////若是遇到其余任何操做符,从栈中弹出这些元素直到遇到发现更低优先级的元素或栈空为止 output=output.concat(OpStack.pop()+" "); } OpStack.push(token); } } else OpStack.push(token);//若是栈空则直接将遇到的操做符送入栈中,第一个不可能为右括号 } else { output=output.concat(token+" ");//若是遇到操做数就直接输出 } } while (!OpStack.empty()){ //若是读到了输入分末尾,则将占中全部元素依次弹出 output=output.concat(OpStack.pop()+" "); }
while (tokenizer.hasMoreTokens()){ token=tokenizer.nextToken(); if(isOperator(token)){ //遇到操做符,两个操做数出栈 op2=stack.pop(); op1=stack.pop(); result=calcSingle(op1,op2,token);//两个操做数进行相关运算 stack.push(new Integer(result));//运算结果进栈 } else { //遇到数字进栈 stack.push(new Integer(token)); } }
MyBC myBC = new MyBC(); output = myBC.getEquation(formula);
MyDC myDC = new MyDC(); result = myDC.calculate(formula);
任务2:客户端输入中缀表达式并转化成后缀表达式发送给服务器,服务器计算后缀表达式,将结果发送给客户端
String formula = scanner.nextLine(); String regex = ".*[^0-9|+|\\-|*|÷|(|)|\\s|/].*"; if(formula.matches(regex)){ System.out.println("输入了非法字符"); System.exit(1); }
任务3:客户端获得的后缀表达式通过3DES或AES加密后将密文发送给服务端,服务端收到后将其进行解密,而后再计算后缀表达式的值,把结果发送给客户端
KeyGenerator kg=KeyGenerator.getInstance("AES");
kg.init(128);
SecretKey k=kg.generateKey( );
Cipher cp=Cipher.getInstance("AES");
cp.init(Cipher.ENCRYPT_MODE,k);
byte []ctext=cp.doFinal(ptext);
String s = new String(ctext);
这种构造方法的话可能会出现javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
报错。String out1 = B_H.parseByte2HexStr(ctext); out.writeUTF(out1);
String cformula = in.readUTF();//读取密文 byte cipher[] = H_B.parseHexStr2Byte(cformula);
byte kb[] = k.getEncoded();
SecretKeySpec key = new SecretKeySpec(decryptKey,"AES");//得到密钥
任务4:使用DH算法进行密钥3DES或AES的密钥交换
KeyPairGenerator kpg=KeyPairGenerator.getInstance("DH");
kpg.initialize(1024);
KeyPair kp=kpg.genKeyPair( );
PublicKey pbkey=kp.getPublic( );
PrivateKey prkey=kp.getPrivate( );
KeyAgreement ka=KeyAgreement.getInstance("DH");
ka.init(prk);
ka.doPhase(pbk,true);
byte[ ] sb=ka.generateSecret();
SecretKeySpec k=new SecretKeySpec(sb,"AES");
客户端
import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.security.Key; import java.util.Scanner; import java.net.*; public class Client5_4 { public static void main(String[] args) { String mode = "AES"; //客户端让用户输入中缀表达式,而后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式经过网络发送给服务器 Scanner scanner = new Scanner(System.in); 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()); System.out.println("请输入要计算的题目:"); String formula = scanner.nextLine(); String regex = ".*[^0-9|+|\\-|*|÷|(|)|\\s|/].*"; if (formula.matches(regex)) { System.out.println("输入了非法字符"); System.exit(1); } String output = ""; MyBC myBC = new MyBC(); try { //中缀转后缀 output = myBC.getEquation(formula); } catch (ExprFormatException e) { System.out.println(e.getMessage()); System.exit(1); } //使用AES进行后缀表达式的加密 KeyGenerator kg = KeyGenerator.getInstance(mode); kg.init(128); SecretKey k = kg.generateKey();//生成密钥 byte mkey[] = k.getEncoded(); Cipher cp = Cipher.getInstance(mode); cp.init(Cipher.ENCRYPT_MODE, k); byte ptext[] = output.getBytes("UTF8"); byte ctext[] = cp.doFinal(ptext); //将加密后的后缀表达式传送给服务器 String out1 = B_H.parseByte2HexStr(ctext); out.writeUTF(out1); //建立客户端DH算法公、私钥 Key_DH.createPubAndPriKey("Clientpub.txt","Clientpri.txt"); //将客户端公钥传给服务器 FileInputStream fp = new FileInputStream("Clientpub.txt"); ObjectInputStream bp = new ObjectInputStream(fp); Key kp = (Key) bp.readObject(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(kp); byte[] kb = baos.toByteArray(); String pop = B_H.parseByte2HexStr(kb); out.writeUTF(pop); Thread.sleep(1000); //接收服务器公钥 String push = in.readUTF(); byte np[] = H_B.parseHexStr2Byte(push); ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream (np)); Key k2 = (Key)ois.readObject();; FileOutputStream f2 = new FileOutputStream("Serverpub.txt"); ObjectOutputStream b2 = new ObjectOutputStream(f2); b2.writeObject(k2); //生成共享信息,并生成AES密钥 SecretKeySpec key = KeyAgree.createKey("Serverpub.txt", "Clientpri.txt"); //对加密后缀表达式的密钥进行加密,并传给服务器 cp.init(Cipher.ENCRYPT_MODE, key); byte ckey[] = cp.doFinal(mkey); String Key = B_H.parseByte2HexStr(ckey); out.writeUTF(Key); //接收服务器回答 String s = in.readUTF(); System.out.println("客户收到服务器的回答:" + s); } catch (Exception e) { System.out.println("服务器已断开" + e); } } }
服务器
import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.security.Key; public class Server5_4 { public static void main(String[] args) { String mode = "AES"; ServerSocket serverForClient = null; Socket socketOnServer = null; DataOutputStream out = null; DataInputStream in = null; try{ serverForClient = new ServerSocket(2010); }catch (IOException e1){ System.out.println(e1); } String result; try{ System.out.println("等待客户呼叫:"); socketOnServer = serverForClient.accept(); out = new DataOutputStream(socketOnServer.getOutputStream()); in = new DataInputStream(socketOnServer.getInputStream()); //接收加密后的后缀表达式 String cformula = in.readUTF(); byte cipher[] = H_B.parseHexStr2Byte(cformula); //接收Client端公钥 String push = in.readUTF(); byte np[] = H_B.parseHexStr2Byte(push); //生成服务器共、私钥 Key_DH.createPubAndPriKey("Serverpub.txt","Serverpri.txt"); //将服务器公钥传给Client端 FileInputStream fp = new FileInputStream("Serverpub.txt"); ObjectInputStream bp = new ObjectInputStream(fp); Key kp = (Key) bp.readObject(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(kp); byte[] kb = baos.toByteArray(); String pop = B_H.parseByte2HexStr(kb); out.writeUTF(pop); Thread.sleep(1000); //生成共享信息,并生成AES密钥 SecretKeySpec key = KeyAgree.createKey("Serverpub.txt","Clientpri.txt"); String k = in.readUTF();//读取加密后密钥 byte[] encryptKey = H_B.parseHexStr2Byte(k); //对加密后密钥进行解密 Cipher cp = Cipher.getInstance(mode); cp.init(Cipher.DECRYPT_MODE,key); byte decryptKey [] = cp.doFinal(encryptKey); //对密文进行解密 SecretKeySpec plainkey=new SecretKeySpec(decryptKey,mode); cp.init(Cipher.DECRYPT_MODE, plainkey); byte []plain=cp.doFinal(cipher); //计算后缀表达式结果 String formula = new String(plain); MyDC myDC = new MyDC(); try{ result = myDC.calculate(formula); //后缀表达式formula调用MyDC进行求值 }catch (ExprFormatException e){ result = e.getMessage(); }catch (ArithmeticException e0){ result = "Divide Zero Error"; } //将计算结果传给Client端 out.writeUTF(result); }catch (Exception e){ System.out.println("客户已断开"+e); } } }
任务5:客户端利用MD5算法计算其摘要值clientMD5传送给服务器,服务器经过解密获得的明文利用MD5算法计算其摘要值serverMD5,比较两者是否相等
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); }
任务1
任务2
任务3
任务4
任务5
使用byte []ctext=cp.doFinal(ptext);
进行加密,产生的密文是byte[]数组,可是传送给服务器时所使用的参数应该是String类型,若是直接使用String s = new String(ctext);
这种构造方法会出现javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
报错。
缘由:加密后的byte数组是不能强制转换成字符串的,换言之:字符串和byte数组在这种状况下不是互逆的;要避免这种状况,咱们须要作一些修订,能够考虑将二进制数据转换成十六进制表示。
String out1 = B_H.parseByte2HexStr(ctext); out.writeUTF(out1);
String cformula = in.readUTF();//读取密文 byte cipher[] = H_B.parseHexStr2Byte(cformula);