Skip to content

Commit 12fee7d

Browse files
committed
Introducing the tls protocol to automaticaly encapsulate a ZMQ socket in
TLS.
1 parent 0ce7f90 commit 12fee7d

File tree

16 files changed

+616
-205
lines changed

16 files changed

+616
-205
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changelog
22

3+
## v2.0.0 (2026-??-??)
4+
5+
## Added
6+
7+
* Errno now embed the thrown exception if needed. So adding event to notify that an exception was thrown, that can be
8+
logged or handled. This needs to increase the value of zmq.ZMQ.ZMQ_EVENT_ALL to 0xffffffff.
9+
* Many inner Enum are now autonomous.
10+
* Event serialization now directly resolve serialized values.
11+
* Adding the protocol TLS protocol. As Java doesn’t provide a way to handle TLS in channel, it’s not supporterd in the
12+
default package, an implementation using [tls-channel](https://github.com/marianobarrios/tls-channel) is available.
13+
314
## v0.7.0 (2025-11-14)
415

516
### Added

jeromq-core/src/main/java/org/zeromq/ZMQ.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.nio.channels.SelectableChannel;
88
import java.nio.channels.Selector;
99
import java.nio.charset.Charset;
10+
import java.time.Duration;
1011
import java.util.ArrayList;
1112
import java.util.List;
1213
import java.util.concurrent.ThreadLocalRandom;
@@ -15,6 +16,9 @@
1516
import java.util.function.BiFunction;
1617
import java.util.function.Consumer;
1718

19+
import javax.net.ssl.SSLContext;
20+
import javax.net.ssl.SSLParameters;
21+
1822
import org.zeromq.proto.ZPicture;
1923

2024
import zmq.Ctx;
@@ -28,6 +32,7 @@
2832
import zmq.io.mechanism.Mechanisms;
2933
import zmq.io.net.SocketFactory.ChannelFactoryWrapper;
3034
import zmq.io.net.SelectorProviderChooser;
35+
import zmq.io.net.tls.PrincipalConverter;
3136
import zmq.msg.MsgAllocator;
3237
import zmq.util.Draft;
3338
import zmq.util.Z85;
@@ -3103,6 +3108,69 @@ public ChannelFactoryWrapper<? extends SocketAddress> getChannelWrapper()
31033108
return base.getSocketOptx(zmq.ZMQ.ZMQ_CHANNEL_WRAPPER_FACTORY);
31043109
}
31053110

