Reputation: 2681
I want to implement a DTLS 1.0 client in Java and after googling a bit I found that the JSSERefGuide says the following:
The JSSE API is capable of supporting SSL versions 2.0 and 3.0 and TLS version 1.0. These security protocols encapsulate a normal bidirectional stream socket, and the JSSE API adds transparent support for authentication, encryption, and integrity protection. The JSSE implementation shipped with the JDK supports SSL 3.0, TLS (1.0, 1.1, and 1.2) and DTLS (version 1.0 and 1.2). It does not implement SSL 2.0.
So I thought I could implement it in pure Java without using any library (e.g. BouncyCastle)
But when I try running (and a few other, like DTLSv1.2, DTLSv1...):
final SSLContext sslContext = SSLContext.getInstance("DTLSv1.0", "SunJSSE");
It throws:
Exception in thread "main" java.security.NoSuchAlgorithmException: no such algorithm: DTLSv1.0 for provider SunJSSE
at sun.security.jca.GetInstance.getService(GetInstance.java:87)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
at javax.net.ssl.SSLContext.getInstance(SSLContext.java:199)
while for example the following works:
final SSLContext sslContext = SSLContext.getInstance("TLSv1.2", "SunJSSE");
Listing all Security Providers I find no DTLS stuff at all.
So is there actually a DTLS implementation? And if so how are you supposed to use it?
Upvotes: 4
Views: 6587
Reputation: 141
Another option is to use DTLS from native library like mbedtls. Here is a library that exposes kotlin (java compatible) API: https://github.com/open-coap/kotlin-mbedtls.
Upvotes: 1
Reputation: 24447
Bases on the sample from JDK and many try and error I have written the follow code which works in my use case:
public static final int MAXIMUM_PACKET_SIZE = 1500;
private final DatagramSocket socket;
private final SSLEngine engine;
public DtlsSocket( @Nonnull DatagramSocket socket, KeyManager km, TrustManager tm, boolean isClient ) {
try {
this.socket = socket;
SSLContext sslCtx = SSLContext.getInstance( "DTLS" );
KeyManager[] kms = { km };
TrustManager[] tms = { tm };
sslCtx.init( kms, tms, null );
engine = sslCtx.createSSLEngine();
engine.setNeedClientAuth( true );
engine.setUseClientMode( isClient );
SSLParameters paras = engine.getSSLParameters();
paras.setMaximumPacketSize( MAXIMUM_PACKET_SIZE );
engine.setSSLParameters( paras );
engine.beginHandshake();
byte[] receiveBytes = new byte[MAXIMUM_PACKET_SIZE];
DatagramPacket receivePacket = new DatagramPacket( receiveBytes, 0, MAXIMUM_PACKET_SIZE );
ByteBuffer receiveBuffer = ByteBuffer.wrap( receiveBytes );
receiveBuffer.limit( 0 );
ByteBuffer emptyBuffer = ByteBuffer.allocate( 0 );
byte[] sendData = new byte[32768];
DatagramPacket sendPacket = new DatagramPacket( sendData, 0, 32768 );
ByteBuffer sendBuffer = ByteBuffer.wrap( sendData );
while( true ) {
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
switch( hs ) {
case NEED_UNWRAP:
case NEED_UNWRAP_AGAIN:
if( !receiveBuffer.hasRemaining() && hs != SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN ) {
socket.receive( receivePacket );
receiveBuffer.position( 0 );
receiveBuffer.limit( receivePacket.getLength() );
}
checkStatus( engine.unwrap( receiveBuffer, emptyBuffer ) );
break;
case NEED_WRAP:
checkStatus( engine.wrap( emptyBuffer, sendBuffer.clear() ) );
sendPacket.setLength( sendBuffer.position() );
socket.send( sendPacket );
break;
case NEED_TASK:
Runnable runnable;
while( (runnable = engine.getDelegatedTask()) != null ) {
runnable.run();
}
break;
case NOT_HANDSHAKING:
return;
default:
// should never occur
throw new SSLException( "handshake error " + hs );
}
}
} catch( Exception ex ) {
throw ErrorCode.throwAny( ex );
}
}
private void checkStatus( SSLEngineResult engineResult ) throws IOException {
if( engineResult.getStatus() != SSLEngineResult.Status.OK ) {
throw new SSLException( "handshake error " + engineResult.getStatus() + " with handshake state " + engineResult.getHandshakeStatus() );
}
}
Upvotes: 1
Reputation: 171
You can use https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java (or https://github.com/twosigma/OpenJDK/blob/master/test/jdk/javax/net/ssl/DTLS/DTLSOverDatagram.java , its the same)
For the person which killed my previous answer because of the links: Even if the link breaks this is no problem - because at looking at the link you'll see with ease that DTLSOverDatagram is part of the official open-jdk 11 tests - so even if the link vanishes you can easily find other sources.
While these are tests for the DTLS implementation, with little refactoring this can be used as a base for DTLS over (udp-) datagrams. For both client and server - in fact, they are almost the same.
Upvotes: 7
Reputation: 3174
The doc is right and you get an Exception because there is no DTLS protocol : https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
Choosing DTLS comes at the moment of creating the socket, as it will be one of TCP or datagram types. As beginning, it will look like :
DatagramSocket s = new DatagramSocket();
...
final SSLContext sslContext = SSLContext.getInstance("TLSv1.0", "SunJSSE");
sslContext.init(null, yourSSLTrustManager, null);
SSLSocketFactory factory = (SSLSocketFactory)sslContext.getSocketFactory();
SSLSocket daSocket = (SSLSocket) factory.createSocket(s, host, port, false);
Upvotes: 2