package org.rzo.netty.ahessian.crypto; import java.io.ByteArrayOutputStream; import java.security.Key; import java.security.KeyFactory; import java.security.SecureRandom; import java.security.spec.KeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelEvent; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelHandler; import org.rzo.netty.ahessian.log.OutLogger; public class ClientCryptoFilter extends SimpleChannelHandler implements CryptoConstants { private StreamCipher _encodeCipher; private StreamCipher _decodeCipher; private byte[] _encodedPublicKey; private int _bytesRead; private SecureRandom _secureRandom = new SecureRandom(); private ChannelEvent _connectedEvent; public void messageReceived( ChannelHandlerContext ctx, MessageEvent e) throws Exception { // have we sent our secret key ? if (_decodeCipher != null) { // decode and send upstream MessageEvent m = Util.code(_decodeCipher, e, true); ctx.sendUpstream(m); } // we are still in the crypto protocol else { ChannelBuffer b = (ChannelBuffer) e.getMessage(); // is this our first message ? if (_encodedPublicKey == null) { int size = b.readInt(); _encodedPublicKey = new byte[size]; } // readin the server's public key // it may come in multiple chunks int available = b.readableBytes(); int toRead = Math.min(_encodedPublicKey.length - _bytesRead, available); b.readBytes(_encodedPublicKey, _bytesRead, toRead); _bytesRead += toRead; // we have completed reception of the public key ? if (_bytesRead == _encodedPublicKey.length) { // generate our secret key and send it to the server sendSecretKey(ctx); } } } private Cipher getAsymCipher() { try { // generate Cipher using the server's public key KeyFactory fact = KeyFactory.getInstance(ASYM_KEY_TYPE); KeySpec ks = new X509EncodedKeySpec(_encodedPublicKey); Key pubKey = fact.generatePublic(ks); String type = "".equals(ASYM_CIPHER_TYPE) ? ASYM_KEY_TYPE : ASYM_KEY_TYPE+"/"+ASYM_CIPHER_TYPE; Cipher result = Cipher.getInstance(type); result.init(Cipher.ENCRYPT_MODE, pubKey); return result; } catch (Exception ex) { ex.printStackTrace(); } return null; } private byte[] getSymKey() //private Key getSymKey() { // generate a random secret key try { // KeyGenerator keyGenerator = KeyGenerator.getInstance(SYM_KEY_TYPE); // keyGenerator.init(SYM_KEY_SIZE); // return keyGenerator.generateKey(); byte[] key = new byte[SYM_KEY_SIZE]; _secureRandom.nextBytes(key); return key; } catch (Exception ex) { ex.printStackTrace(); } return null; } private byte[] getIv() { byte[] iv = new byte[SYM_IV_SIZE]; _secureRandom.nextBytes(iv); return iv; } private void sendSecretKey(ChannelHandlerContext ctx) { try { // generate our secret key and iv and write it to a buffer byte[] symKeyEncoded = getSymKey(); byte[] ivEncoded = getIv(); ByteArrayOutputStream b = new ByteArrayOutputStream(); b.write(ivEncoded); b.write(symKeyEncoded); b.flush(); System.out.println("generated iv+key: "+OutLogger.asString(b.toByteArray())); // encode it using the server's public key Cipher asymCipher = getAsymCipher(); byte[] encryptedIvSymKey = asymCipher.doFinal(b.toByteArray()); ChannelBuffer cb = ChannelBuffers.dynamicBuffer(); cb.writeInt(encryptedIvSymKey.length); cb.writeBytes(encryptedIvSymKey); // send it to the server Channel channel = ctx.getChannel(); ChannelFuture future = Channels.future(ctx.getChannel()); Channels.write(ctx, future, cb); // wait for the message transmission future.await(); // we can now accept in/out messages encrypted with our key // first create symmetric ciphers _encodeCipher = StreamCipherFactory.createCipher(SYM_KEY_TYPE); _encodeCipher.engineInitEncrypt(symKeyEncoded, ivEncoded); _decodeCipher = StreamCipherFactory.createCipher(SYM_KEY_TYPE); _decodeCipher.engineInitDecrypt(symKeyEncoded, ivEncoded); // inform others in the pipeline that a secure connection has been established ctx.sendUpstream(_connectedEvent); } catch (Exception ex) { ex.printStackTrace(); } } @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { // remember this event, so that we can propagate it to the rest of the pipeline once we have // encryption and decryption ciphers in place _connectedEvent = e; } @Override public void writeRequested( ChannelHandlerContext ctx, MessageEvent e) throws Exception { // if we can encode if (_encodeCipher != null) { // encode the message and send it downstream MessageEvent m = Util.code(_encodeCipher, e, false); ctx.sendDownstream(m); } // else ignore. this should not happen, since we have not yet propagated the connected event. } public static void main(String[] args) { ServerCryptoFilter s = new ServerCryptoFilter(); ClientCryptoFilter c = new ClientCryptoFilter(); c._encodedPublicKey = s.getPublicKeyEncoded(); c.sendSecretKey(null); } }