public enum HandshakeType {
HELLO_REQUEST(0),
CLIENT_HELLO(1),
SERVER_HELLO(2),
// certificate
CERTIFICATE(3),
// server_key_exchange (12)
SERVER_KEY_EXCHANGE(12),
// certificate_request(13)
CERTIFICATE_REQUEST(13),
// server_hello_done(14)
SERVER_HELLO_DONE(14),
// certificate_verify(15)
CERTIFICATE_VERIFY(15),
// client_key_exchange(16)
CLIENT_KEY_EXCHANGE(16),
// finished(20)
FINISHED(20);
private int value;
private HandshakeType(int value) {
this.value = value;
}
public int value() {
return value;
}
}
public abstract class Handshake {
public abstract ByteArrayOutputStream body() throws IOException;
}
public enum ContentType {
CHANGE_CIPHER_SPEC(20),
ALERT(21),
HANDSHAKE(22),
APPLICATION_DATA(23);
private int value;
private ContentType(int value) {
this.value = value;
}
public int value() {
return value;
}
}
public enum CipherSuite {
TLS_NULL_WITH_NULL_NULL((byte) 0x00, (byte) 0x00),
TLS_RSA_WITH_NULL_MD5((byte) 0x00, (byte) 0x01),
TLS_RSA_WITH_NULL_SHA((byte) 0x00, (byte) 0x02),
TLS_RSA_EXPORT_WITH_RC4_40_MD5((byte) 0x00, (byte) 0x03),
TLS_RSA_WITH_RC4_128_MD5((byte) 0x00, (byte) 0x04),
TLS_RSA_WITH_RC4_128_SHA((byte) 0x00, (byte) 0x05),
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5((byte) 0x00, (byte) 0x06),
TLS_RSA_WITH_IDEA_CBC_SHA((byte) 0x00, (byte) 0x07),
TLS_RSA_EXPORT_WITH_DES40_CBC_SHA((byte) 0x00, (byte) 0x08),
TLS_RSA_WITH_DES_CBC_SHA((byte) 0x00, (byte) 0x09),
TLS_RSA_WITH_3DES_EDE_CBC_SHA((byte) 0x00, (byte) 0x0A),
TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA((byte) 0x00, (byte) 0x0B),
TLS_DH_DSS_WITH_DES_CBC_SHA((byte) 0x00, (byte) 0x0C),
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA((byte) 0x00, (byte) 0x0D),
TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA((byte) 0x00, (byte) 0x0E),
TLS_DH_RSA_WITH_DES_CBC_SHA((byte) 0x00, (byte) 0x0F),
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA((byte) 0x00, (byte) 0x10),
TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA((byte) 0x00, (byte) 0x11),
TLS_DHE_DSS_WITH_DES_CBC_SHA((byte) 0x00, (byte) 0x12),
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA((byte) 0x00, (byte) 0x13),
TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA((byte) 0x00, (byte) 0x14),
TLS_DHE_RSA_WITH_DES_CBC_SHA((byte) 0x00, (byte) 0x15),
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA((byte) 0x00, (byte) 0x16),
TLS_DH_anon_EXPORT_WITH_RC4_40_MD5((byte) 0x00, (byte) 0x17),
TLS_DH_anon_WITH_RC4_128_MD5((byte) 0x00, (byte) 0x18),
TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA((byte) 0x00, (byte) 0x19),
TLS_DH_anon_WITH_DES_CBC_SHA((byte) 0x00, (byte) 0x1A),
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA((byte) 0x00, (byte) 0x1B);
private byte hb;
private byte lb;
private CipherSuite(byte high, byte low) {
this.hb = high;
this.lb = low;
}
public byte high() {
return hb;
}
public byte low() {
return lb;
}
}
public class ClientHello extends Handshake {
// 23 * 16 + 3
public ByteArrayOutputStream body() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(Test.MAJOR);
bos.write(Test.MINOR);
long ms = System.currentTimeMillis();
ms = ms / 1000;
bos.write((int) ((ms << 32) >> 56));
bos.write((int) ((ms << 40) >> 56));
bos.write((int) ((ms << 48) >> 56));
bos.write((int) ((ms << 56) >> 56));
Random random = new Random(ms);
byte[] random_bytes = new byte[28];
random.nextBytes(random_bytes);
bos.write(random_bytes);
// bos.write(0); // the length of session id, this value indicates the bytes of length
// // if session id is empty, then the length of session id is zero
// // and so here do not write session id
byte[] sessionId = new byte[32];
Random sessionIdGenerator = new Random(System.currentTimeMillis());
sessionIdGenerator.nextBytes(sessionId);
bos.write(sessionId.length);
bos.write(sessionId);
// // the length of cipher suite, this value indicates the bytes of cipher suite
// bos.write(0);
// bos.write(0);
// // if doe's contains cipher suite, do not write cipher suites
// the length of cipher suite, this value indicates the bytes of cipher suite
bos.write(0);
bos.write(2);
bos.write(CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.high());
bos.write(CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.low());
bos.write(1);
bos.write(0);
return bos;
}
}
public class ClientHello extends Handshake {
// 23 * 16 + 3
public ByteArrayOutputStream body() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(Test.MAJOR);
bos.write(Test.MINOR);
long ms = System.currentTimeMillis();
ms = ms / 1000;
bos.write((int) ((ms << 32) >> 56));
bos.write((int) ((ms << 40) >> 56));
bos.write((int) ((ms << 48) >> 56));
bos.write((int) ((ms << 56) >> 56));
Random random = new Random(ms);
byte[] random_bytes = new byte[28];
random.nextBytes(random_bytes);
bos.write(random_bytes);
// bos.write(0); // the length of session id, this value indicates the bytes of length
// // if session id is empty, then the length of session id is zero
// // and so here do not write session id
byte[] sessionId = new byte[32];
Random sessionIdGenerator = new Random(System.currentTimeMillis());
sessionIdGenerator.nextBytes(sessionId);
bos.write(sessionId.length);
bos.write(sessionId);
// // the length of cipher suite, this value indicates the bytes of cipher suite
// bos.write(0);
// bos.write(0);
// // if doe's contains cipher suite, do not write cipher suites
// // the length of cipher suite, this value indicates the bytes of cipher suite
// bos.write(0);
// bos.write(2);
// bos.write(CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.high());
// bos.write(CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.low());
// the length of cipher suite, this value indicates the bytes of cipher suite
bos.write(0x00);
bos.write(0x38);
byte[] cipherSuites = new byte[] {(byte)0xc0, (byte)0x14, (byte)0x00, (byte)0x88,
(byte)0x00, (byte)0x87, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x38, (byte)0xc0, (byte)0x0f,
(byte)0x00, (byte)0x84, (byte)0x00, (byte)0x35, (byte)0xc0, (byte)0x11, (byte)0xc0, (byte)0x13,
(byte)0x00, (byte)0x45, (byte)0x00, (byte)0x44, (byte)0x00, (byte)0x66, (byte)0x00, (byte)0x33,
(byte)0x00, (byte)0x32, (byte)0xc0, (byte)0x0c, (byte)0xc0, (byte)0x0e, (byte)0x00, (byte)0x96,
(byte)0x00, (byte)0x41, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x2f,
(byte)0xc0, (byte)0x12, (byte)0x00, (byte)0x16, (byte)0x00, (byte)0x13, (byte)0xc0, (byte)0x0d,
(byte)0xfe, (byte)0xff, (byte)0x00, (byte)0x0a};
bos.write(cipherSuites);
bos.write(1);
bos.write(0);
return bos;
}
}
public class Test {
// 版本由主版本和小版本组成:major.minor,如3.2,3表示主版本,2表示小版本
// 目前版本包括3.1, 3.2
// 主版本,3表示版本3.x, 3.0以上的版本。
public static final int MAJOR = 0x03;
// 小版本
public static final int MINOR = 0x02;
public static final int DEFAULT_PORT = 443;
private Socket socket;
public Test(String host) throws IOException {
this(host, DEFAULT_PORT);
}
public Test(String host, int port) throws IOException {
socket = new Socket(host, port);
}
public void handshake() throws IOException {
OutputStream os = socket.getOutputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(ContentType.HANDSHAKE.value());
bos.write(MAJOR);
bos.write(MINOR);
ByteArrayOutputStream bbos = hello();
int length = bbos.size();
bos.write((int) ((length << 16) >> 24));
bos.write((int) ((length << 24) >> 24));
bos.write(bbos.toByteArray());
os.write(bos.toByteArray());
InputStream is = socket.getInputStream();
}
public ByteArrayOutputStream hello() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(HandshakeType.CLIENT_HELLO.value());
ClientHello client_hello = new ClientHello();
ByteArrayOutputStream bbos = client_hello.body();
int length = bbos.size();
bos.write((int) ((length << 8) >> 24));
bos.write((int) ((length << 16) >> 24));
bos.write((int) ((length << 24) >> 24));
bos.write(bbos.toByteArray());
return bos;
}
}
public class TestTest {
private static final String DEFAULT_HOST = "112.65.203.33";
private static com.chos.tls.Test test;
@BeforeClass
public static void initialize() throws IOException {
test = new com.chos.tls.Test(DEFAULT_HOST);
}
@Test
public void handshake() throws IOException {
test.handshake();
}
}