我收到了一个字节数据包,它是SSL协议数据包的ClientHello
。
在我开始自己编写代码之前,通过遍历整个字节来获取每个字段值的代码,我想知道是否有用于获取这些字节的任何Java对象(来自java.security)并解析数据,以便我可以使用SSL协议字段并使用它们?
答案 0 :(得分:3)
据我所知,Java中的java.security
包不提供您所寻找的任何功能。 GitHub上可能还有其他示例/库。
如果你最终自己这样做,关键的事情是:
ClientHello
格式ClientHello
消息;它们仅在握手过程中稍后发送,以响应来自服务器的CertificateRequest
消息。解码SSL / TLS消息通常涉及读取(并重新读取)相关RFC,并通过其在数据包内的位置确定每个字节值表示的内容。设计为二进制协议,为了提高效率,梳理您想要的字段可能比您认为必要的更复杂。但是,了解这些领域是非常值得的。
这里有一些Java代码可能有助于您入门。 注意它在Java中使用字节缓冲区,因为在解码这样的二进制协议时必须处理 unsigned 数据类型。
// 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:
//
// http://tools.ietf.org/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;
}
请参阅此SslClientHelloDecoder.java文件以了解其余代码。
希望这有帮助!