java-在非安全网络上建立可信任安全的通道(2/3)
在不安全的网络环境下进行密钥交互(1/3,前面那一节),容易遭受中间人攻击,什么是中间人攻击,请google it。
?
通信的双方必须是相互信任的,在这个基础上再进行密钥协商才是可靠的。那么,如何建立信任关系呢?
?
我以前的几篇博文介绍了用如何 用 ?Java编程方式生成CA证书 以及用CA证书签发客户证书。
?
现在假设,Alice和Bob的证书都是被同一个CA atlas签发的(见我前面的博文),那么他们之间如何安全的交互密钥呢?
?
Alice:
?
发送自己的证书Certa;发送DH 密钥对的公钥PDa;发送用自己的私钥 对DH公钥的签名Sa。Bob与Alice执行相同的步骤,下面是Bob处理Alice发送的东西的步骤:接收Alice的公钥Certa、DH公钥PDa、和签名Sa使用CA的证书验证Certa,如果通过,就证明了Alice的证书Certa是合法的。使用证书Certa验证签名Sa,如果通过,就证明了Certa确实是Alice发送过来的,因为只有Alice用自己的私钥才可以对PDa进行签名。Alice和Bob是对等的。经过上面的过程,可以看出Alice和Bob在相互信任的基础上交互了DH算法的公钥,即通过这种方式避免了中间人攻击。/** * 安全的密钥交互类。 * 这个交互工具可以校验双方的身份,并对发送的DH公钥进行签名,防止中间者攻击。 * @author atlas * @date 2012-9-6 */public class SecureKeyExchanger extends DHKeyExchanger {/** * 签名算法 */private static String signAlgorithm = "SHA1withRSA";/** * 此方的私钥 */private PrivateKey privateKey;/** * 此方的证书 */private Certificate certificate;/** * 用于校验彼方公钥的CA证书,此证书来自此方的CA或者此方可信任的CA */private Certificate caCertificate;/** * 彼方的证书,在DH公钥交换之前进行交互获取的 */private Certificate peerCert;/** * * @param out * @param in * @param privateKey 此方的私钥 * @param certificate 此方的证书 * @param caCertificate 用于校验彼方公钥的CA证书,此证书来自此方的CA或者此方可信任的CA */public SecureKeyExchanger(Pipe pipe,PrivateKey privateKey, Certificate certificate,Certificate caCertificate) {super(pipe);this.privateKey = privateKey;this.certificate = certificate;this.caCertificate = caCertificate;}// Send the public key.public void sendPublicKey() throws IOException,CertificateEncodingException {byte[] keyBytes = certificate.getEncoded();write(keyBytes);}public void receivePublicKey() throws IOException, SkipException {byte[] keyBytes =read();try {CertificateFactory cf = CertificateFactory.getInstance("X.509");peerCert = cf.generateCertificate(new ByteArrayInputStream(keyBytes));peerCert.verify(caCertificate.getPublicKey());} catch (CertificateException e) {throw new SkipException("Unsupported certificate type X.509", e);} catch (InvalidKeyException e) {throw new SkipException("Peer's certificate was not invlaid or not signed by current CA.",e);} catch (NoSuchAlgorithmException e) {throw new SkipException("Signature algorithm not supported.", e);} catch (NoSuchProviderException e) {throw new SkipException("No signature Provider.", e);} catch (SignatureException e) {throw new SkipException("Peer's certificate was not invlaid or not signed by current CA.",e);}}@Overridepublic void receiveDHPublicKey() throws IOException, SkipException {// receiver public keyreceivePublicKey();// receive dh public keybyte[] publicKeyBytes = read();// receive signature of dh public keybyte[] sign = read();KeyFactory kf;try {// verify signature using peer certificateSignature sig = Signature.getInstance(signAlgorithm);sig.initVerify(peerCert);sig.verify(sign);kf = KeyFactory.getInstance("DH");X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(publicKeyBytes);peerDHPublicKey = kf.generatePublic(x509Spec);} catch (NoSuchAlgorithmException e) {throw new SkipException("Signature algorithm " + signAlgorithm+ " not supported.", e);} catch (InvalidKeySpecException e) {throw new SkipException("Peer's public key invalid.", e);} catch (InvalidKeyException e) {throw new SkipException("Peer's public key invalid.", e);} catch (SignatureException e) {throw new SkipException("Invalid signature.", e);}}@Overridepublic void sendDHPublicKey() throws IOException, SkipException {try {// send public keysendPublicKey();// send dh public keybyte[] keyBytes = dhKeyPair.getPublic().getEncoded();write(keyBytes);// sign dh public key using my private key and send the signatureSignature sig;sig = Signature.getInstance(signAlgorithm);sig.initSign(privateKey);sig.update(keyBytes);byte[] sign = sig.sign();write(sign);} catch (NoSuchAlgorithmException e) {throw new SkipException("Signature algorithm " + signAlgorithm+ " not supported.", e);} catch (InvalidKeyException e) {throw new SkipException("My private key invalid.", e);} catch (SignatureException e) {throw new SkipException("Signature exception when sending dh public key.", e);} catch (CertificateEncodingException e) {throw new SkipException("error when sending dh public key.", e);}}}?测试代码:?
public class KeyInfo {PrivateKey privateKey;Certificate certificate;Certificate caCertificate;public KeyInfo(PrivateKey privateKey, Certificate certificate,Certificate caCertificate) {super();this.privateKey = privateKey;this.certificate = certificate;this.caCertificate = caCertificate;}public Certificate getCaCertificate() {return caCertificate;}public Certificate getCertificate() {return certificate;}public PrivateKey getPrivateKey() {return privateKey;}}public class Server4Alice {public static void main(String[] args) throws Exception {int port = Integer.parseInt("1111");System.out.println(Base64.encode(exchangeFrom(port)));}public static byte[] exchangeFrom(int port) throws SkipException,IOException {InputStream file = SkipServer4Alice.class.getResourceAsStream("atlas-alice.jks");KeyInfo key = Reader.read(file, "alice", "alice");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 SecureKeyExchanger(pipe,key.getPrivateKey(), key.getCertificate(),key.getCaCertificate());exchanger.exchange();s.close();ss.close();return exchanger.getKey();}}public class Client4Bob {public static void main(String[] args) throws Exception {String host = "localhost";int port = Integer.parseInt("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 {InputStream file = SkipServer4Alice.class.getResourceAsStream("atlas-bob.jks");KeyInfo key = Reader.read(file, "bob", "bob");Socket s = new Socket(host, port);DataOutputStream out = new DataOutputStream(s.getOutputStream());DataInputStream in = new DataInputStream(s.getInputStream());Pipe pipe = new DataPipe(in, out);KeyExchanger exchanger = new SecureKeyExchanger(pipe,key.getPrivateKey(), key.getCertificate(),key.getCaCertificate());exchanger.exchange();s.close();return exchanger.getKey();}}?几个JKS文件:
?
atlas-alice.jks:包含一个alice的私钥和证书,证书是用atlas的CA签发的
?
atlas-bob.jks:包含一个bob的私钥和证书,证书是用atlas的CA签发的
?
CA atlas的证书分别在alice和bob的信任证书列表里面有一个copy
?
Reader.read()是个工具方法,负责把jks文件里面的证书信息读取出来:
?
public class Reader {public static KeyInfo read(InputStream file, String alias, String password) {try {KeyStore store = KeyStore.getInstance("JKS");store.load(file, password.toCharArray());PrivateKeyEntry ke = (PrivateKeyEntry) store.getEntry(alias,new PasswordProtection(password.toCharArray()));KeyInfo info = new KeyInfo(ke.getPrivateKey(), ke.getCertificate(),ke.getCertificateChain()[1]);return info;} catch (Exception e) {e.printStackTrace();}return null;}}