-
Notifications
You must be signed in to change notification settings - Fork 6
Feature: Mutual TLS (mTLS) support with client certificate authentication #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
crazyrokr
wants to merge
7
commits into
JavaSaBr:develop
Choose a base branch
from
crazyrokr:mtls-support
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
8a9c59d
implement mutual tls support
crazyrokr 6e49fff
fix mutual tls flow
crazyrokr 740086c
add mutual tls tests
crazyrokr a3d0505
update javadocs
crazyrokr e968498
fix tls flow
crazyrokr 1b17a25
apply formatting
crazyrokr 963d0b7
remove unnecessary changes
crazyrokr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
rlib-network/src/main/java/javasabr/rlib/network/impl/StringDataMtlsServerConnection.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package javasabr.rlib.network.impl; | ||
|
|
||
| import javasabr.rlib.network.BufferAllocator; | ||
| import javasabr.rlib.network.Network; | ||
| import javasabr.rlib.network.packet.impl.StringReadableNetworkPacket; | ||
|
|
||
| import javax.net.ssl.SSLContext; | ||
| import java.nio.channels.AsynchronousSocketChannel; | ||
|
|
||
| /** | ||
| * @author crazyrokr | ||
| */ | ||
| public class StringDataMtlsServerConnection extends DefaultDataSslConnection<StringDataMtlsServerConnection> { | ||
|
|
||
| public StringDataMtlsServerConnection( | ||
| Network<StringDataMtlsServerConnection> network, | ||
| AsynchronousSocketChannel channel, | ||
| BufferAllocator bufferAllocator, | ||
| SSLContext sslContext) { | ||
| super(network, channel, bufferAllocator, sslContext, 100, 2, false); | ||
| sslEngine.setNeedClientAuth(true); | ||
| } | ||
|
|
||
| @Override | ||
| protected StringReadableNetworkPacket<StringDataMtlsServerConnection> createReadablePacket() { | ||
| return new StringReadableNetworkPacket<>(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
153 changes: 153 additions & 0 deletions
153
rlib-network/src/test/java/javasabr/rlib/network/packet/impl/SslPacketReaderTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| package javasabr.rlib.network.packet.impl; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
| import static org.mockito.ArgumentMatchers.any; | ||
| import static org.mockito.Mockito.mock; | ||
| import static org.mockito.Mockito.verify; | ||
| import static org.mockito.Mockito.when; | ||
|
|
||
| import java.nio.ByteBuffer; | ||
| import java.util.concurrent.atomic.AtomicInteger; | ||
| import java.util.function.Consumer; | ||
| import javasabr.rlib.network.BufferAllocator; | ||
| import javasabr.rlib.network.Network; | ||
| import javasabr.rlib.network.NetworkConfig; | ||
| import javasabr.rlib.network.UnsafeConnection; | ||
| import javasabr.rlib.network.impl.DefaultBufferAllocator; | ||
| import javasabr.rlib.network.packet.ReadableNetworkPacket; | ||
| import javasabr.rlib.network.packet.WritableNetworkPacket; | ||
| import javax.net.ssl.SSLEngine; | ||
| import javax.net.ssl.SSLEngineResult; | ||
| import javax.net.ssl.SSLEngineResult.HandshakeStatus; | ||
| import javax.net.ssl.SSLEngineResult.Status; | ||
| import javax.net.ssl.SSLSession; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.api.extension.ExtendWith; | ||
| import org.mockito.Mock; | ||
| import org.mockito.junit.jupiter.MockitoExtension; | ||
| import org.mockito.junit.jupiter.MockitoSettings; | ||
| import org.mockito.quality.Strictness; | ||
|
|
||
| /** | ||
| * The tests of SSL packet reader | ||
| * | ||
| * @author crazyrokr | ||
| */ | ||
| @ExtendWith(MockitoExtension.class) | ||
| @MockitoSettings(strictness = Strictness.LENIENT) | ||
| public class SslPacketReaderTest { | ||
|
|
||
| private interface TestConnection extends UnsafeConnection<TestConnection> {} | ||
|
|
||
| @Mock | ||
| private TestConnection connection; | ||
|
|
||
| @Mock | ||
| private Network<TestConnection> network; | ||
|
|
||
| @Mock | ||
| private SSLEngine sslEngine; | ||
|
|
||
| @Mock | ||
| private SSLSession sslSession; | ||
|
|
||
| @Mock | ||
| private Consumer<ReadableNetworkPacket<TestConnection>> packetHandler; | ||
|
|
||
| @Mock | ||
| private Consumer<WritableNetworkPacket<TestConnection>> packetWriter; | ||
|
|
||
| private BufferAllocator bufferAllocator; | ||
|
|
||
| @BeforeEach | ||
| void setUp() { | ||
| bufferAllocator = new DefaultBufferAllocator(NetworkConfig.DEFAULT_CLIENT); | ||
| when(connection.bufferAllocator()).thenReturn(bufferAllocator); | ||
| when(connection.network()).thenReturn((Network) network); | ||
| when(connection.remoteAddress()).thenReturn("test-address"); | ||
| when(network.config()).thenReturn(NetworkConfig.DEFAULT_CLIENT); | ||
| when(sslEngine.getSession()).thenReturn(sslSession); | ||
| when(sslSession.getApplicationBufferSize()).thenReturn(1024); | ||
| when(sslSession.getPacketBufferSize()).thenReturn(1024); | ||
| } | ||
|
|
||
| private static class TestSslPacketReader extends | ||
| AbstractSslNetworkPacketReader<ReadableNetworkPacket<TestConnection>, TestConnection> { | ||
|
|
||
| private final AtomicInteger readPacketsCount = new AtomicInteger(); | ||
|
|
||
| protected TestSslPacketReader( | ||
| TestConnection connection, | ||
| Consumer<? super ReadableNetworkPacket<TestConnection>> packetHandler, | ||
| SSLEngine sslEngine, | ||
| Consumer<WritableNetworkPacket<TestConnection>> packetWriter) { | ||
| super(connection, () -> {}, packetHandler, packetHandler, sslEngine, packetWriter, 100); | ||
| } | ||
|
|
||
| @Override | ||
| protected boolean canStartReadPacket(ByteBuffer buffer) { | ||
| return buffer.remaining() >= 1; | ||
| } | ||
|
|
||
| @Override | ||
| protected int readFullPacketLength(ByteBuffer buffer) { | ||
| return 1; | ||
| } | ||
|
|
||
| @Override | ||
| protected ReadableNetworkPacket<TestConnection> createPacketFor( | ||
| ByteBuffer buffer, | ||
| int startPacketPosition, | ||
| int packetFullLength, | ||
| int packetDataLength) { | ||
| buffer.get(); // consume 1 byte | ||
| readPacketsCount.incrementAndGet(); | ||
| return mock(ReadableNetworkPacket.class); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| void testShouldNotLoseDataOnNeedWrapDuringHandshake() throws Exception { | ||
| // given | ||
| var reader = new TestSslPacketReader(connection, packetHandler, sslEngine, packetWriter); | ||
|
|
||
| // Initial state: NEED_UNWRAP | ||
| when(sslEngine.getHandshakeStatus()).thenReturn(HandshakeStatus.NEED_UNWRAP); | ||
|
|
||
| // First unwrap will result in NEED_WRAP and status OK, consuming some data | ||
| // MQTT broker received 10 bytes, first 5 bytes are handshake, and the last 5 bytes are application data | ||
| ByteBuffer networkData = ByteBuffer.allocate(10); | ||
| networkData.put(new byte[10]); | ||
| networkData.flip(); | ||
|
|
||
| // doHandshake calls unwrap in NEED_UNWRAP, consumes first 5 bytes, then returns OK | ||
| when(sslEngine.unwrap(any(ByteBuffer.class), any(ByteBuffer[].class))).thenAnswer(invocation -> { | ||
| ByteBuffer in = invocation.getArgument(0); | ||
| in.position(in.position() + 5); // consume 5 bytes of handshake | ||
| // Change status to NEED_WRAP for next getHandshakeStatus() call | ||
| when(sslEngine.getHandshakeStatus()).thenReturn(HandshakeStatus.NEED_WRAP); | ||
| return new SSLEngineResult(Status.OK, HandshakeStatus.NEED_WRAP, 5, 0); | ||
| }); | ||
|
|
||
| // decryptAndRead calls unwrap, consumes the remaining 5 bytes, then return FINISHED or NOT_HANDSHAKING | ||
| when(sslEngine.unwrap(any(ByteBuffer.class), any(ByteBuffer.class))).thenAnswer(invocation -> { | ||
| ByteBuffer in = invocation.getArgument(0); | ||
| ByteBuffer out = invocation.getArgument(1); | ||
| int remaining = in.remaining(); | ||
| in.position(in.limit()); // consume all | ||
| out.put(new byte[remaining]); // put decrypted data (mocked) | ||
| when(sslEngine.getHandshakeStatus()).thenReturn(HandshakeStatus.NOT_HANDSHAKING); | ||
| return new SSLEngineResult(Status.OK, HandshakeStatus.NOT_HANDSHAKING, remaining, remaining); | ||
| }); | ||
|
|
||
| // when | ||
| reader.readPackets(networkData); | ||
|
|
||
| // then | ||
| // readPackets should have been called for the remaining 5 bytes, | ||
| // since each packet is 1 byte, it should have read 5 packets | ||
| assertThat(reader.readPacketsCount.get()).isEqualTo(5); | ||
| verify(packetWriter).accept(any(SslWrapRequestNetworkPacket.class)); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.