JAva-信頼性の高い安全なチャネルを非安全なネットワーク上に確立(1/3)

7123 ワード

タイトルを見ると、ほとんどの人がSSLを思い浮かべますが、SSLは比較的重量級で、javaのJCEシステム(JSSEではありません)だけを利用して非安全なネットワーク環境の下で信頼できる、安全な通路を構築したいと思っています.
だから、このブログには2つのテーマが含まれています.信頼性と安全です.
このセクションでは、キーをインタラクティブにする方法だけを考慮します.次のセクション(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,2 nd 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();
	}
}