3111+
/**
3112+
* When using the tls {@link zmq.io.net.NetProtocol}, it will set the {@link SSLContext} to be used.
3113+
* @param sslContext The {@link SSLContext} to used
3114+
*
3115+
* @return true if the option was set, otherwise false
3116+
*/
3117+
public boolean setSslContext(SSLContext sslContext)
3118+
{
3119+
return base.setSocketOpt(zmq.ZMQ.ZMQ_TLS_CONTEXT, sslContext);
3120+
}
3121+
3122+
/**
3123+
* The {@link SSLContext} to use can be defined both in the factory or specific for the current socket using
3124+
* {@link #setSslContext}. This method return the effective one.
3125+
* @return The effective {@link SSLContext}
3126+
*/
3127+
public SSLContext getSslContext()
3128+
{
3129+
return base.getSocketOptx(zmq.ZMQ.ZMQ_TLS_CONTEXT);
3130+
}
3131+
3132+
/**
3133+
* When using the tls {@link zmq.io.net.NetProtocol}, it will set the {@link SSLParameters} to be used.
3134+
* @param sslParams The {@link SSLParameters} to used
3135+
*
3136+
* @return true if the option was set, otherwise false
3137+
*/
3138+
public boolean setSslParameters(SSLParameters sslParams)
3139+
{
3140+
return base.setSocketOpt(zmq.ZMQ.ZMQ_TLS_PARAMETERS, sslParams);
3141+
}
3142+
3143+
/**
3144+
* The {@link SSLContext} to use can be defined both in the factory or specific for the current socket using
3145+
* {@link #setSslParameters}. This method return the effective one.
3146+
* @return The effective {@link SSLParameters}
3147+
*/
3148+
public SSLParameters getSslParameter()
3149+
{
3150+
return base.getSocketOptx(zmq.ZMQ.ZMQ_TLS_PARAMETERS);
3151+
}
3152+
3153+
/**
3154+
* When using the tls {@link zmq.io.net.NetProtocol}, it will set the {@link PrincipalConverter} to be used.
3155+
* @param principalConverter The {@link PrincipalConverter} to be used
3156+
*
3157+
* @return true if the option was set, otherwise false
3158+
*/
3159+
public boolean setPrincipalConvert(PrincipalConverter principalConverter)
3160+
{
3161+
return base.setSocketOpt(zmq.ZMQ.ZMQ_TLS_PRINCIPAL_CONVERT, principalConverter);
3162+
}
3163+
3164+
/**
3165+
* The {@link PrincipalConverter} to use can be defined both in the factory or specific for the current socket using
3166+
* {@link #setPrincipalConvert}. This method return the effective one.
3167+
* @return The effective {@link PrincipalConverter}
3168+
*/
3169+
public PrincipalConverter getPrincipalConverter()
3170+
{
3171+
return base.getSocketOptx(zmq.ZMQ.ZMQ_TLS_PRINCIPAL_CONVERT);
3172+
}
3173+
31063174
/**
31073175
* Bind to network interface. Start listening for new connections.
31083176
*

jeromq-core/src/main/java/zmq/Options.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import java.util.Arrays;
99
import java.util.List;
1010

11+
import javax.net.ssl.SSLContext;
12+
import javax.net.ssl.SSLParameters;
13+
1114
import zmq.io.coder.IDecoder;
1215
import zmq.io.coder.IEncoder;
1316
import zmq.io.mechanism.Mechanisms;
@@ -18,6 +21,7 @@
1821
import zmq.io.net.ipc.IpcAddress;
1922
import zmq.io.net.tcp.TcpAddress;
2023
import zmq.io.net.tcp.TcpAddress.TcpAddressMask;
24+
import zmq.io.net.tls.PrincipalConverter;
2125
import zmq.msg.MsgAllocator;
2226
import zmq.msg.MsgAllocatorThreshold;
2327
import zmq.util.Errno;
@@ -196,6 +200,11 @@ public class Options
196200
private ChannelFactoryWrapper<? extends SocketAddress> channelWrapperFactory = SocketFactory.TRANSPARENT;
197201
private SocketFactory<? extends SocketAddress> effectiveFactory = null;
198202

203+
// Must be kept as null, default values will be handled by the TLS socket factory
204+
public SSLContext sslContext = null;
205+
public SSLParameters sslParameters = null;
206+
public PrincipalConverter principalConverter = null;
207+
199208
/**
200209
* <p>Set an option with the given object.</p>
201210
* <p>When an option expect a duration, it can be given as an integer number of milliseconds, a floating
@@ -580,6 +589,33 @@ else if (optval instanceof MsgAllocator) {
580589
return false;
581590
}
582591

592+
case ZMQ.ZMQ_TLS_CONTEXT:
593+
if (optval instanceof SSLContext) {
594+
sslContext = (SSLContext) optval;
595+
return true;
596+
}
597+
else {
598+
return false;
599+
}
600+
601+
case ZMQ.ZMQ_TLS_PARAMETERS:
602+
if (optval instanceof SSLParameters) {
603+
sslParameters = (SSLParameters) optval;
604+
return true;
605+
}
606+
else {
607+
return false;
608+
}
609+
610+
case ZMQ.ZMQ_TLS_PRINCIPAL_CONVERT:
611+
if (optval instanceof PrincipalConverter) {
612+
this.principalConverter = (PrincipalConverter) optval;
613+
return true;
614+
}
615+
else {
616+
return false;
617+
}
618+
583619
default:
584620
throw new IllegalArgumentException("Unknown Option " + option);
585621
}
@@ -836,6 +872,15 @@ public <T> T getSocketOpt(int option)
836872
case ZMQ.ZMQ_CHANNEL_WRAPPER_FACTORY:
837873
return (T) channelWrapperFactory;
838874

875+
case ZMQ.ZMQ_TLS_CONTEXT:
876+
return (T) sslContext;
877+
878+
case ZMQ.ZMQ_TLS_PARAMETERS:
879+
return (T) sslParameters;
880+
881+
case ZMQ.ZMQ_TLS_PRINCIPAL_CONVERT:
882+
return (T) principalConverter;
883+
839884
default:
840885
throw new IllegalArgumentException("option=" + option);
841886
}

jeromq-core/src/main/java/zmq/ZMQ.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ public class ZMQ
156156
public static final int ZMQ_MSG_ALLOCATOR = ZMQ_CUSTOM_OPTION + 3;
157157
public static final int ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD = ZMQ_CUSTOM_OPTION + 4;
158158
public static final int ZMQ_HEARTBEAT_CONTEXT = ZMQ_CUSTOM_OPTION + 5;
159-
public static final int ZMQ_CHANNEL_WRAPPER_FACTORY = ZMQ_CUSTOM_OPTION + 6;
159+
public static final int ZMQ_CHANNEL_WRAPPER_FACTORY = ZMQ_CUSTOM_OPTION + 7;
160+
public static final int ZMQ_TLS_CONTEXT = ZMQ_CUSTOM_OPTION + 8;
161+
public static final int ZMQ_TLS_PARAMETERS = ZMQ_CUSTOM_OPTION + 9;
162+
public static final int ZMQ_TLS_PRINCIPAL_CONVERT = ZMQ_CUSTOM_OPTION + 10;
160163

161164
/* Message options */
162165
public static final int ZMQ_MORE = 1;

jeromq-core/src/main/java/zmq/io/net/NetProtocol.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ public <S extends SocketAddress> void resolve(Address<S> paddr, boolean ipv6)
3232
paddr.resolve(ipv6);
3333
}
3434
},
35+
tls(false, false)
36+
{
37+
@Override
38+
public <S extends SocketAddress> void resolve(Address<S> paddr, boolean ipv6)
39+
{
40+
paddr.resolve(ipv6);
41+
}
42+
},
3543
udp(true, true)
3644
{
3745
@Override
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package zmq.io.net.tls;
2+
3+
import java.util.Optional;
4+
5+
import javax.net.ssl.SSLSession;
6+
7+
/**
8+
* This class will be used to extract an identity from the current {@link SSLSession} and it will be stored in the "User-Id"
9+
* metadata attribute.
10+
*/
11+
@FunctionalInterface
12+
public interface PrincipalConverter {
13+
Optional<String> getPrincipal(SSLSession session);
14+
}

jeromq-tls/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
An implementation of a TLS protocol using [TLS Channel](https://github.com/marianobarrios/tls-channel)
2+
3+
It can be used as simple drop-in, and it will be used automatically. For better control, it can be used as channel wrapper
4+
for better control using and configuring the factory zmq.io.net.tls.TlsSocketFactory directly.

jeromq-tls/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<dependency>
3232
<groupId>com.github.marianobarrios</groupId>
3333
<artifactId>tls-channel</artifactId>
34-
<version>0.9.0</version>
34+
<version>0.9.1</version>
3535
</dependency>
3636
<dependency>
3737
<groupId>org.bouncycastle</groupId>

0 commit comments

Comments
 (0)