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

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

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

 

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

 

      DH算法(Diffie-Hellman)是一种密钥协商算法,不理解原理的可以看这里:

 

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

 

参考《Java security,2nd edition》

核心代码

  1. public class DHKeyExchanger implements KeyExchanger {  

  2.   

  3.     protected Pipe pipe;  

  4.     protected KeyPair dhKeyPair;  

  5.   

  6.     protected PublicKey peerDHPublicKey;  

  7.   

  8.     private byte[] key;  

  9.   

  10.     /** 

  11.      *  

  12.      * @param pipe 密钥交互管道 

  13.      */  

  14.     public DHKeyExchanger(Pipe pipe) {  

  15.         this.pipe = pipe;  

  16.     }  

  17.   

  18.     // 初始化DH密钥对  

  19.     protected void init() throws SkipException {  

  20.         try {  

  21.             // Create a Diffie-Hellman key pair.  

  22.             KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");  

  23.             kpg.initialize(SKIP.DHParameterSpec);  

  24.             dhKeyPair = kpg.genKeyPair();  

  25.         } catch (InvalidAlgorithmParameterException e) {  

  26.             throw new SkipException("Invalid DH algorithm parameter.", e);  

  27.         } catch (NoSuchAlgorithmException e) {  

  28.             throw new SkipException("DH algorithm not supported.", e);  

  29.         }  

  30.     }  

  31.   

  32.     // 发送dh公钥  

  33.     protected void sendDHPublicKey() throws IOException, SkipException {  

  34.         byte[] keyBytes = dhKeyPair.getPublic().getEncoded();  

  35.         write(keyBytes);  

  36.     }  

  37.   

  38.     // 接收对方的dh公钥  

  39.     protected void receiveDHPublicKey() throws IOException, SkipException {  

  40.         byte[] publicKeyBytes = read();  

  41.         KeyFactory kf;  

  42.         try {  

  43.             kf = KeyFactory.getInstance("DH");  

  44.             X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(publicKeyBytes);  

  45.             peerDHPublicKey = kf.generatePublic(x509Spec);  

  46.         } catch (NoSuchAlgorithmException e) {  

  47.             throw new SkipException("DH algorithm not supported.", e);  

  48.         } catch (InvalidKeySpecException e) {  

  49.             throw new SkipException("Invalid public key", e);  

  50.         }  

  51.     }  

  52.   

  53.     // 生成密钥  

  54.     public byte[] generateKey() throws SkipException {  

  55.         KeyAgreement ka;  

  56.         try {  

  57.             ka = KeyAgreement.getInstance("DH");  

  58.             ka.init(dhKeyPair.getPrivate());  

  59.             ka.doPhase(peerDHPublicKey, true);  

  60.             return ka.generateSecret();  

  61.         } catch (NoSuchAlgorithmException e) {  

  62.             throw new SkipException("DH algorithm not supported.", e);  

  63.         } catch (InvalidKeyException e) {  

  64.             throw new SkipException("Invalid private key.", e);  

  65.         }  

  66.     }  

  67.   

  68.     // all in one  

  69.     public void exchange() throws SkipException, IOException {  

  70.         this.init();  

  71.         this.sendDHPublicKey();  

  72.         this.receiveDHPublicKey();  

  73.         this.key = generateKey();  

  74.     }  

  75.   

  76.     // read a byte array  

  77.     protected byte[] read() throws IOException {  

  78.         return pipe.read();  

  79.     }  

  80.   

  81.     // write a byte array  

  82.     protected void write(byte[] bytes) throws IOException {  

  83.         pipe.write(bytes);  

  84.     }  

  85.   

  86.     @Override  

  87.     public byte[] getKey() {  

  88.         return key;  

  89.     }  

  90. }  

Java代码  

  1. public interface KeyExchanger {  

  2.   

  3.     public void exchange() throws SkipException, IOException;  

  4.     /** 

  5.      * @return 协商好的密钥 

  6.      */  

  7.     byte[] getKey();  

  8. }  

