任務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);