网络安全课程设计

大连理工大学

网络安全课程设计实验报告

课程名称: 网络安全课程设计

学院(系): 软件学院

专 业: 网络工程

学生信息:***

20xx年7 月1 日

一. 协议功能简介

Bellovin-Merritt主要用来验证身份并且抵抗字典攻击。

网络安全课程设计

Bellovin-Merritt协议是一个简单的两方会话的密钥交换协议,更准确的说是带身份认证的基于口令的密钥交换协议。基于口令的带身份认证的密钥交换协议必须具备两个安全性质:其一是口令安全,其二是密钥保密。即除协议的双方之外,任何第三方(包括被动和主动攻击者)都不可能有效推出协议所协商的会话密钥。

Bellovin-Merritt协议用到两个对称加密方案和一个公钥加密方案。加密过程如下:

(1)A、B共享秘密口令pw, 开始会话时A随机生成一对新的,用于公钥加密方案的公钥私钥对(pkA,skA),将自己的身份标识和用pw加密的pkA发送给B;

(2)B接收到该消息后,用pw解密得到pkA, 随机生成会话密钥Ks,把Ks公钥加密后的密文再用pw对称加密后发送给A;

(3)A接收到第二条消息后先用pw解密再用skA解密得到Ks, 并随机生成NA,用Ks作为密钥加密NA发送给NB;

(4)B接收到消息后解密得到NA,再随机生成NB,连接NA和NB用Ks加密后发送给A;

(5)A用Ks解密后验证第一个分量是否为NA,如果不是,则验证不成功。如果是则将得到的NB用Ks加密后发给B

(6)B验证收到的消息解密后是否为NB,如果是则验证成功,,确认对方是A。如果不是,则验证不成功。会话结束。

Bellovin-Merritt协议之所以能抵抗字典攻击在于用到多重加密和不同的加密算法以为不同的密钥,并且每次加密都是随机生成(pkA,skA),Ks,NA,NB等参数,攻击者不存在有效的算法根据公开的参数和消息中直接观测的数据推测或者计算出密钥,并且没有有效的办法逐一测试字典中的口令以发现真实的口令。

二. 详细设计

1. Java安全库函数

Java安全机制非常严密庞大,封装了很多加密解密的算法以及密钥生成和保

存的方法。在Bellovin-Merritt协议的实现过程中,所有的加密算法都是通过调用Java的库函数方法实现。

主要用到包有java.security,java.crypto。主要用到的类和接口举例如下:

import java.security.InvalidKeyException;

import java.security.Key;

import java.security.NoSuchAlgorithmException;

import java.security.SecureRandom;

import java.security.KeyFactory;

import java.security.PublicKey;

import java.security.spec.EncodedKeySpec;

import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;

import javax.crypto.Cipher;

import javax.crypto.IllegalBlockSizeException;

import javax.crypto.KeyGenerator;

import javax.crypto.NoSuchPaddingException;

import javax.crypto.SecretKey;

import javax.crypto.spec.SecretKeySpec;

1) 密钥的产生:

A. 对称加密的密钥:只有一个(加密算法通常有:BlowFish, DESede)

javax.crypto.KeyGenerator keygen=KeyGenerator.getInstance("DESede");

keygen.init(1024);

java.security.Key mykey=keygen.generateKey();

B. 非对称加密的密钥:一对

javax.crypto.KeyPairGenerator keypairgen=KeyPairGenerator.getInstance("RSA");

keypairgen.initialize(1024);

java.security.KeyPair mykeypair=keypairgen.generateKeyPair();

java.security.PrivateKey pvk=mykeypair.getPrivate();

2) 密钥的保存与恢复:

保存:非对称加密的公钥:直接可以写入字节数组byte[]

java.security.PublicKey pbk=mykeypair.getPublic();

byte [] encodedPbk= pbk.getEncoded();

保存:非对称加密的密钥,单钥密钥: 需要加密后保存。

使用Cipher类的wrap方法。

恢复公钥:

PublicKey pbk=mykeypair.getPublic();

byte [] pbkbytes=pbk.getEncoded();

java.security.spec.X509EncodedKeySpec x509keyspec=new X509EncodedKeySpec(pbkbytes);

java.security.KeyFactory keyfac=KeyFactory.getInstance("RSA");

