Skip to content

Commit

Permalink
standard ssl implementation (#110)
Browse files Browse the repository at this point in the history
* ssl

* update (#111)

* can handshake ssl

* add EzySslHandshakeHandler

* update keystore

* update

* can ssh hanshake with android client

* can transfer data with swift

* add EzyChannel.pack

* update EzySecureSocketDataReceiver

* update ssl-keystore

* close connection

* EzySslHandshakeHandlerTest unitest for unwrap

* done EzySslHandshakeHandlerTest

* update EzyAbstractResponseApiTest

* add setting unit test

* change SslType and update EzyAbstractServerBootstrapBuilderTest

* update EzySocketResponseApiTest

* update EzyHandShakeControllerTest

* add EzyChannelTest and SslByteBuffersTest

* add EzySocketChannels

* add EzySecureSocketDataReceiverTest

* add EzyNioSecureSocketChannelTest

* update bootstrap and test

* update EzyNioSocketAcceptorTest

* update EzyAbstractHandlerGroupTest

* update EzySimpleNioHandlerGroupTest

* update EzySecureSocketDataReceiverTest

* update EzyNioServerBootstrapBuilderImplTest

* update EzyAbstractHandlerGroupTest

* update unit test

* update EzyHandshakeController to check clientKey != null

* check null

* update EzySimplePacket.getSize

* remove tryNewSslContext

* update EzySslHandshakeHandler logs

* v2

* update

* runnable

* move logic to channel

* try to complete unit test

* almost done unit test

* complete unit test and remove session locks

* update settings

* add max request size

* resolve review comments
  • Loading branch information
tvd12 authored Oct 15, 2023
1 parent b47df6e commit 06c0331
Show file tree
Hide file tree
Showing 71 changed files with 4,291 additions and 384 deletions.
2 changes: 1 addition & 1 deletion ezyfox-server-core/settings/ssl-config.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ssl.keystore="ssl/ssl-key-store.txt"
ssl.keystore=ssl/ssl-keystore.txt
ssl.keystore_password=ssl/ssl-keystore-password.txt
ssl.certificate_password=ssl/ssl-certificate-password.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

import com.tvd12.ezyfox.constant.EzyConstant;
import com.tvd12.ezyfox.entity.EzyArray;
import com.tvd12.ezyfox.util.EzyLoggable;
import com.tvd12.ezyfoxserver.constant.EzyTransportType;
import com.tvd12.ezyfoxserver.entity.EzySession;
import com.tvd12.ezyfoxserver.response.EzyPackage;
import com.tvd12.ezyfoxserver.socket.EzyPacket;
import com.tvd12.ezyfoxserver.socket.EzySimplePacket;

import java.util.Collection;

public abstract class EzyAbstractResponseApi implements EzyResponseApi {
public abstract class EzyAbstractResponseApi
extends EzyLoggable
implements EzyResponseApi {

@Override
public void response(
Expand All @@ -32,13 +37,40 @@ protected final void normalResponse(
return;
}
Object bytes = encodeData(pack.getData());
EzyConstant transportType = pack.getTransportType();
if (immediate) {
for (EzySession session : recipients) {
session.sendNow(createPacket(bytes, pack));
EzyPacket packet = null;
try {
packet = createPacket(
session,
transportType,
bytes
);
sendPacketNow(session, packet);
} catch (Throwable e) {
if (packet != null) {
packet.release();
}
logger.info("response data now to session: {} failed", session, e);
}
}
} else {
for (EzySession session : recipients) {
session.send(createPacket(bytes, pack));
EzyPacket packet = null;
try {
packet = createPacket(
session,
transportType,
bytes
);
sendPacket(session, packet);
} catch (Throwable e) {
if (packet != null) {
packet.release();
}
logger.info("response data to session: {} failed", session, e);
}
}
}
}
Expand All @@ -53,26 +85,106 @@ protected final void secureResponse(
return;
}
byte[] messageContent = dataToMessageContent(pack.getData());
EzyConstant transportType = pack.getTransportType();
if (immediate) {
for (EzySession session : recipients) {
byte[] bytes = encryptMessageContent(messageContent, session.getSessionKey());
session.sendNow(createPacket(bytes, pack));
EzyPacket packet = null;
try {
byte[] bytes = encryptMessageContent(messageContent, session.getSessionKey());
packet = createPacket(
session,
transportType,
bytes
);
sendPacketNow(session, packet);
} catch (Throwable e) {
if (packet != null) {
packet.release();
}
logger.info("response data now to session: {} failed", session, e);
}
}
} else {
for (EzySession session : recipients) {
byte[] bytes = encryptMessageContent(messageContent, session.getSessionKey());
session.send(createPacket(bytes, pack));
EzyPacket packet = null;
try {
byte[] bytes = encryptMessageContent(messageContent, session.getSessionKey());
packet = createPacket(
session,
transportType,
bytes
);
sendPacket(session, packet);
} catch (Throwable e) {
if (packet != null) {
packet.release();
}
logger.info("response data to session: {} failed", session, e);
}
}
}
}

protected EzySimplePacket createPacket(Object bytes, EzyPackage pack) {
private EzyPacket createPacket(
EzySession session,
EzyConstant transportType,
Object bytes
) {
EzyConstant actualTransportType = transportType;
if (actualTransportType == EzyTransportType.UDP_OR_TCP) {
actualTransportType = session.getDatagramChannelPool() != null
? EzyTransportType.UDP
: EzyTransportType.TCP;
}
return createPacket(actualTransportType, bytes);
}

protected EzySimplePacket createPacket(
EzyConstant transportType,
Object bytes
) {
EzySimplePacket packet = new EzySimplePacket();
packet.setTransportType(pack.getTransportType());
packet.setTransportType(transportType);
packet.setData(bytes);
return packet;
}

private void sendPacket(
EzySession session,
EzyPacket packet
) throws Exception {
if (packet.getTransportType() == EzyTransportType.UDP) {
session.send(packet);
} else {
sendTcpPacket(session, packet);
}
}

protected void sendTcpPacket(
EzySession session,
EzyPacket packet
) throws Exception {
session.send(packet);
}

private void sendPacketNow(
EzySession session,
EzyPacket packet
) throws Exception {
if (packet.getTransportType() == EzyTransportType.UDP) {
session.sendNow(packet);
} else {
sendTcpPacketNow(session, packet);
}
}

protected void sendTcpPacketNow(
EzySession session,
EzyPacket packet
) throws Exception {
session.sendNow(packet);
}

protected abstract EzyConstant getConnectionType();

protected abstract Object encodeData(EzyArray data) throws Exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ public EzyProxyResponseApi(EzyCodecFactory codecFactory) {

private EzyResponseApi newSocketResponseApi(Object socketEncoder) {
return socketEncoder != null
? new EzySocketResponseApi(socketEncoder)
? createSocketResponseApi(socketEncoder)
: EzyEmptyResponseApi.getInstance();
}

protected EzySocketResponseApi createSocketResponseApi(
Object socketEncoder
) {
return new EzySocketResponseApi(socketEncoder);
}

private EzyResponseApi newWebsocketResponseApi(Object wsEncoder) {
return wsEncoder != null
? new EzyWsResponseApi(wsEncoder)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.tvd12.ezyfoxserver.api;

import com.tvd12.ezyfoxserver.codec.EzyCodecFactory;

public class EzySecureProxyResponseApi extends EzyProxyResponseApi {

public EzySecureProxyResponseApi(EzyCodecFactory codecFactory) {
super(codecFactory);
}

@Override
protected EzySocketResponseApi createSocketResponseApi(
Object socketEncoder
) {
return new EzySecureSocketResponseApi(socketEncoder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.tvd12.ezyfoxserver.api;

import com.tvd12.ezyfoxserver.entity.EzySession;
import com.tvd12.ezyfoxserver.exception.EzyConnectionCloseException;
import com.tvd12.ezyfoxserver.socket.EzyChannel;
import com.tvd12.ezyfoxserver.socket.EzyPacket;
import com.tvd12.ezyfoxserver.socket.EzySecureChannel;

import java.io.IOException;

public class EzySecureSocketResponseApi extends EzySocketResponseApi {

public EzySecureSocketResponseApi(Object encoder) {
super(encoder);
}

@Override
protected void sendTcpPacket(
EzySession session,
EzyPacket packet
) throws Exception {
EzyChannel channel = session.getChannel();
if (channel == null) {
throw new IOException("session destroyed");
}
EzySecureChannel secureChannel = (EzySecureChannel) channel;
try {
synchronized (secureChannel.getPackingLock()) {
byte[] packedBytes = secureChannel.pack(
(byte[]) packet.getData()
);
packet.replaceData(packedBytes);
session.send(packet);
}
} catch (EzyConnectionCloseException e) {
session.disconnect();
throw e;
}
}

@Override
protected void sendTcpPacketNow(
EzySession session,
EzyPacket packet
) throws Exception {
EzyChannel channel = session.getChannel();
if (channel == null) {
throw new IOException("session destroyed");
}
EzySecureChannel secureChannel = (EzySecureChannel) channel;
try {
synchronized (secureChannel.getPackingLock()) {
byte[] packedBytes = secureChannel.pack(
(byte[]) packet.getData()
);
packet.replaceData(packedBytes);
session.sendNow(packet);
}
} catch (EzyConnectionCloseException e) {
session.disconnect();
throw e;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public class EzySocketResponseApi extends EzyAbstractResponseApi {
protected final EzyMessageDataEncoder encoder;

public EzySocketResponseApi(Object encoder) {
this.encoder = new EzySimpleMessageDataEncoder((EzyObjectToByteEncoder) encoder);
this.encoder = new EzySimpleMessageDataEncoder(
(EzyObjectToByteEncoder) encoder
);
}

@Override
Expand All @@ -21,14 +23,21 @@ protected Object encodeData(EzyArray data) throws Exception {
}

@Override
protected byte[] dataToMessageContent(EzyArray data) throws Exception {
protected byte[] dataToMessageContent(
EzyArray data
) throws Exception {
return encoder.toMessageContent(data);
}

@Override
protected byte[] encryptMessageContent(
byte[] messageContent, byte[] encryptionKey) throws Exception {
return encoder.encryptMessageContent(messageContent, encryptionKey);
byte[] messageContent,
byte[] encryptionKey
) throws Exception {
return encoder.encryptMessageContent(
messageContent,
encryptionKey
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public void response(EzyPackage pack, boolean immediate) throws Exception {
}

@Override
protected EzySimplePacket createPacket(Object bytes, EzyPackage pack) {
EzySimplePacket packet = super.createPacket(bytes, pack);
protected EzySimplePacket createPacket(EzyConstant transportType, Object bytes) {
EzySimplePacket packet = super.createPacket(transportType, bytes);
packet.setBinary(false);
return packet;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ protected EzyServerContext newServerContext(EzyServer server) {
}

protected SSLContext newSslContext(EzySslConfigSetting sslConfig) {
if (getWebsocketSetting().isSslActive()) {
EzySocketSetting socketSetting = getSocketSetting();
EzyWebSocketSetting webSocketSetting = getWebsocketSetting();
boolean activeSslForSocket = socketSetting.isCertificationSslActive();
boolean activeSslForWebsocket = webSocketSetting.isSslActive();
if (activeSslForSocket || activeSslForWebsocket) {
return newSslContextInitializer(sslConfig).init();
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,27 @@ public EzyCloseSessionImpl(EzyServerContext ctx) {

@Override
public void close(EzySession session, EzyConstant reason) {
sendToClients(session, reason);
sendToClient(session, reason);
disconnectSession(session, reason);
}

protected void sendToClients(EzySession session, EzyConstant reason) {
protected void sendToClient(EzySession session, EzyConstant reason) {
if (shouldSendToClient(reason)) {
doSendToClients(session, reason);
doSendToClient(session, reason);
}
}

protected boolean shouldSendToClient(EzyConstant reason) {
return reason != EzyDisconnectReason.UNKNOWN;
return reason != EzyDisconnectReason.UNKNOWN
&& reason != EzyDisconnectReason.SSH_HANDSHAKE_FAILED;
}

protected void disconnectSession(EzySession session, EzyConstant reason) {
logger.info("close session: {}, reason: {}", session.getClientAddress(), reason);
session.close();
}

protected void doSendToClients(EzySession session, EzyConstant reason) {
protected void doSendToClient(EzySession session, EzyConstant reason) {
EzyResponse response = newResponse(reason);
context.sendNow(response, session);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public enum EzyDisconnectReason implements EzyConstant {
ADMIN_KICK(5),
MAX_REQUEST_PER_SECOND(6),
MAX_REQUEST_SIZE(7),
SERVER_ERROR(8);
SERVER_ERROR(8),
SSH_HANDSHAKE_FAILED(9);

private static final Map<Integer, EzyDisconnectReason> REASONS_BY_ID = reasonsById();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.tvd12.ezyfoxserver.constant;

public enum SslType {
CERTIFICATION,
CUSTOMIZATION
}
Loading

0 comments on commit 06c0331

Please sign in to comment.