假定A,B通信的共享密钥已经都拥有了
发送内容为从工程目录下名为"test-1.txt"的文件读入
=========================DECUtil.java,DEC工具类===================
package second;
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class DESUtil {
/**
* 生成密钥
*
* @return
* 密钥
*/
public static Key generateKey(String str){
try {
KeyGenerator kg = KeyGenerator.getInstance("DES",new org.bouncycastle.jce.provider.BouncyCastleProvider());
SecureRandom sr = new SecureRandom(str.getBytes());//随机数源
kg.init(sr);//初始化
Key key = kg.generateKey();//生成密钥
return key;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
/**
*
* @param key
* @return
*/
public static SecretKey generateSecretKey(byte[] key){
SecretKeyFactory fac = null;
try {
//创建一个密钥工厂
fac = SecretKeyFactory.getInstance("DES",new org.bouncycastle.jce.provider.BouncyCastleProvider());
DESKeySpec spec = new DESKeySpec(key);//从原始密匙数据创建一个DESKeySpec对象
return fac.generateSecret(spec);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
/**
* 加密
*
* @param key
* 密钥
* @param data
* 待加密的明文
* @return
* 加密后的密文
*/
public static byte[] encrypt(SecretKey secretKey,byte[] data){
Cipher cipher;
try {
cipher = Cipher.getInstance("DES",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);//初始化cipher,加密模式,并设置初始化向量
return cipher.doFinal(data);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
/**
* 解密
*
* @param key
* 密钥
* @param data
* 待解密的密文
* @return
* 解密后的明文
*/
public static byte[] decrypt(SecretKey secretKey,byte[] data){
Cipher cipher = null;
try {
cipher = Cipher.getInstance("DES",
new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.DECRYPT_MODE,secretKey);//初始化cipher,解密模式,并设置初始化向量
return cipher.doFinal(data);
}
catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* 转换16进制
*
* @param data
* @return
*/
public static byte[] getHexString(byte[] data){
String s = new String();
for(int i=0;i<data.length;i++){
s += Integer.toHexString(data[i]& 0xFF);
}
return s.getBytes();
}
}
===========================User.java,用户类=======================
package second;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
import java.security.Key;
import java.util.Random;
import javax.crypto.*;
import javax.swing.*;
/**
* 会话用户类
*
* @author Administrator
*
*/
public class User extends JFrame {
/**
*
*/
private static final long serialVersionUID = 7267580701580372205L;
private JButton jb; // 按钮
private JTextArea jta; // 文本显示区
private Container cta;
private String orgData; // 保存要加密发送的明文
private int id; // 用户ID
private SecretKey secretKey; // 共享会话主密钥
private boolean working = false; // 是否正在发送或接收
private ObjectInputStream sendin, receivein; // 发起会话请求的用户(A)的输入输出流
private ObjectOutputStream sendout, receiveout;// 接受会话请求的用户(B)的输入输出流
private Socket s;
Thread thread;
private int N1, N2; // 随机数
/**
* 构造函数
*
* @param id
* 用户ID
* @param secretKey
* 共享会话主密钥
*/
public User(int id, SecretKey secretKey) {
this.id = id;
this.secretKey = secretKey;
cta = getContentPane();
jta = new JTextArea();
jta.setEditable(false);
jta.setLineWrap(true);
jb = new JButton("SEND");
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!working) {
SendMsg sm = new SendMsg();
Thread threadsm = new Thread(sm);
threadsm.start(); // 启动请求会话线程
} else
jta.append("/n正在发送或接收数据,请稍后!");
}
});
cta.setLayout(new BorderLayout());
cta.add(jb, BorderLayout.SOUTH);
cta.add(new JScrollPane(jta), BorderLayout.CENTER);
setSize(300, 300);
setVisible(true);
WaitForMsg wfm = new WaitForMsg(id);
thread = new Thread(wfm);
thread.start(); // 启动等待会话线程
}
/**
* 从文件中读入要发送的明文
*
* @throws Exception
*/
private void readin() throws Exception {
File file = new File("test-1.txt");
FileInputStream fin;
fin = new FileInputStream(file);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] tmpbuf = new byte[1024];
int count = 0;
while ((count = fin.read(tmpbuf)) != -1) {
bout.write(tmpbuf, 0, count);
tmpbuf = new byte[1024];
}
fin.close();
orgData = bout.toString(); // 明文
}
/**
* 利用Socket发送数据,先发送数据长度,再发送数据
*
* @param data
* 要发送的数据
* @param out
* 输出流
* @throws Exception
*/
public void sendData(byte[] data, ObjectOutputStream out) throws Exception {
int num = data.length;
out.writeInt(num);
out.flush();
out.write(data);
out.flush();
}
/**
* 接收数据,先接收数据长度,然后接收真正的数据
*
* @param in
* 输入流
* @return 接收到的数据
* @throws Exception
*/
public byte[] recData(ObjectInputStream in) throws Exception {
int len = in.readInt();
byte[] rec1 = new byte[len];
in.read(rec1);
return rec1;
}
/**
* 产生一个随机数,在0~999之间
*
* @return 产生的随机数
*/
public int rand() {
return (new Random()).nextInt() % 1000;
}
/**
* 当请求会话时,调用该函数
*/
public void send() {
InetAddress ip;
int sendport = id == 10000 ? 20000 : 10000; // 请求连接对方的端口号
Socket connect;
try {
ip = InetAddress.getByName("localhost"); // 连接到本机
connect = new Socket(ip, sendport);
sendout = new ObjectOutputStream(connect.getOutputStream());// 获得输出流
sendin = new ObjectInputStream(connect.getInputStream()); // 获得输入流
N1 = rand(); // 产生随机数N1
String strSend1 = id + "//" + N1; // 拼接上 IDA
sendData(strSend1.getBytes(), sendout); // 发送
byte[] datatemp1 = recData(sendin); // 接收数据,EMKm[Ks/IDA/IDB/f(N1)/N2]
byte[] datatemp2 = DESUtil.decrypt(secretKey, datatemp1);// 解密数据
String[] datas = (new String(datatemp2)).split("//"); // 分割数据
if ((Integer.parseInt(datas[datas.length - 2]) - 1) == N1) // 判断是否为B用户
{
byte[] strN2 = f((datas[datas.length - 1]).getBytes())
.getBytes();// 获得N2,并进行F变换
Key key = DESUtil.generateKey(datas[0]); // 生成唯一的Key
SecretKey secretkey_Ks = DESUtil.generateSecretKey(key
.getEncoded()); // 生成本次会话密钥
byte[] strSend3 = DESUtil.encrypt(secretkey_Ks, strN2); // 加密变化后的N2
sendData(strSend3, sendout); // 发送N2
readin(); // 从文件中读入明文
byte[] strSend6 = DESUtil.encrypt(secretkey_Ks, orgData
.getBytes()); // 用会话密钥加密明文
sendData(strSend6, sendout); // 发送
sendout.close();
sendin.close();
connect.close();
} else {
JOptionPane.showMessageDialog(null, "获得N1有误,可能不是B用户或解析错误");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 当收到会话请求时,调用该函数
*
* @param data
*/
public void dealWith(String data) {
String[] datas = data.split("//"); // 分割收到的数据,IDA//N1
int keyNum = rand(); // 产生随机数,用于生成Key
Key key = DESUtil.generateKey(keyNum + ""); // 生成Key
SecretKey secretkey_Ks = DESUtil.generateSecretKey(key.getEncoded()); // 用Key生成密钥
N2 = rand(); // 生成随机数N2
String strSend1 = keyNum + "//" + (new String(datas[0])) + "//" + id
+ "//" + f(datas[1].getBytes()) + "//" + N2; // 拼接
// Ks//IDA//IDB//f(N1)//N2
byte[] strSend3 = DESUtil.encrypt(secretKey, strSend1.getBytes()); // 加密明文
try {
sendData(strSend3, receiveout); // 发送
byte[] datatemp1 = recData(receivein); // 得到F(N2)
byte[] recN2 = DESUtil.decrypt(secretkey_Ks, datatemp1); // 利用会话密钥解密
int tempN2 = Integer.parseInt(new String(recN2)) - 1; // 做F的逆变化
if (tempN2 == N2) // 判断对方是否为A
{
byte[] datatemp2 = recData(receivein); // 接收数据
byte[] result = DESUtil.decrypt(secretkey_Ks, datatemp2); // 解密
jta.append("/n收到数据:" + new String(result));
} else {
JOptionPane.showMessageDialog(null, "对方不是A或者N2解释错误");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* F函数加1变换
*
* @param n
* 要变换的数值
* @return 变换完的数值
*/
public String f(byte[] n) {
String temp = (new String(n)).trim();
int a = Integer.parseInt(temp);
a++;
return ("" + a);
}
/**
* 发起会话请求的线程
*
* @author Administrator
*
*/
private class SendMsg implements Runnable {
public void run() {
// TODO Auto-generated method stub
jta.append("/n正在发送数据。");
working = true;
send();
jta.append("/n数据发送完成。");
working = false;
}
}
/**
* 等待会话请求的线程
*
* @author Administrator
*
*/
private class WaitForMsg implements Runnable {
byte[] datatemp1 = null;
private ServerSocket receiveSocket;
private String rev;
/**
* 构造函数
*
* @param port
* 监听的端口号
*/
public WaitForMsg(int port) {
try {
receiveSocket = new ServerSocket(port, 5);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 循环监听会话请求,如果有会话,处理会话
*/
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
s = receiveSocket.accept();
receiveout = new ObjectOutputStream(s.getOutputStream());
receivein = new ObjectInputStream(s.getInputStream());
rev = new String(recData(receivein));
jta.append("/n收到会话请求,正在接收数据。");
working = true;
dealWith(rev);
working = false;
} catch (Exception e1) {
e1.printStackTrace();
JOptionPane.showMessageDialog(null, "出错");
System.exit(0);
}
}
}
}
}
============================test.java,测试类=======================
package second;
import javax.crypto.SecretKey;
import javax.swing.JFrame;
/**
* 测试类
*
* @author Administrator
*
*/
public class test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 为A,B产生共享的会话主密钥
SecretKey key = DESUtil.generateSecretKey(DESUtil.generateKey("1234")
.getEncoded());
User u1 = new User(10000, key); // 初始化用户和共享会话主密钥
User u2 = new User(20000, key); // 初始化用户和共享会话主密钥
u1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
u2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
其中,发送会话密钥时,如果直接发送key,或者secretKey,会出现数据丢失,应该是字节数组与字符串互转时丢掉的,所以就只发用于生成Key的随即数