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%
原文地址:https://www.cnblogs.com/bonsai/p/6936443.html