实验五 Java网络编程及安全
一、实验内容
1.掌握Socket程序的编写;
2.掌握密码技术的使用;
3.设计安全传输系统。
二、实验步骤
1. 基于Java Socket实现安全传输
2. 基于TCP实现客户端和服务器,结对编程一人负责客户端,一人负责服务器
3. 使用Git进行版本控制
4. 选择对称算法进行数据加解密.
5. 选择非对称算法对对称加密密钥进行密钥分发.
6. 选择合适的Hash算法进行完整性验证.
7. 选择合适的算法对Hash值进行签名/验证.
三、设计思路
起初,我与队友看到这个实验题目是一筹莫展的,一是不太了解IO流的文件读取与文件存储位置,二是即使在课上听懂了客户端与服务器之间的加解密消息与传送的流程也不知道如何应用java代码将其实现。
在实验课上,我们咨询了老师的大致操作,并且仔细学习书上有关IO流的知识以及应用哈希函数验证加解密过程的正确性的实现,开始动手操作了。
我的结队伙伴是宋宸宁:http://home.cnblogs.com/blog/
1.首先是运行服务器与客户端的代码。
客户端
public static void main(String[] args) throws Exception{ InetAddress addr=InetAddress.getByName("192.168.252.1"); System.out.println("addr="+addr); Socket socket=new Socket(addr,8080); try{ System.out.println("socket="+socket); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true); for(int i=0;i<10;i++){ out.println("howdy"+i); String str=in.readLine(); System.out.println(str); }
2.在客户端Client程序中输入明文“Hello World!”然后利用随机秘钥发生器产生DES秘钥,并将秘钥应用IO流存入文件keykb1.dat中。
String s="Hello World!"; KeyGenerator kg=KeyGenerator.getInstance("DESede"); kg.init(168); SecretKey k=kg.generateKey( ); byte[ ] kb=k.getEncoded( ); FileOutputStream fk=new FileOutputStream("keykb1.dat"); fk.write(kb); for(int i=0;i<kb.length;i++){ System.out.print(kb[i]+",");//打印扩展秘钥 }
3.然后打印明文,并转换为UTF8格式,并将明文用秘钥加密。
Cipher cp=Cipher.getInstance("DESede"); cp.init(Cipher.ENCRYPT_MODE, k); byte ptext[]=s.getBytes("UTF8"); for(int i=0;i<ptext.length;i++){ System.out.print(ptext[i]+","); } System.out.println(""); byte ctext[]=cp.doFinal(ptext); for(int i=0;i<ctext.length;i++){ System.out.print(ctext[i] +","); }
4.传递密文给服务器
FileOutputStream f2=new FileOutputStream("SEnc.dat"); f2.write(ctext);
5.服务器应用随机秘钥发生器产生服务器的公钥和私钥
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(1024); KeyPair kp = kpg.genKeyPair(); PublicKey pbkey = kp.getPublic(); PrivateKey prkey = kp.getPrivate(); FileOutputStream f1 = new FileOutputStream( "Skey_RSA_pub.dat"); ObjectOutputStream b1 = new ObjectOutputStream(f1); b1.writeObject(pbkey); FileOutputStream f2 = new FileOutputStream( "Skey_RSA_priv.dat"); ObjectOutputStream b2 = new ObjectOutputStream(f2); b2.writeObject(prkey);
6.客户端创建服务器的公开密钥,将密钥加密传递给服务器
FileInputStream f=new FileInputStream("Skey_RSA_pub.dat"); ObjectInputStream b=new ObjectInputStream(f); RSAPublicKey pbk=(RSAPublicKey)b.readObject( ); BigInteger e=pbk.getPublicExponent(); BigInteger n=pbk.getModulus(); System.out.println("e= "+e); System.out.println("n= "+n); byte ptext1[]=s.getBytes("UTF8"); BigInteger m=new BigInteger(ptext1); BigInteger c=m.modPow(e,n); System.out.println("c= "+c); String cs=c.toString( ); BufferedWriter out1= new BufferedWriter(new OutputStreamWriter( new FileOutputStream("Enc_RSA.dat"))); out1.write(cs,0,cs.length( )); out1.close( );
7.服务器用服务器的私钥将客户端传输的DES的秘钥解密
BufferedReader in1= new BufferedReader(new InputStreamReader(new FileInputStream("Enc_RSA.dat"))); String ctext=in1.readLine(); BigInteger c=new BigInteger(ctext); FileInputStream f=new FileInputStream("Skey_RSA_priv.dat"); ObjectInputStream b=new ObjectInputStream(f); RSAPrivateKey prk=(RSAPrivateKey)b.readObject( ); BigInteger d=prk.getPrivateExponent(); BigInteger n=prk.getModulus(); System.out.println("d= "+d); System.out.println("n= "+n); BigInteger m=c.modPow(d,n); System.out.println("m= "+m); byte[] mt=m.toByteArray(); System.out.println("PlainKey is "); for(int i=0;i<mt.length;i++) { System.out.print((char) mt[i]); }
8.服务器用上一步产生的秘钥解密DES产生的密文
FileInputStream fsd=new FileInputStream("SEnc.dat"); int num=fsd.available(); byte[ ] ctextd=new byte[num]; fsd.read(ctextd); FileInputStream fsd2=new FileInputStream("keykb1.dat"); int num2=fsd2.available(); byte[ ] keykb=new byte[num2]; fsd2.read(keykb); SecretKeySpec k=new SecretKeySpec(keykb,"DESede"); Cipher cp=Cipher.getInstance("DESede"); cp.init(Cipher.DECRYPT_MODE, k); byte []ptext=cp.doFinal(ctextd); String p=new String(ptext,"UTF8"); System.out.println("服务器收到的信息为"+p);
9.服务器、客户端应用Hash函数验证加解密的正确性
System.out.println(result);*/ String ha = in.readLine(); String sa = hash(p); if(ha.equals(sa)) System.out.println("正确"); } finally { System.out.println("closing.."); socket.close(); } } finally { s.close(); }
String x=s; MessageDigest md5=MessageDigest.getInstance("MD5"); md5.update(x.getBytes( )); byte smd5[ ]=md5.digest( ); String result=""; for (int i=0; i<smd5.length; i++){ result+=Integer.toHexString((0x000000ff & smd5[i]) | 0xffffff00).substring(6); } System.out.println(result);
四、实验结果
客户端
服务器
两张图片验证的哈希值相同,说明加解密成功。
五、遇到的问题
两个人进行测试时,服务器与客户端已成功连接,但是服务器不显示结果,当自己相连时,会出现服务器结果,但是会被客户端结果覆盖,但可以快速截图截下结果,且结果正确。
六、解决办法
至今为止,调试多次并没有找到可行的解决办法,明天检查代码时咨询老师。
七、实验分析
这次实验从全班同学都不会做,到一点点探究,一点点学习,最终做出不甚完美,但结果正确的代码还是很有收获的。我从中学会了IO流传输以及文件的存储与读取。而且在这次试验中,我并不太了解老师打包发给我们的密码算法的每一条语句,但是把他们筛选整合,拼凑到一个代码中的技能却掌握了,学习java语言最重要的是掌握方法与架构,那些具体的函数与语句不是完全明白也可以应用它们。
八、统计时间
步骤 |
耗时 |
百分比 |
需求分析 |
3h |
27.3% |
设计 |
1h | 9.1% |
代码实现 |
2h | 18.2% |
测试 |
4h | 36.4% |
分析总结 |
1h | 9.1% |