/* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
/** * Instances of this class acts as an explorer of the network data of an * SSL/TLS connection.
*/ publicfinalclass SSLExplorer {
// Private constructor prevents construction outside this class. private SSLExplorer() {
}
/** * The header size of TLS/SSL records. * <P> * The value of this constant is {@value}.
*/ publicfinalstaticint RECORD_HEADER_SIZE = 0x05;
/** * Returns the required number of bytes in the {@code source} * {@link ByteBuffer} necessary to explore SSL/TLS connection. * <P> * This method tries to parse as few bytes as possible from * {@code source} byte buffer to get the length of an * SSL/TLS record. * <P> * This method accesses the {@code source} parameter in read-only * mode, and does not update the buffer's properties such as capacity, * limit, position, and mark values. * * @param source * a {@link ByteBuffer} containing * inbound or outbound network data for an SSL/TLS connection. * @throws BufferUnderflowException if less than {@code RECORD_HEADER_SIZE} * bytes remaining in {@code source} * @return the required size in byte to explore an SSL/TLS connection
*/ publicfinalstaticint getRequiredSize(ByteBuffer source) {
ByteBuffer input = source.duplicate();
// Do we have a complete header? if (input.remaining() < RECORD_HEADER_SIZE) { thrownew BufferUnderflowException();
}
// Is it a handshake message? byte firstByte = input.get(); byte secondByte = input.get(); byte thirdByte = input.get(); if ((firstByte & 0x80) != 0 && thirdByte == 0x01) { // looks like a V2ClientHello // return (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2; return RECORD_HEADER_SIZE; // Only need the header fields
} else { return (((input.get() & 0xFF) << 8) | (input.get() & 0xFF)) + 5;
}
}
/** * Returns the required number of bytes in the {@code source} byte array * necessary to explore SSL/TLS connection. * <P> * This method tries to parse as few bytes as possible from * {@code source} byte array to get the length of an * SSL/TLS record. * * @param source * a byte array containing inbound or outbound network data for * an SSL/TLS connection. * @param offset * the start offset in array {@code source} at which the * network data is read from. * @param length * the maximum number of bytes to read. * * @throws BufferUnderflowException if less than {@code RECORD_HEADER_SIZE} * bytes remaining in {@code source} * @return the required size in byte to explore an SSL/TLS connection
*/ publicfinalstaticint getRequiredSize(byte[] source, int offset, int length) throws IOException {
/** * Launch and explore the security capabilities from byte buffer. * <P> * This method tries to parse as few records as possible from * {@code source} byte buffer to get the {@link SSLCapabilities} * of an SSL/TLS connection. * <P> * Please NOTE that this method must be called before any handshaking * occurs. The behavior of this method is not defined in this release * if the handshake has begun, or has completed. * <P> * This method accesses the {@code source} parameter in read-only * mode, and does not update the buffer's properties such as capacity, * limit, position, and mark values. * * @param source * a {@link ByteBuffer} containing * inbound or outbound network data for an SSL/TLS connection. * * @throws IOException on network data error * @throws BufferUnderflowException if not enough source bytes available * to make a complete exploration. * * @return the explored {@link SSLCapabilities} of the SSL/TLS * connection
*/ publicfinalstatic SSLCapabilities explore(ByteBuffer source) throws IOException {
ByteBuffer input = source.duplicate();
// Do we have a complete header? if (input.remaining() < RECORD_HEADER_SIZE) { thrownew BufferUnderflowException();
}
// Is it a handshake message? byte firstByte = input.get(); byte secondByte = input.get(); byte thirdByte = input.get(); if ((firstByte & 0x80) != 0 && thirdByte == 0x01) { // looks like a V2ClientHello return exploreV2HelloRecord(input,
firstByte, secondByte, thirdByte);
} elseif (firstByte == 22) { // 22: handshake record return exploreTLSRecord(input,
firstByte, secondByte, thirdByte);
} else { thrownew SSLException("Not handshake record");
}
}
/** * Launch and explore the security capabilities from byte array. * <P> * Please NOTE that this method must be called before any handshaking * occurs. The behavior of this method is not defined in this release * if the handshake has begun, or has completed. Once handshake has * begun, or has completed, the security capabilities can not and * should not be launched with this method. * * @param source * a byte array containing inbound or outbound network data for * an SSL/TLS connection. * @param offset * the start offset in array {@code source} at which the * network data is read from. * @param length * the maximum number of bytes to read. * * @throws IOException on network data error * @throws BufferUnderflowException if not enough source bytes available * to make a complete exploration. * @return the explored {@link SSLCapabilities} of the SSL/TLS * connection * * @see #explore(ByteBuffer)
*/ publicfinalstatic SSLCapabilities explore(byte[] source, int offset, int length) throws IOException {
ByteBuffer byteBuffer =
ByteBuffer.wrap(source, offset, length).asReadOnlyBuffer(); return explore(byteBuffer);
}
/* * uint8 V2CipherSpec[3]; * struct { * uint16 msg_length; // The highest bit MUST be 1; * // the remaining bits contain the length * // of the following data in bytes. * uint8 msg_type; // MUST be 1 * Version version; * uint16 cipher_spec_length; // It cannot be zero and MUST be a * // multiple of the V2CipherSpec length. * uint16 session_id_length; // This field MUST be empty. * uint16 challenge_length; // SHOULD use a 32-byte challenge * V2CipherSpec cipher_specs[V2ClientHello.cipher_spec_length]; * opaque session_id[V2ClientHello.session_id_length]; * opaque challenge[V2ClientHello.challenge_length; * } V2ClientHello;
*/ privatestatic SSLCapabilities exploreV2HelloRecord(
ByteBuffer input, byte firstByte, byte secondByte, byte thirdByte) throws IOException {
// We only need the header. We have already had enough source bytes. // int recordLength = (firstByte & 0x7F) << 8) | (secondByte & 0xFF); try { // Is it a V2ClientHello? if (thirdByte != 0x01) { thrownew SSLException( "Unsupported or Unrecognized SSL record");
}
// 0x00: major version of SSLv20 // 0x02: minor version of SSLv20 // // SNIServerName is an extension, SSLv20 doesn't support extension. returnnew SSLCapabilitiesImpl((byte)0x00, (byte)0x02,
helloVersionMajor, helloVersionMinor,
Collections.<SNIServerName>emptyList());
} catch (BufferUnderflowException bufe) { thrownew SSLProtocolException( "Invalid handshake record");
}
}
// Is it a handshake message? if (firstByte != 22) { // 22: handshake record thrownew SSLException("Not handshake record");
}
// We need the record version to construct SSLCapabilities. byte recordMajorVersion = secondByte; byte recordMinorVersion = thirdByte;
// Is there enough data for a full record? int recordLength = getInt16(input); if (recordLength > input.remaining()) { thrownew BufferUnderflowException();
}
// We have already had enough source bytes. try { return exploreHandshake(input,
recordMajorVersion, recordMinorVersion, recordLength);
} catch (BufferUnderflowException bufe) { thrownew SSLProtocolException( "Invalid handshake record");
}
}
/* * enum { * hello_request(0), client_hello(1), server_hello(2), * certificate(11), server_key_exchange (12), * certificate_request(13), server_hello_done(14), * certificate_verify(15), client_key_exchange(16), * finished(20) * (255) * } HandshakeType; * * struct { * HandshakeType msg_type; * uint24 length; * select (HandshakeType) { * case hello_request: HelloRequest; * case client_hello: ClientHello; * case server_hello: ServerHello; * case certificate: Certificate; * case server_key_exchange: ServerKeyExchange; * case certificate_request: CertificateRequest; * case server_hello_done: ServerHelloDone; * case certificate_verify: CertificateVerify; * case client_key_exchange: ClientKeyExchange; * case finished: Finished; * } body; * } Handshake;
*/ privatestatic SSLCapabilities exploreHandshake(
ByteBuffer input, byte recordMajorVersion, byte recordMinorVersion, int recordLength) throws IOException {
// What is the handshake type? byte handshakeType = input.get(); if (handshakeType != 0x01) { // 0x01: client_hello message thrownew IllegalStateException("Not initial handshaking");
}
// What is the handshake body length? int handshakeLength = getInt24(input);
// Theoretically, a single handshake message might span multiple // records, but in practice this does not occur. if (handshakeLength > (recordLength - 4)) { // 4: handshake header size thrownew SSLException("Handshake message spans multiple records");
}
int length = getInt16(input); // length of extensions while (length > 0) { int extType = getInt16(input); // extenson type int extLen = getInt16(input); // length of extension data
if (extType == 0x00) { // 0x00: type of server name indication return exploreSNIExt(input, extLen);
} else { // ignore other extensions
ignoreByteVector(input, extLen);
}
Map<Integer, SNIServerName> sniMap = new LinkedHashMap<>();
int remains = extLen; if (extLen >= 2) { // "server_name" extension in ClientHello int listLen = getInt16(input); // length of server_name_list if (listLen == 0 || listLen + 2 != extLen) { thrownew SSLProtocolException( "Invalid server name indication extension");
}
remains -= 2; // 0x02: the length field of server_name_list while (remains > 0) { int code = getInt8(input); // name_type int snLen = getInt16(input); // length field of server name if (snLen > remains) { thrownew SSLProtocolException( "Not enough data to fill declared vector size");
} byte[] encoded = newbyte[snLen];
input.get(encoded);
SNIServerName serverName; switch (code) { case StandardConstants.SNI_HOST_NAME: if (encoded.length == 0) { thrownew SSLProtocolException( "Empty HostName in server name indication");
}
serverName = new SNIHostName(encoded); break; default:
serverName = new UnknownServerName(code, encoded);
} // check for duplicated server name type if (sniMap.put(serverName.getType(), serverName) != null) { thrownew SSLProtocolException( "Duplicated server name of type " +
serverName.getType());
}
remains -= encoded.length + 3; // NameType: 1 byte // HostName length: 2 bytes
}
} elseif (extLen == 0) { // "server_name" extension in ServerHello thrownew SSLProtocolException( "Not server name indication extension in client");
}
if (remains != 0) { thrownew SSLProtocolException( "Invalid server name indication extension");
}
return Collections.<SNIServerName>unmodifiableList( new ArrayList<>(sniMap.values()));
}
privatestaticvoid ignoreByteVector(ByteBuffer input, int length) { if (length != 0) { int position = input.position();
input.position(position + length);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.