zoukankan      html  css  js  c++  java
  • java第五次实验

    20155313 实验五《Java网络编程》


    实验内容

    1.掌握Socket程序的编写

    2.掌握密码技术的使用

    3.设计安全传输系统


    实验要求

    1. 结对实现中缀表达式转后缀表达式的功能 MyBC.java

    2. 结对实现从上面功能中获取的表达式中实现后缀表达式求值的功能,调用MyDC.jav

    3. 基于Java Socket实现客户端/服务器功能,传输方式用TCP

    4. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器

    5. 服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

    6. 客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器

    7. 服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

    8. 客户端和服务器用DH算法进行3DES或AES算法的密钥交换

    9. 服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

    10. 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端


    实验步骤

    1.实现中缀表达式转后缀表达式的功能 MyBC.java

    代码示例如下:

    
    package cc.openhome;
    
    import java.util.Stack;
    import java.util.StringTokenizer;
    
    public class MyBC {
        public boolean comparePrior(String operator1, String operator2) {
            if("(".equals(operator2)) {
                return true;
            }
            if ("*".equals(operator1) || "/".equals(operator1)) {
                if ("+".equals(operator2) || "-".equals(operator2)) {
                    return true;
                }
            }
            return false;
        }
    
    
        private boolean isOperator(String token) {
            return (token.equals("+") || token.equals("-") ||
                    token.equals("*") || token.equals("/"));
        }
        private boolean isNum(String token){
            if (Integer.parseInt(token)>=0)
                return true;
            return false;
        }
    
        public String exc(String expr){
            String token;
            StringBuffer exch=new StringBuffer();
            Stack<String> op=new Stack<String>();
            StringTokenizer tokenizer = new StringTokenizer(expr);
            while (tokenizer.hasMoreTokens()) {
                token = tokenizer.nextToken();
    
                //若遇到操作数,直接输出,并输出一个空格作为两个操作数的分隔符
    
                if (isOperator(token)){
                    if (op.empty() || comparePrior(token,op.peek())){
                        op.push(token);
                    }
                    else{
                        String s=op.pop();
                        exch.append(s);
                        exch.append(" ");
                    }
                }
                else if("(".equals(token) || ")".equals(token)){
                    if ("(".equals(token)){
                        op.push(token);
                    }
                    if(")".equals(token)){
                        while (!(op.peek()).equals("(")){
                            String s=op.pop();
                            exch.append(s);
                            exch.append(" ");
                        }
                    }
    
                }
                else{
                    exch.append(token);
                    exch.append(" ");
                }
    
            }
            while (!op.empty()){
                String s=op.pop();
                if (isOperator(s))
                {
                    exch.append(s);
                    exch.append(" ");}
            }
            return exch.toString();
        }
    
    
    
    }
    
    
    

    运行结果如下:

    2.客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式通过网络发送给服务器;服务器接收到后缀表达式,调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

    设置IP地址

    打开cmd,输入ipconfig指令,如图:

    获得我在当前局域网中的IP地址:10.29.43.143,并与结对伙伴确认连通;

    建立Socket对象

    Socket对象的相关作用在老师给的《Java网络编程技术》的博客中已经有所了解,在此总结获取Socket信息的方法:

    getInetAddress(): 获得远程服务器的IP 地址.
    getPort(): 获得远程服务器的端口.
    getLocalAddress(): 获得客户本地的IP 地址.
    getLocalPort(): 获得客户本地的端口.
    getInputStream(): 获得输入流. 如果Socket 还没有连接, 或者已经关闭, 或者已经通过 shutdownInput() 方法关闭输入流, 那么此方法会抛出IOException.
    getOutputStream(): 获得输出流, 如果Socket 还没有连接, 或者已经关闭, 或者已经通过 shutdownOutput() 方法关闭输出流, 那么此方法会抛出IOException.
    建立一个Socket对象,用来连接特定服务器的指定端口,输入的参数是刚刚获取的ip地址和双方默认的同一端口。

    实现连接的截图:

    3.客户端让用户输入中缀表达式,然后把中缀表达式调用MyBC.java的功能转化为后缀表达式,把后缀表达式用3DES或AES算法加密后通过网络把密文发送给服务器;服务器接收到后缀表达式表达式后,进行解密(和客户端协商密钥,可以用数组保存),然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

    密钥的加密:

    发送方A——>接收方B

    A加密时,用B的公钥

    B解密时,用B的私钥
    发送方A对信息(明文)采用DES密钥加密,使用RSA公钥加密前面的DES密钥信息,最终将混合信息进行传递。同时用hash函数将明文进行用作验证。

    接收方B接收到信息后,用RSA私钥解密DES密钥信息,再用RSA解密获取到的密钥信息解密密文信息,最终就可以得到我们要的信息(明文)。用hash函数对解出的明文进行验证,与发送过来的hash值相等,验证通过。

    具体代码如下:

    
    package cc.openhome;
    
    import java.security.Key;
    import java.security.NoSuchAlgorithmException;
    import javax.crypto.Cipher;
    import javax.crypto.KeyGenerator;
    import javax.crypto.SecretKey;
    import javax.crypto.spec.SecretKeySpec;
    
    public class AESCode {
        public AESCode(){
    
        }
        private static final String KEY_ALGORITHM = "AES";
        private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法
    
        public static byte[] initSecretKey() {
    
            //返回生成指定算法密钥生成器的 KeyGenerator 对象
            KeyGenerator kg = null;
            try {
                kg = KeyGenerator.getInstance(KEY_ALGORITHM);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
                return new byte[0];
            }
            //初始化此密钥生成器,使其具有确定的密钥大小
            //AES 要求密钥长度为 128
            kg.init(128);
            //生成一个密钥
            SecretKey  secretKey = kg.generateKey();
            return secretKey.getEncoded();
        }
    
        private static Key toKey(byte[] key){
            //生成密钥
            return new SecretKeySpec(key, KEY_ALGORITHM);
        }
    
        public  byte[] encrypt(byte[] data,Key key) throws Exception{
            return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
        }
    
        public static byte[] encrypt(byte[] data,byte[] key) throws Exception{
            return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
        }
    
        public static byte[] encrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception{
            //还原密钥
            Key k = toKey(key);
            return encrypt(data, k, cipherAlgorithm);
        }
    
        public static byte[] encrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception{
            //实例化
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            //使用密钥初始化,设置为加密模式
            cipher.init(Cipher.ENCRYPT_MODE, key);
            //执行操作
            return cipher.doFinal(data);
        }
    
        public static byte[] decrypt(byte[] data,byte[] key) throws Exception{
            return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
        }
    
        public byte[] decrypt(byte[] data,Key key) throws Exception{
            return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
        }
    
        public static byte[] decrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception{
            //还原密钥
            Key k = toKey(key);
            return decrypt(data, k, cipherAlgorithm);
        }
    
        public static byte[] decrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception{
            //实例化
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            //使用密钥初始化,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, key);
            //执行操作
            return cipher.doFinal(data);
        }
    
        public static String  showByteArray(byte[] data){
            if(null == data){
                return null;
            }
            StringBuilder sb = new StringBuilder("{");
            for(byte b:data){
                sb.append(b).append(",");
            }
            sb.deleteCharAt(sb.length()-1);
            sb.append("}");
            return sb.toString();
        }
    
        public static void main(String[] args) throws Exception {
            //byte[] key = initSecretKey();
            byte[] key={1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0};
            //System.out.println("key:"+showByteArray(key));
            //Key k = toKey(key); //生成秘钥
            String data ="1 2 +";
            System.out.println("加密前数据: string:"+data);
            System.out.println("加密前数据: byte[]:"+showByteArray(data.getBytes()));
            System.out.println();
            byte[] encryptData = encrypt(data.getBytes(), key);//数据加密
            System.out.println("加密后数据: byte[]:"+showByteArray(encryptData));
            //System.out.println("加密后数据: hexStr:"+Hex.encodeHexStr(encryptData));
            System.out.println();
            byte[] decryptData = decrypt(encryptData, key);//数据解密
            System.out.println("解密后数据: byte[]:"+showByteArray(decryptData));
            System.out.println("解密后数据: string:"+new String(decryptData));
        }
    

    实验截图:

    4.客户端和服务器用DH算法进行3DES或AES算法的密钥交换;服务器接收到后缀表达式表达式后,进行解密,然后调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

    具体代码如下:

    package cc.openhome;
    
    import java.security.Key;
    import java.security.KeyFactory;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.HashMap;
    import java.util.Map;
    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;
    
    public abstract class DHCoder {
        /**
    
         * 非对称加密密钥算法
    
         */
        private static final String KEY_ALGORITHM = "DH";
        /**
    
         * 本地密钥算法,即对称加密密钥算法
    
         * 可选DES、DESede或者AES
    
         */
        private static final String SELECT_ALGORITHM = "AES";
        /**
    
         * 密钥长度
    
         */
        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();
        }
    }
    

    5. 服务器接收到后缀表达式表达式后,进行解密,解密后计算明文的MD5值,和客户端传来的MD5进行比较,一致则调用MyDC.java的功能计算后缀表达式的值,把结果发送给客户端

    package cc.openhome;
    
    import java.security.NoSuchAlgorithmException;
    
    import java.io.UnsupportedEncodingException;
    import java.security.MessageDigest;
    import sun.misc.BASE64Encoder;
    
    
    
    public class MD5Coder {
        /**利用MD5进行加密
           * @param str  待加密的字符串
           * @return  加密后的字符串
           * @throws NoSuchAlgorithmException  没有这种产生消息摘要的算法
            * @throws UnsupportedEncodingException
           */
        public static String EncoderByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException{
    
            //确定计算方法
            MessageDigest md5=MessageDigest.getInstance("MD5");
            BASE64Encoder base64en = new BASE64Encoder();
    
            //加密后的字符串
            String newstr=base64en.encode(md5.digest(str.getBytes("utf-8")));
    
            return newstr;
        }
    
    
    }
    

    实验中遇到的问题及解决方案

    问题如下:

    解决方案:

    通过上网查找资料,得到如下解释:如果有其他的Java项目(程序)在运行中,则JVM会被占用,便会出现该错误,要解决该问题直接结束正在运行的程序即可。因此在运行你想要调试的项目前,要确保JVM是处于Stop状态的。


    步骤 耗时 百分比
    需求分析 10分钟 20%
    设计 10分钟 20%
    代码实现 20分钟 40%
    测试 5分钟 10%
    分析总结 5分钟 10%
  • 相关阅读:
    LCS 最长公共子序列
    零和数组
    Learn2Rank
    ac自动机
    208. Implement Trie (Prefix Tree)
    php截取中文字符串 GB2312 utf-8
    纵向文字滚动代码,带上下图片控制的。鼠标放到上下图片上时滚动
    js图片切换 带左右控制的
    实时显示输入的内容
    Lightbox JS v2.0图片切换效果
  • 原文地址:https://www.cnblogs.com/bonsai/p/6936443.html
Copyright © 2011-2022 走看看