看到标题,几乎所有人都会想到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();
}
}