This has been driving me crazy for a few days now. I created a client using java nio with ssl encryption using an SSLEngine. Handshake works fine, and I write a GET request to a website and it works fine (I get the header with 200 code). The problem is that when the website sends the packets back, on the second packet I get a BadPaddingException. Here is my read method:
public void read(SelectionKey key,ByteBuffer readBuffer) throws IOException, BadPaddingException {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer clientSSLData = ByteBuffer.allocate(getPacketBufferSize());
System.out.println("Reading data. PacketBufferSize: "+(getPacketBufferSize()));
int length = socketChannel.read(clientSSLData);
System.out.println("read "+length+" bytes");
if (length == -1){
System.out.println("Length is -1 which means nothing was read from the channel ");
socketChannel.close();
return;
}
clientSSLData.flip();
readBuffer.clear();
SSLEngineResult res = sslEngine.unwrap(clientSSLData, readBuffer);
System.out.println(res.toString());
}
My get request is something simple like this: "GET \r\nHost: www.google.com\r\n\r\n"
Basically if its a small website like https://www.example.com, I have no problem receiving because it sends it in one read. But if I do something like https://www.google.com, I get the BadPaddingException. Any ideas? Thank you!
EDIT: Exception is...
javax.net.ssl.SSLException: bad record MAC at sun.security.ssl.Alerts.getSSLException(Unknown Source) at sun.security.ssl.SSLEngineImpl.fatal(Unknown Source) at sun.security.ssl.SSLEngineImpl.readRecord(Unknown Source) at sun.security.ssl.SSLEngineImpl.readNetRecord(Unknown Source) at sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source) at javax.net.ssl.SSLEngine.unwrap(Unknown Source) at ssl.engine.impl.SecureIO.read(SecureIO.java:244) at ssl.engine.impl.ChannelHandler.read(ChannelHandler.java:144) at ssl.engine.impl.ChannelHandler.run(ChannelHandler.java:69) at java.lang.Thread.run(Unknown Source) Caused by: javax.crypto.BadPaddingException: bad record MAC at sun.security.ssl.EngineInputRecord.decrypt(Unknown Source) ... 8 moreClosing down
You're doing this wrong. When you need to get data you should:
unwrap().
Similarly when you need to put data, you should:
wrap().
Or do all that when you need to flush.
Your primary interface should be with the engine, and only with the channel as a result of what the engine tells you about buffer underflows and overflows.
Similarly you must let the engine dictate the handshake (NEED_WRAP/NEED_UNWRAP) rather than try to dictate to it.
The SSLEngine
is a very difficult thing to get right. Many have tried: few have succeeded. For one working success, that is the basis for a commercial product, see the SSLEngineManager
class in the source code for my book Fundamental Networking in Java, Springer 2006, here.