PublicKey pbkrecover=keyfac.generatePublic(x509keyspec);

恢复密钥:

PrivateKey pvk=mykeypair.getPrivate();

byte [] pvkbytes=pvk.getEncoded();

java.security.spec.PKCS8EncodedKeySpec pkcs8keyspec=new PKCS8EncodedKeySpec(pvkbytes);

java.security.KeyFactory keyfacpri=KeyFactory.getInstance("RSA");

PrivateKey pvkrecover=keyfacpri.generatePrivate(pkcs8keyspec);

3) 非对称加密:用公钥加密,用私钥解密, 对称加密用相同的key

Cipher cipher =Cipher.getInstance("RSA");

cipher.init(Cipher.ENCRYPT_MODE, pbk);

String toencrypt="I want to be encrypted";

byte [] cipherbytes=cipher.doFinal(toencrypt.getBytes());

cipher=Cipher.getInstance("RSA");

cipher.init(Cipher.DECRYPT_MODE, pvk);

String todecrypte="I've been encrypted,now I want to be decrypted";

byte [] cipherbytess=cipher.doFinal(todecrypte.getBytes());

2. 函数接口及实现

在对称加密DESede、Blowfish和公钥加密RSA实现后,我们将其封装成一个加密算法类SecureAlgorithm,在该类中根据加密算法的不同对明文进行不同的加密过程。类的主要方法实现如下:

public void setAlgorithm(String algorithm,String pwd);

//此方法根据参数确定选择哪一种加密算法

public byte[] encryptMsg(byte[] msg)

//此方法根据根据选定的加密算法进行加密,返回值为字节数组

public byte[] decryptMsg(byte[] msg)

//此方法根据根据选定的加密算法进行解密,返回值同样为字节数组

其他成员变量及其函数方法实现截图如下:

https://upload.fanwen118.com/wk-img/img100/4028003_2.jpg

3. 多线程设计

服务器运行后处于等待状态,每有客户端访问,便为该客户端创建一个

线程,在该线程内执行消息传递加密解密等操作。当另外一个客户端访问的时候就另外创建一个线程。线程内部读写操作接受消息发送消息加密解密都是按协议结构顺序执行。

ThreadedServer类方法实现(服务器端)

public void startServer();//启动服务器,接收客户端消息