Java代码  

  1. public class SKIP {  

  2.     // SKIP's 1024 DH parameters  

  3.     private static final String SKIP1024String = "F488FD584E49DBCD20B49DE49107366B336C380D451D0F7C88B31C7C5B2D8EF6"  

  4.             + "F3C923C043F0A55B188D8EBB558CB85D38D334FD7C175743A31D186CDE33212C"  

  5.             + "B52AFF3CE1B1294018118D7C84A70A72D686C40319C807297ACA950CD9969FAB"  

  6.             + "D00A509B0246D3083D66A45D419F9C7CBD894B221926BAABA25EC355E92F78C7";  

  7.     // Modulus  

  8.     private static final BigInteger SKIP1024Modulus = new BigInteger(  

  9.             SKIP1024String, 16);  

  10.     // Base  

  11.     private static final BigInteger SKIP1024Base = BigInteger.valueOf(2);  

  12.     public static final DHParameterSpec DHParameterSpec = new DHParameterSpec(  

  13.             SKIP1024Modulus, SKIP1024Base);  

  14.   

  15. }  

 

数据交互通道: 

 

Java代码  

  1. public interface Pipe {  

  2.     byte[] read() throws IOException;  

  3.   

  4.     void write(byte[] data) throws IOException;  

  5. }  

  6.   

  7. public class DataPipe implements Pipe {  

  8.     DataInput in;  

  9.     DataOutput out;  

  10.   

  11.     public DataPipe(InputStream in, OutputStream out) {  

  12.         super();  

  13.         if (in instanceof DataInputStream) {  

  14.             this.in = (DataInputStream) in;  

  15.         } else {  

  16.             this.in = new DataInputStream(in);  

  17.         }  

  18.         if (out instanceof DataOutputStream) {  

  19.             this.out = (DataOutputStream) out;  

  20.         } else {  

  21.             this.out = new DataOutputStream(out);  

  22.         }  

  23.     }  

  24.   

  25.     @Override  

  26.     public byte[] read() throws IOException {  

  27.         byte[] bytes = new byte[in.readInt()];  

  28.         in.readFully(bytes);  

  29.         return bytes;  

  30.     }  

  31.   

  32.     @Override  

  33.     public void write(byte[] data) throws IOException {  

  34.         out.writeInt(data.length);  

  35.         out.write(data);  

  36.     }  

  37.   

  38. }  

测试代码:

Java代码  

  1. public class Client {  

  2.     public static void main(String[] args) throws Exception {  

  3.         String host = "localhost";  

  4.         int port =1111;  

  5.         // Open the network connection.  

  6.         byte[] key = exchangeFrom(host, port);  

  7.         System.out.println(Base64.encode(key));  

  8.     }  

  9.   

  10.     public static byte[] exchangeFrom(String host, int port)  

  11.             throws SkipException, IOException {  

  12.         Socket s = new Socket(host, port);  

  13.         Pipe pipe = new DataPipe(s.getInputStream(), s.getOutputStream());  

  14.         KeyExchanger exchanger = new DHKeyExchanger(pipe);  

  15.         exchanger.exchange();  

  16.         s.close();  

  17.         return exchanger.getKey();  

  18.     }  

  19. }  

  20. //  

  21. public class Server {  

  22.     public static void main(String[] args) throws Exception {  

  23.         System.out.println(Base64.encode(exchangeFrom(1111)));  

  24.     }  

  25.       

  26.   

  27.     public static byte[] exchangeFrom(int port)  

  28.             throws SkipException, IOException {  

  29.         ServerSocket ss = new ServerSocket(port);  

  30.         // Wait for a connection.  

  31.         Socket s = ss.accept();  

  32.         DataOutputStream out = new DataOutputStream(s.getOutputStream());  

  33.         DataInputStream in = new DataInputStream(s.getInputStream());  

  34.         Pipe pipe = new DataPipe(in, out);  

  35.         KeyExchanger exchanger = new DHKeyExchanger(pipe);  

  36.         exchanger.exchange();  

  37.         s.close();  

  38.         ss.close();  

  39.         return exchanger.getKey();  

  40.     }  

  41. }