`
OpenMind
  • 浏览: 177115 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

java-在非安全网络上建立可信任安全的通道(1/3)

 
阅读更多

      看到标题,几乎所有人都会想到SSL,但SSL比较重量级,我想做的是只利用java的JCE体系(不是JSSE)在非安全网络环境下建立起一个可信任的、安全的通道。

      所以这篇博文包括两个主题:可信任和安全。

这一节只考虑如何交互密钥。下一节(2/3)讨论如何建立信任关系,并在可信关系上交换密钥(防止中间人攻击)。

 

     非对称密钥不适合做通道加密,通道加密必然使用对称密钥。既然如此,通信的双方(或多方)如何获取一个共同的密钥呢?

 

      DH算法(Diffie-Hellman)是一种密钥协商算法,不理解原理的可以看这里:http://zh.wikipedia.org/wiki/Diffie-Hellman%E5%AF%86%E9%92%A5%E4%BA%A4%E6%8D%A2

 

下面的代码使用Java security api在socket通道上面演示密钥交换:

 

参考《Java security,2nd edition》

核心代码

 

public class DHKeyExchanger implements KeyExchanger {

	protected Pipe pipe;
	protected KeyPair dhKeyPair;

	protected PublicKey peerDHPublicKey;

	private byte[] key;

	/**
	 * 
	 * @param pipe 密钥交互管道
	 */
	public DHKeyExchanger(Pipe pipe) {
		this.pipe = pipe;
	}

	// 初始化DH密钥对
	protected void init() throws SkipException {
		try {
			// Create a Diffie-Hellman key pair.
			KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
			kpg.initialize(SKIP.DHParameterSpec);
			dhKeyPair = kpg.genKeyPair();
		} catch (InvalidAlgorithmParameterException e) {
			throw new SkipException("Invalid DH algorithm parameter.", e);
		} catch (NoSuchAlgorithmException e) {
			throw new SkipException("DH algorithm not supported.", e);
		}
	}

	// 发送dh公钥
	protected void sendDHPublicKey() throws IOException, SkipException {
		byte[] keyBytes = dhKeyPair.getPublic().getEncoded();
		write(keyBytes);
	}

	// 接收对方的dh公钥
	protected void receiveDHPublicKey() throws IOException, SkipException {
		byte[] publicKeyBytes = read();
		KeyFactory kf;
		try {
			kf = KeyFactory.getInstance("DH");
			X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(publicKeyBytes);
			peerDHPublicKey = kf.generatePublic(x509Spec);
		} catch (NoSuchAlgorithmException e) {
			throw new SkipException("DH algorithm not supported.", e);
		} catch (InvalidKeySpecException e) {
			throw new SkipException("Invalid public key", e);
		}
	}

	// 生成密钥
	public byte[] generateKey() throws SkipException {
		KeyAgreement ka;
		try {
			ka = KeyAgreement.getInstance("DH");
			ka.init(dhKeyPair.getPrivate());
			ka.doPhase(peerDHPublicKey, true);
			return ka.generateSecret();
		} catch (NoSuchAlgorithmException e) {
			throw new SkipException("DH algorithm not supported.", e);
		} catch (InvalidKeyException e) {
			throw new SkipException("Invalid private key.", e);
		}
	}

	// all in one
	public void exchange() throws SkipException, IOException {
		this.init();
		this.sendDHPublicKey();
		this.receiveDHPublicKey();
		this.key = generateKey();
	}

	// read a byte array
	protected byte[] read() throws IOException {
		return pipe.read();
	}

	// write a byte array
	protected void write(byte[] bytes) throws IOException {
		pipe.write(bytes);
	}

	@Override
	public byte[] getKey() {
		return key;
	}
}
public interface KeyExchanger {

	public void exchange() throws SkipException, IOException;
	/**
	 * @return 协商好的密钥
	 */
	byte[] getKey();
}
public class SKIP {
	// SKIP's 1024 DH parameters
	private static final String SKIP1024String = "F488FD584E49DBCD20B49DE49107366B336C380D451D0F7C88B31C7C5B2D8EF6"
			+ "F3C923C043F0A55B188D8EBB558CB85D38D334FD7C175743A31D186CDE33212C"
			+ "B52AFF3CE1B1294018118D7C84A70A72D686C40319C807297ACA950CD9969FAB"
			+ "D00A509B0246D3083D66A45D419F9C7CBD894B221926BAABA25EC355E92F78C7";
	// Modulus
	private static final BigInteger SKIP1024Modulus = new BigInteger(
			SKIP1024String, 16);
	// Base
	private static final BigInteger SKIP1024Base = BigInteger.valueOf(2);
	public static final DHParameterSpec DHParameterSpec = new DHParameterSpec(
			SKIP1024Modulus, SKIP1024Base);

}

 

数据交互通道: 

 

public interface Pipe {
	byte[] read() throws IOException;

	void write(byte[] data) throws IOException;
}

public class DataPipe implements Pipe {
	DataInput in;
	DataOutput out;

	public DataPipe(InputStream in, OutputStream out) {
		super();
		if (in instanceof DataInputStream) {
			this.in = (DataInputStream) in;
		} else {
			this.in = new DataInputStream(in);
		}
		if (out instanceof DataOutputStream) {
			this.out = (DataOutputStream) out;
		} else {
			this.out = new DataOutputStream(out);
		}
	}

	@Override
	public byte[] read() throws IOException {
		byte[] bytes = new byte[in.readInt()];
		in.readFully(bytes);
		return bytes;
	}

	@Override
	public void write(byte[] data) throws IOException {
		out.writeInt(data.length);
		out.write(data);
	}

}

测试代码:

public class Client {
	public static void main(String[] args) throws Exception {
		String host = "localhost";
		int port =1111;
		// Open the network connection.
		byte[] key = exchangeFrom(host, port);
		System.out.println(Base64.encode(key));
	}

	public static byte[] exchangeFrom(String host, int port)
			throws SkipException, IOException {
		Socket s = new Socket(host, port);
		Pipe pipe = new DataPipe(s.getInputStream(), s.getOutputStream());
		KeyExchanger exchanger = new DHKeyExchanger(pipe);
		exchanger.exchange();
		s.close();
		return exchanger.getKey();
	}
}
//
public class Server {
	public static void main(String[] args) throws Exception {
		System.out.println(Base64.encode(exchangeFrom(1111)));
	}
	

	public static byte[] exchangeFrom(int port)
			throws SkipException, IOException {
		ServerSocket ss = new ServerSocket(port);
		// Wait for a connection.
		Socket s = ss.accept();
		DataOutputStream out = new DataOutputStream(s.getOutputStream());
		DataInputStream in = new DataInputStream(s.getInputStream());
		Pipe pipe = new DataPipe(in, out);
		KeyExchanger exchanger = new DHKeyExchanger(pipe);
		exchanger.exchange();
		s.close();
		ss.close();
		return exchanger.getKey();
	}
}

 

 

分享到:
评论
1 楼 OpenMind 2012-10-05  
数据交互通道采用接口方式设计,是与通信协议无关的,实例使用的是socket,其实http连接的双方也可以通过不同的Pipe的实现做到交互密钥的。

相关推荐

Global site tag (gtag.js) - Google Analytics