public void processRequest() {

while (true){

int id = 1;

connection = server.accept();

id ++;

System.out.println("Accept client "+id+"'s connection");

System.out.println("client address :"+ connection.getRemoteSocketAddress());

new AuthServiceThread(connection).start();}

AuthServiceThread类方法实现(服务器端创建的线程)

public class AuthServiceThread extends Thread {

private Socket connection = null;

public AuthServiceThread(Socket connection){

this.connection = connection; }

public void run(){

//……}//线程内处理消息的接受发送加密解密

Client类方法实现

public class Client extends Thread{

private String clientID = null;

private String pwd = null;

private boolean ok = false;

public Client(){}

public Client(String id,String pwd){

this.clientID = id;

this.pwd = pwd;

}

public void run(){

//……}//线程内实现消息的接收发送加密解密

4. 线程同步的实现

由于线程执行加密解密时都需要调用SecureAlgorithm类的方法,而

每次加密解密所用的密钥各不相同,如果调用时出现混乱将会导致加密的失败,解决方案是将SecureAlgorithm类的方法加上synchronized关键字。

synchronized public void setAlgorithm(String algorithm,String pwd);

synchronized public byte[] encryptMsg(byte[] msg)

synchronized public byte[] decryptMsg(byte[] msg)

synchronized public void setPublicKey(PublicKey pk);

synchronized public void setPrivateKey(PrivateKey sk);

synchronized public void setKey(Key key);

synchronized关键字代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B,有的话要等正在使用这个方法的线程B运行完这个方法后再运行此线程A,没有的话,直接运行

三. 数据结构和算法

1. 消息结构

所有协议消息都采用类Message进行封装,属性和方法截图如下:

https://upload.fanwen118.com/wk-img/img100/4028003_3.jpg

其中clientID代表客户端的用户名,msgID代表消息的序号,text代表消息密文,类型为byte[]。

2. 随机数生成算法

调用Java中的Random类,用StringBuffer收集Random类生成的随机字母,函数体如下:

public static final String allChar = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

public static String generateString(int length) //参数为返回随机数的长度

{

StringBuffer sb = new StringBuffer();

Random random = new Random();

for (int i = 0; i < length; i++

{

sb.append(allChar.charAt(random.nextInt(allChar.length())));

}

return sb.toString();}

3. 公钥加密RSA算法

先调用KeyPairGenerator生成公钥和密钥对

final String NAME = "RSA";

private int keySize = 1024;

public PublicKey pk = null;

private PrivateKey sk = null;

private KeyPair kp = null;

KeyPairGenerator kpg = KeyPairGenerator.getInstance(NAME);

kpg.initialize(keySize);

kp = kpg.generateKeyPair();

pk = kp.getPublic();

sk = kp.getPrivate();

再调用Cipher类进行加密解密运算

cp = Cipher.getInstance("RSA/ECB/NoPadding");

cp.init(Cipher.ENCRYPT_MODE, pk);

ctext = cp.doFinal(msg);

cp.init(Cipher.DECRYPT_MODE, sk);

ptext = cp.doFinal(msg);

加密解密运算完成后可以返回公钥,私钥,明文,密文等等。

4. 对称加密算法DESede和Blowfish

调用密钥工厂生成双方持有的密钥

KeyGenerator kg = KeyGenerator.getInstance(NAME);

SecureRandom sr = new SecureRandom(pwd.getBytes());

kg.init(sr);

sk = kg.generateKey();

再同样调用Cipher类进行加密解密运算

cp = Cipher.getInstance("DESede/ECB/PKCS5Padding"); //填充机制

cp.init(Cipher.DECRYPT_MODE, sk);

ptext = cp.doFinal(msg);

cp.init(Cipher.ENCRYPT_MODE, sk);

ptext = cp.doFinal(msg);

加密解密运算完成后同样可以返回公钥,私钥,明文,密文等等。

四. 程序主要源代码

1.Client类主要代码

public void run(){

Socket client = null ; // 表示客 户端

//非对称加密生成公钥私钥对

PublicKey pk = null;

//初始化加密解密类

SecureAlgorithm sa = new SecureAlgorithm();

//随机生成的Ks Ns

......

try {

client = new Socket("192.168.7.29",8886) ;

ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());

ObjectInputStream ois = new ObjectInputStream(client.getInputStream());

//发送消息1

msg = new Message();

msg.setClientID(this.clientID);

msg.setId(1);

//生成RSA公钥私钥

sa.setAlgorithm("RSA", null);

pk = sa.getPublicKey();

sk = sa.getPrivateKey();

//加密pk

sa.setAlgorithm("DESede", pwd);

msg.setText(sa.encryptMsg(pk.getEncoded()));

oos.writeObject(msg);

oos.flush();

//接收消息2,解密的ks

Message msg2 = (Message)ois.readObject();

byte[] temp = sa.decryptMsg(msg2.getText());

sa.setAlgorithm("RSA", null);

sa.setPrivateKey(sk);

ks = new String(sa.decryptMsg(temp));

System.out.println("Client Ks is :"+ks);

//发送消息3

sa.setAlgorithm("Blowfish", ks);

na = RandomGenerator.generateString(36);

System.out.println("随机生成Na :"+na);

Message msg3 = new Message();

msg3.setClientID(this.clientID);

msg3.setId(3);

msg3.setText(sa.encryptMsg(na.getBytes()));

oos.writeObject(msg3);

oos.flush();

//接受消息4,解密后验证Na,得到Nb

Message msg4 = (Message)ois.readObject();

ns = new String(sa.decryptMsg(msg4.getText()));

if(ns.startsWith(na))//验证Na

{

System.out.println("Na相等, 验证成功,"+msg.getClientID()+" 身份属实");

ok = true;

}

nb=ns.substring(na.length());//得到Nb

System.out.println("接收到Nb:"+nb);

//发送消息5验证Nb

Message msg5 = new Message();

msg5.setClientID(this.clientID);

msg5.setId(5);

msg5.setText(sa.encryptMsg(nb.getBytes()));

oos.writeObject(msg5);

oos.flush();

} catch (Exception e) {

// TODO Auto-generated catch block

System.out.println("验证失败");

}finally{

try {

client.close() ;

} catch (IOException e) {}

2. AuthServiceThread类主要代码

public void run(){

System.out.println("start verify client.....");

//共有密码对

Map<String, String> m = new HashMap<String,String>();

m.put("A", new String("helloworld"));

m.put("B",new String("dlut1234"));

m.put("C", new String("hahahha#453[]"));

System.out.println("All password:");

Collection<String> values = m.values();

Iterator<String> it = values.iterator();

while(it.hasNext()){

System.out.println(it.next());

}

//初始化加密解密算法

SecureAlgorithm sa = new SecureAlgorithm();

.......

try{

ObjectInputStream ois = new ObjectInputStream(connection.getInputStream());

ObjectOutputStream oos = new ObjectOutputStream(connection.getOutputStream());

//接收消息1解密得pk,随机生成ks

msg = (Message)ois.readObject();

pwd = m.get(msg.getClientID());

sa.setAlgorithm("DESede", pwd);

//获得非对称加密的公钥

KeyFactory keyFactory = KeyFactory.getInstance("RSA");

EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(sa.decryptMsg(msg.getText()));

PublicKey publicKey2 = keyFactory.generatePublic(publicKeySpec);

//发送消息2

//随机生成Ks,先用非对称RSA加密Ks,再用对称加密DESede加密

ks = RandomGenerator.generateString(128);

System.out.println("Random Ks is :" + ks);

sa.setAlgorithm("RSA", null);

sa.setPublicKey(publicKey2);

byte[] temp = sa.encryptMsg(ks.getBytes());

sa.setAlgorithm("DESede", pwd);

Message msg2 = new Message();

msg2.setClientID(msg.getClientID());

msg2.setId(2);

msg2.setText(sa.encryptMsg(temp));

oos.writeObject(msg2);

oos.flush();

//接收消息3,解密的Na

Message msg3 = (Message)ois.readObject();

sa.setAlgorithm("Blowfish", ks);

na = new String(sa.decryptMsg(msg3.getText()));

System.out.println("接收到Na:"+na);

//发送消息4,随机生成Nb,连接Na,Nb加密后发送

nb = RandomGenerator.generateString(36);

System.out.println("随机生成Nb :"+nb);

ns=na+nb;Message msg4 = new Message();

msg4.setClientID("A");

msg4.setId(4);

msg4.setText(sa.encryptMsg(ns.getBytes()));

oos.writeObject(msg4);

oos.flush();

//接收消息5,验证Nb

Message msg5 = (Message)ois.readObject();

String nb2 = new String(sa.decryptMsg(msg5.getText()));

if(nb2.equals(nb)){

System.out.println("接收到Nb:"+nb2);

System.out.println("Nb相等,"+msg5.getClientID()+" 验证成功,对方身份属实");

}

else{

System.out.println("接收到Nb:"+nb2);

System.out.println("Nb不相等,"+msg5.getClientID()+"身份验证失败");

}

} catch (Exception e) {

System.out.println("验证过程出现错误,"+msg.getClientID()+" 验证失败");

}

五. 测试方案和结果

1. 本地多线程测试

在Client类主函数中添加测试代码,创建三个客户端跟服务器连接,服务器

也会相应的创建线程相应。

Client c1 = new Client("A","helloworld");

Client c2 = new Client("B","dlut1234");

Client c3 = new Client("C","hahahha#453[]");

c1.start();c2.start();c3.start();

c1.join();c2.join();c3.join();

System.out.println(c1.getClientID()+" is Verification ok:"+ c1.isOk());

System.out.println(c2.getClientID()+" is Verification ok:"+ c2.isOk());

System.out.println(c3.getClientID()+" is Verification ok:"+ c3.isOk());

客户端运行截图:

https://upload.fanwen118.com/wk-img/img100/4028003_4.jpg

服务器运行截图

https://upload.fanwen118.com/wk-img/img100/4028003_5.jpg

2. 网络通信多线程测试

服务器端运行时截图

https://upload.fanwen118.com/wk-img/img100/4028003_6.jpg

https://upload.fanwen118.com/wk-img/img100/4028003_7.jpg

客户端运行时截图:

https://upload.fanwen118.com/wk-img/img100/4028003_8.jpg

相关推荐