Java實驗五
北京電子科技學院
實 驗 報 告
課程:移動平臺應用開發實踐 班級:201592 姓名:蔡斌思 學號:20159217
成績:___________ 指導老師:婁嘉鵬 實驗日期 :2015.10.25
實驗名稱: Java 網絡編程及安全
實驗內容: 1、掌握 Socket程序的編寫 2、掌握密碼技術的使用 3、設計安全傳輸系統
我的實驗搭檔是曾俊宏 http://www.rzrgm.cn/jinh/ ,服務器由我負責,客戶端由他負責
實驗步驟:
1、基于Java Socket實現安全傳輸
2、給予TCP實現客戶端和服務器,結對編程一人負責客戶端,一人負責服務器
3、使用Git進行版本控制
4、選擇對稱算法進行數據加密
5、選擇非對稱算法對對稱加密密鑰進行密鑰分發
6、選擇合適的Hash算法進行完整性驗證
在本次試驗中我組使用用DES算法進行明文加/解密,用RSA算法實現對DES密鑰的加/解密。
實驗開始前,將準備好的RSA公鑰和私鑰分發給客戶端和服務器。
客戶端對明文信息采用DES密鑰加密,同時使用RSA公鑰加密DES的解密密鑰,最終將密文信息和加密過的密鑰發送給服務器。同時用hash函數將明文進行用作驗證。
接收方B接收到信息后,用RSA私鑰解密DES密鑰,再用所獲得的DES解密密鑰解密密文信息,就可以得到客戶端發送過來的明文信息。用hash函數對解出的明文進行驗證,與客戶端發送過來的hash值相等,驗證通過。
/**
* 客戶端
*/
import java.net.*;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.spec.*;
import javax.crypto.interfaces.*;
import java.security.interfaces.*;
import java.math.*;
public class Client {
public static void main(String srgs[]) throws Exception {
try {
KeyGenerator kg = KeyGenerator.getInstance("DESede");
kg.init(168);
SecretKey k = kg.generateKey();
byte[] ptext2 = k.getEncoded();//產生des解密密鑰
// 創建連接特定服務器的指定端口的Socket對象
Socket socket = new Socket("222.28.132.26", 4421);
// 網絡輸入流
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
// 網絡輸出流
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
// 創建鍵盤輸入流
BufferedReader stdin = new BufferedReader(new InputStreamReader(
System.in));
FileInputStream f3 = new FileInputStream("Skey_RSA_pub.dat");//輸入服務器的公鑰
ObjectInputStream b2 = new ObjectInputStream(f3);
RSAPublicKey pbk = (RSAPublicKey) b2.readObject();
BigInteger e = pbk.getPublicExponent();
BigInteger n = pbk.getModulus();
BigInteger m = new BigInteger(ptext2);
BigInteger c = m.modPow(e, n);//用服務器公鑰對des解密密鑰進行加密
String cs = c.toString();
out.println(cs); // 通過網絡傳送到服務器
System.out.print("請輸入待發送的數據:");
String s = stdin.readLine();
Cipher cp = Cipher.getInstance("DESede");
cp.init(Cipher.ENCRYPT_MODE, k);
byte ptext[] = s.getBytes("UTF8");
byte ctext[] = cp.doFinal(ptext);//對明文進行des加密
String str = parseByte2HexStr(ctext);
out.println(str);
String x = s;
MessageDigest m2 = MessageDigest.getInstance("MD5");//對明文進行hash
m2.update(x.getBytes());
byte a[] = m2.digest();
String result = "";
for (int i = 0; i < a.length; i++) {
result += Integer.toHexString((0x000000ff & a[i]) | 0xffffff00)
.substring(6);
}
System.out.println(result);
out.println(result);
str = in.readLine();// 從網絡輸入流讀取結果
System.out.println("從服務器接收到的結果為:" + str); // 輸出服務器返回的結果
} catch (Exception e) {
System.out.println(e);
} finally {
}
}
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2),
16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
服務器端由蔡斌思負責http://www.rzrgm.cn/20159217cbs/
/**
* 服務器
*/
import java.net.*;
import java.io.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
import java.security.interfaces.*;
import java.math.*;
public class Server {
public static void main(String srgs[]) throws Exception {
ServerSocket sc = null;
Socket socket = null;
try {
sc = new ServerSocket(4421);// 創建服務器套接字
System.out.println("端口號:" + sc.getLocalPort());
System.out.println("服務器已經啟動...");
socket = sc.accept(); // 等待客戶端連接
System.out.println("已經建立連接");
// 獲得網絡輸入流對象的引用
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
// //獲得網絡輸出流對象的引用
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
String aline2 = in.readLine();
BigInteger c = new BigInteger(aline2);
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();
BigInteger m = c.modPow(d, n);
byte[] keykb = m.toByteArray();
String aline = in.readLine();// 讀取客戶端傳送來的數據
byte[] ctext = parseHexStr2Byte(aline);
Key k = new SecretKeySpec(keykb, "DESede");
Cipher cp = Cipher.getInstance("DESede");
cp.init(Cipher.DECRYPT_MODE, k);
byte[] ptext = cp.doFinal(ctext);
String p = new String(ptext, "UTF8");
System.out.println("從客戶端接收到信息為:" + p); // 通過網絡輸出流返回結果給客戶端
String aline3 = in.readLine();
String x = p;
MessageDigest m2 = MessageDigest.getInstance("MD5");
m2.update(x.getBytes());
byte a[] = m2.digest();
String result = "";
for (int i = 0; i < a.length; i++) {
result += Integer.toHexString((0x000000ff & a[i]) | 0xffffff00)
.substring(6);
}
System.out.println(result);
if (aline3.equals(result)) {
System.out.println("匹配成功");
}
out.println("匹配成功");
out.close();
in.close();
sc.close();
} catch (Exception e) {
System.out.println(e);
}
}
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2),
16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
通過查詢服務器IP地址,我們組先做了ping測試,通過之后,由蔡斌思先啟動服務器,我再啟動客戶端。
運行結果如下
1.服務器運行結果:

