Reputation: 576
I got a packet of bytes which are the ClientHello
of SSL protocol packet.
Before I'm starting to code by myself, a code which is going through all over the bytes to get each field value, I wonder if there is any Java Object (from java.security) which is used to get these bytes and parse the data so I would be able to take the SSL protocol fields and use them?
Upvotes: 2
Views: 1103
Reputation: 3089
As far as I know, the java.security
package in Java does not provide any functionality along the lines of what you're looking for. There may be other examples/libraries on GitHub.
If you end up doing this yourself, key things to be aware of are:
ClientHello
formatsClientHello
messages; they are only sent later in the handshake process, in response to a CertificateRequest
message from the server.Decoding SSL/TLS messages usually involves reading (and re-reading) the relevant RFCs, and figuring out what each byte value signifies, by its position within the packet. Being designed as a binary protocol, for efficiency, it can be more complicated to tease out the fields you want than you might think necessary. But it's well-worth gaining an understanding of these fields.
Here's some Java code which might help to get you started. Note that it uses byte buffers in Java, as it is necessary to deal with unsigned data types when decoding binary protocols like this.
// These values are defined in the IETF TLS cipher suite registry; see:
//
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-3
private static final int RC4_MD5_HEX = 0x0004;
private static final int RC4_SHA_HEX = 0x0005;
private static final short HANDSHAKE_CONTENT_TYPE = 22;
private static final short CLIENTHELLO_MESSAGE_TYPE = 1;
private static final short SSLV2_CLIENTHELLO = 128;
protected boolean doDecode(IoSession session,
IoBufferEx original,
ProtocolDecoderOutput out)
throws Exception {
// Need at least 2 bytes to differentiate between SSLv2 ClientHello
// messages and SSLv3/TLSv1/+ messages.
//
// For SSLv2 ClientHello, we need:
//
// length header (short)
// content type (byte)
//
// For more details, see:
//
// https://datatracker.ietf.org/doc/html/draft-hickman-netscape-ssl-00
//
// Otherwise, we need:
//
// content type (byte)
// version (short)
// length (short)
//
// So wait for at least 5 bytes, to cover either case.
if (original.remaining() < 5) {
return false;
}
// Make a copy, so that we can read things non-destructively
IoBufferEx dup = original.duplicate();
// If not a Handshake record, be done. Note that we have to
// successfully handle SSLv2 ClientHello formats as well.
short contentType = dup.getUnsigned();
if (contentType == HANDSHAKE_CONTENT_TYPE) {
// Skip the ProtocolVersion here; we will get it later
dup.skip(2);
int recordSize = dup.getUnsignedShort();
// Now wait until we have the entire record
if (original.remaining() < (5 + recordSize)) {
// Keep buffering
return false;
}
} else if (contentType == SSLV2_CLIENTHELLO) {
short len = dup.getUnsigned();
// Decode the length
int recordSize = ((contentType & 0x7f) << 8 | len);
// Now wait until we have the entire record
if (original.remaining() < (2 + recordSize)) {
// Keep buffering
return false;
}
} else {
// We're only interested in Handshake records
out.write(original.getSlice(original.remaining()));
return true;
}
// For the format of the ClientHello message, see RFC 5246,
// Section 7.4.1.2.
short messageType = dup.getUnsigned();
if (messageType != CLIENTHELLO_MESSAGE_TYPE) {
// We're only interested in ClientHello messages
out.write(original.getSlice(original.remaining()));
return true;
}
if (contentType == HANDSHAKE_CONTENT_TYPE) {
// If we're not an SSLv2 ClientHello, then skip the ClientHello
// message size.
dup.skip(3);
// Use the ClientHello ProtocolVersion
SslVersion version = SslVersion.decode(dup.getUnsignedShort());
// Skip ClientRandom
dup.skip(32);
// Skip SessionID
int sessionIDSize = dup.getUnsigned();
dup.skip(sessionIDSize);
// Now we get to what we're really after: the ciphersuites supported
// by the client.
int cipherSuiteSize = dup.getUnsignedShort();
// cipherSuiteSize is the number of bytes; each cipher is specified
// using a short (2 bytes). Thus the cipher suite count is the half
// the cipher suite size.
int cipherSuiteCount = cipherSuiteSize / 2;
// Iterate through each of the ciphersuites
for (int i = 0; i < cipherSuiteCount; i++) {
int cipher = dup.getUnsignedShort();
if (cipher == RC4_SHA_HEX) {
ciphers.add(SslCipherSelectionFilter.RC4_SHA);
} else if (cipher == RC4_MD5_HEX) {
ciphers.add(SslCipherSelectionFilter.RC4_MD5);
}
}
} else {
// SSLv2 ClientHello.
// Use the ClientHello ProtocolVersion
SslVersion version = SslVersion.decode(dup.getUnsignedShort());
// Determine cipher specs size
short msb = dup.getUnsigned();
short lsb = dup.getUnsigned();
int cipherSuiteSize = ((msb << 8) | lsb);
// Skip the sessionID size
dup.skip(2);
// Skip the challenge size
dup.skip(2);
// Now we get to what we're really after: the ciphersuites supported
// by the client.
// cipherSuiteSize is the number of bytes; each cipher is specified
// using a medium int (3 bytes).
int cipherSuiteCount = cipherSuiteSize / 3;
// Iterate through each of the ciphersuites, looking for
// SSL_RSA_WITH_RC4_128_MD5. (It's the only one supported in
// SSLv2 ClientHellos).
for (int i = 0; i < cipherSuiteCount; i++) {
int cipherKind = dup.getUnsignedMediumInt();
if (cipherKind == SSLV2_RC4_MD5_HEX) {
appletCiphers.add(SslCipherSelectionFilter.RC4_MD5);
}
}
}
out.write(original.getSlice(original.remaining()));
return true;
}
See this SslClientHelloDecoder.java file for the rest of the code.
Hope this helps!
Upvotes: 6