Is there any Java x509certificate ClientHello parser in Java?

情到浓时终转凉″ 提交于 2019-12-06 12:41:39

问题


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?


回答1:


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:

  • SSLv2 vs SSLv3 vs TLSv1 ClientHello formats
  • Your question title mentions certificates; client certificates are not present ClientHello 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:
    //
    //  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;
}

See this SslClientHelloDecoder.java file for the rest of the code.

Hope this helps!



来源:https://stackoverflow.com/questions/34131970/is-there-any-java-x509certificate-clienthello-parser-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!