• 2018-2019-2 实验五 《网络编程与安全》实验报告


    课程:Java2实用教程 班级: 姓名: 学号:
    成绩: 指导教师: 实验日期:5月31日
    实验密级: 预习程度: 实验时间:
    仪器组次: 必修/选修:选修 实验序号:

    目录

    实验名称:实验五 网络编程与安全

    实验仪器:

    名称 型号 数量
    PC端 1

    实验内容、步骤与体会:


    零、"两人一组"结对对象


    一、实验五 网络编程与安全-1

    两人一组结对编程:
    0. 参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA

    1. 结对实现中缀表达式转后缀表达式的功能 MyBC.java
    2. 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.java
    3. 上传测试代码运行结果截图和码云链接

    代码

    (代码已折叠)

    MyBC.java
      
    import java.util.*;
    import java.util.stream.Collectors;
    public class MyBC{
        private static final Map basic = new HashMap();
        static {
            basic.put('-', 1);
            basic.put('+', 1);
            basic.put('*', 2);
            basic.put('/', 2);
            basic.put('(', 0);
        }
        //中缀表达式 转 后缀表达式
    
    public static String toSuffix(String infix){
        List<String> queue = new ArrayList<String>();
        List<Character> stack = new ArrayList<Character>();
        char[] charArr = infix.trim().toCharArray();
        String standard = "*/+-()";
        char ch = '&';
        int len = 0;
        for (int i = 0; i < charArr.length; i++) {
    
            ch = charArr[i];
            if(Character.isDigit(ch)) {
                len++;
            }else if(Character.isLetter(ch)) {
                len++;
            }else if(ch == '.'){
                len++;
            }else if(Character.isSpaceChar(ch)) {
                if(len > 0) {
                    queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len, i)));
                    len = 0;
                }
                continue;
            }else if(standard.indexOf(ch) != -1) {
                if(len > 0) {
                    queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len, i)));
                    len = 0;
                }
                if(ch == '(') {
                    stack.add(ch);
                    continue;
                }
                if (!stack.isEmpty()) {
                    int size = stack.size() - 1;
                    boolean flag = false;
                    while (size >= 0 && ch == ')' && stack.get(size) != '(') {
                        queue.add(String.valueOf(stack.remove(size)));
                        size--;
                        flag = true;
                    }
                    while (size >= 0 && !flag && basic.get(stack.get(size)) >= basic.get(ch)) {
                        queue.add(String.valueOf(stack.remove(size)));
                        size--;
                    }
                }
                if(ch != ')') {
                    stack.add(ch);
                } else {
                    stack.remove(stack.size() - 1);
                }
            }
            if(i == charArr.length - 1) {
                if(len > 0) {
                    queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len+1, i+1)));
                }
                int size = stack.size() - 1;
                while (size >= 0) {
                    queue.add(String.valueOf(stack.remove(size)));
                    size--;
                }
            }
        }
        return queue.stream().collect(Collectors.joining(" "));
    }
    

    }

    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
      
    import java.util.Scanner;
    

    public class MyBCTest {
    public static void main(String[] args) {
    MyBC mybc = new MyBC ();
    MyDC mydc = new MyDC ();
    String inExpression;
    String str;
    Scanner reader = new Scanner (System.in);
    System.out.println ("Enter a expression: ");
    inExpression = reader.nextLine ();
    str = mybc.toSuffix (inExpression);
    System.out.println ("Exchange the expression by MyBC: " +str);
    System.out.println ("The calculation result of MyDC: " +mydc.evaluate(str));
    }
    }

    过程

    实现后缀表达式求值的功能:

    MyDC evaluator = new MyDC ( );
    //用 Scanner 输入 evaluator 的内容
    String result = evaluator.evaluate (expression);
    //输出 result
    

    截图


    二、实验五 网络编程与安全-2

    结对编程:1人负责客户端,一人负责服务器
    0. 注意责任归宿,要会通过测试证明自己没有问题

    1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
    2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器
    3. 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
    4. 客户端显示服务器发送过来的结果
    5. 上传测试结果截图和码云链接

    代码

    (代码已折叠)

    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 ("客户收到服务器的后缀表达式:
    " + temp);
                String answer = in.readUTF ( );
                System.out.println ("客户收到服务器的计算结果:
    " + 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 ("服务器收到客户的中缀表达式:
    " + question);
                temp = MyBC.toSuffix (question);
                System.out.println ("服务器将中缀表达式变形为后缀表达式:
    " +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. 注意责任归宿,要会通过测试证明自己没有问题

    1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
    2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器
    3. 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
    4. 客户端显示服务器发送过来的结果
    5. 上传测试结果截图和码云链接

    代码

    (代码已折叠)

    AES.java
      
    import javax.crypto.*;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.UnsupportedEncodingException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    public class AES {
        public static String ecodes(String content, String key) {
            if (content == null || content.length ( ) < 1) {
                return null;
            }
            try {
                KeyGenerator kgen = KeyGenerator.getInstance ("AES");
                SecureRandom random = SecureRandom.getInstance ("SHA1PRNG");
                random.setSeed (key.getBytes ( ));
                kgen.init (128, random);
                SecretKey secretKey = kgen.generateKey ( );
                byte[] enCodeFormat = secretKey.getEncoded ( );
                SecretKeySpec secretKeySpec = new SecretKeySpec (enCodeFormat, "AES");
                Cipher cipher = Cipher.getInstance ("AES");
                byte[] byteContent = content.getBytes ("utf-8");
                cipher.init (Cipher.ENCRYPT_MODE, secretKeySpec);
                byte[] byteRresult = cipher.doFinal (byteContent);
                StringBuffer sb = new StringBuffer ( );
                for (int i = 0; i < byteRresult.length; i++) {
                    String hex = Integer.toHexString (byteRresult[i] & 0xFF);
                    if (hex.length ( ) == 1) {
                        hex = '0' + hex;
                    }
                    sb.append (hex.toUpperCase ( ));
                }
                return sb.toString ( );
            } 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 String dcodes(String content, String key) {
        if (content == null || content.length ( ) < 1) {
            return null;
        }
        if (content.trim ( ).length ( ) < 19) {
            return content;
        }
        byte[] byteRresult = new byte[content.length ( ) / 2];
        for (int i = 0; i < content.length ( ) / 2; i++) {
            int high = Integer.parseInt (content.substring (i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt (content.substring (i * 2 + 1, i * 2 + 2), 16);
            byteRresult[i] = (byte) (high * 16 + low);
        }
        try {
            KeyGenerator kgen = KeyGenerator.getInstance ("AES");
            SecureRandom random = SecureRandom.getInstance ("SHA1PRNG");
            random.setSeed (key.getBytes ( ));
            kgen.init (128, random);
            SecretKey secretKey = kgen.generateKey ( );
            byte[] enCodeFormat = secretKey.getEncoded ( );
            SecretKeySpec secretKeySpec = new SecretKeySpec (enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance ("AES");
            cipher.init (Cipher.DECRYPT_MODE, secretKeySpec);
            byte[] result = cipher.doFinal (byteRresult);
            return new String (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;
    }
    

    }

    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 ("服务器收到客户的密文:
    " + miwen);
                String key = "123123123";
                String mingwen = AES.dcodes (miwen, key);
                answer = String.valueOf (mydc.evaluate (mingwen));
                out.writeUTF (answer);
                System.out.println ("
    **计算结果由客户端打印输出**");
                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 ("服务器将中缀表达式变形为后缀表达式:
    " + temp);
                //输入密文,32字符密钥
                String miwen = AES.ecodes (temp, key);
                System.out.println ("客户发往服务器的密文:
    " + miwen + "
    ");
                out.writeUTF (miwen);
                //in读取信息,堵塞状态
                String answer = in.readUTF ( );
                System.out.println ("客户收到服务器的计算结果:
    " + 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. 注意责任归宿,要会通过测试证明自己没有问题

    1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
    2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文发送给服务器
    3. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
    4. 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
    5. 客户端显示服务器发送过来的结果
    6. 上传测试结果截图和码云链接

    代码

    (代码已折叠)

    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 ("后缀表达式明文:
    " + plainText);
                //将后缀表达式明文通过AES加密,并将后缀表达式密文发往客户端
                cipherText = AES.ecodes (plainText, AES_Key);
                System.out.println ("后缀表达式密文:
    " + cipherText + "
    ");
                //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 ("
    **计算由服务器进行**
    
    客户收到服务器的计算结果:
    " + answer);
                Thread.sleep (500);
            } catch (Exception e) {
                System.out.println ("服务器已断开" + e);
            }
        }
    }
      
    ServerDH.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 ServerDH {
        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 ("服务器收到客户的后缀表达式密文:
    " + cipherText + "
    ");
    
            //由客户端的公钥产生的密钥对
            // in读取信息,堵塞状态
            String tempKey1 = in.readUTF ( );
            byte[] publicKey1 = Base64.decodeBase64 (tempKey1);
            System.out.println ("客户端公钥:/n" + Base64.encodeBase64String (publicKey1));
            Map <String, Object> 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 ("
    服务器解密后的AES_Key数据:" + AES_Key);
    
            //使用解密后的AES_Key对后缀表达式密文解密,并算出结果
            plainText = AES.dcodes (cipherText, AES_Key);
            System.out.println ("
    AES_Key对后缀表达式密文解密:
    " + plainText);
            answer = String.valueOf (mydc.evaluate (plainText));
            //将计算结果发给客户端
            out.writeUTF (answer);
            System.out.println ("
    **结果由客户端进行输出**" );
            Thread.sleep (500);
        } catch (Exception e) {
            System.out.println ("客户已断开" + e);
        }
    }
    

    }

    过程

    网络编程与安全-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. 注意责任归宿,要会通过测试证明自己没有问题

    1. 基于Java Socket实现客户端/服务器功能,传输方式用TCP
    2. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密通过网络把密文和明文的MD5値发送给服务器
    3. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换
    4. 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端
    5. 客户端显示服务器发送过来的结果
    6. 上传测试结果截图和码云链接

    代码

    (代码已折叠)

    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 ("后缀表达式明文:
    " + plainText);
                //将后缀表达式明文通过AES加密,并将后缀表达式密文发往服务器
                cipherText = AES.ecodes (plainText, AES_Key);
                System.out.println ("后缀表达式密文:
    " + cipherText + "
    ");
                //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 ("
    客户端计算的MD5值:" + valueMD5);
                //out发送信息
                out.writeUTF (valueMD5);
                //接受服务器的计算结果
                answer = in.readUTF ( );
                System.out.println ("
    **计算由服务器进行**
    
    客户收到服务器的计算结果:
    " + 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 ("服务器收到客户的后缀表达式密文:
    " + cipherText + "
    ");
                //由客户端的公钥产生的密钥对
                // 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 ("
    服务器解密后的AES_Key数据:" + AES_Key);
                //使用解密后的AES_Key对后缀表达式密文解密,并算出结果
                plainText = AES.dcodes (cipherText, AES_Key);
                System.out.println ("
    AES_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 ("
    服务器MD5值:"+valueMD5 +"
    **与客户端相同**");
                //将计算结果发给客户端
                out.writeUTF (answer);
                System.out.println ("
    **结果由客户端进行输出**" );
                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.jarUS_export_policy.jar 也放到 %JDK_HOME%jrelibsecurity 下,进行替换。
    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%
  • 相关阅读:
    搭建GitLab+Jenkins持续集成环境图文教程
    Linux学习教程,Linux入门教程(超详细)
    Python基础教程,Python入门教程(非常详细)
    我的Dojo中有一个Mojo(如何编写Maven插件)
    Nginx与安全有关的几个配置
    Tomcat安全设置
    MFC 重绘CButton 支持透明背景的png
    Qt中 QTreeView、QTableView单元项进行重命名
    C++将int与size_t进行比较的陷阱
    C++ using的用法
  • 原文地址:https://www.cnblogs.com/Yogile/p/10938944.html
Copyright © 2020-2023  润新知