2.客戶端運行結果

實驗總結:
本次實驗中,最難的在于Socket通信的使用。通過本次實驗,因為程序代碼邏輯是由客戶端發起連接請求,服務器被動的響應,所以對一部分操作有限制。
按照設想,本來應該由服務器端創建RSA公私密鑰對,再通過Socket將RSA公鑰傳輸給客戶端使用,但是由于服務器向客戶端傳輸文件的過程必然導致代碼的龐雜,最后我們選擇了客戶端和服務器之間相互只傳遞數據流,而不涉及文件的傳輸操作。
本次實驗收獲有很多方面:
1.復習了Socket傳輸內容,讓我了解到數據流之間的轉化可以用各種各樣的方式。
2.進一步理解了Socket通信,了解了服務器的應答機制,但仍存在一定疑惑。
3.學習了java中對于加解密算法的操作過程,熟悉了加解密的操作流程。
4.學習了Socket通信中對文件的傳輸操作,但是由于操作上和實現邏輯上的復雜性,最后只編寫了一個傳輸Demo,并未投入使用。
5.對安全系統有了簡單的理解。
遇到的問題與解答:
在本次實驗中,我們原本采用的是傳輸文件的方式用于客戶端和服務器的數據的傳輸,代碼有些復雜。同時我們還在思考如何從服務器向客戶端傳送文件(如服務器的公鑰),最終在網上搜到了一些代碼,比較復雜。
在實際運用中,服務器的公鑰應該是可以提前得到的,所以并不用特別的從服務器向客戶端傳輸。
posted on 2015-10-27 00:22 20159217蔡斌思 閱讀(194) 評論(0) 收藏 舉報
浙公網安備 33010602011771號