4

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?

osundblad
  • 2,675
  • 1
  • 29
  • 34
  • Good quest6ion. DTLS isn't mentioned in the Standard Names page. – user207421 Dec 21 '17 at 09:52
  • I think this is a glitch in the website; the docs packages I downloaded several years ago are different here. The table at 'Support Classes and Interfaces' halfway down the page correctly shows only SSL3, TLS1, 1.1, 1.2 for 8 and 7, and for 6 the TLS1.1 and 1.2 are only as of 6u111 and 6u121 (i.e. on the paid plan or OpenJDK). Ditto the 'standard names' at e.g. https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext et pred. – dave_thompson_085 Dec 21 '17 at 10:07

5 Answers5

6

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.

mifritscher
  • 161
  • 1
  • 3
2

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);
Eugène Adell
  • 3,089
  • 2
  • 18
  • 34
  • Thanks, and a follow up question: To use DTLSv1.0 I assume I should use TLSv1.1? (since there is no DTLS based on TLSv1) – osundblad Dec 21 '17 at 10:09
  • Choosing DTLS comes at the moment of creating the `SSLContext`. – user207421 Dec 21 '17 at 10:10
  • 1
    You're assuming right, RFC 4737 which describes DTLS 1.0 says "Where we do not explicitly call out differences, DTLS is the same as in [TLS11]" – Eugène Adell Dec 21 '17 at 10:24
  • 1
    EJP : no, check again the SSLContext.getInstance(...) methods, it's impossible at this moment. We very seldom see you give the bad answer, thanks for your great work by the way. – Eugène Adell Dec 21 '17 at 10:28
  • 4
    @EugèneAdell the code example you gave does not compile `factory.createSocket` does not accept a `DatagramSocket`. You don't happen to have some code that works? Thanks in advance – osundblad Dec 21 '17 at 15:04
  • I haven't any code but I can take some time to make tests later. Have a look at the deprecated which is still supposed to work https://docs.oracle.com/javase/7/docs/api/java/net/Socket.html#Socket(java.lang.String,%20int,%20boolean) – Eugène Adell Dec 21 '17 at 15:14
  • Note that if you ask for "TLSv1.0" when calling SSLContext.getInstance, SunJSSE will interpret this as the *maximum* protocol version you want to support. You should generally just ask for "TLS" to let it negotiate the most recent version. – Neil Madden Feb 25 '20 at 12:45
  • @NeilMadden is this documented anywhere ? I can't find it. – Eugène Adell Feb 25 '20 at 18:23
  • Not documented as far as I know. I know it from bitter experience. See the source code: http://hg.openjdk.java.net/jdk/jdk/file/61ee15b9a1ac/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java#l638 Each TLSXXContext only enables protocols up to that version by default. – Neil Madden Feb 26 '20 at 11:52
  • @osundbladdid you were able to find a solution? – Paulo Oliveira Aug 23 '21 at 22:16
  • @PauloOliveira didn't see your comment until now. No I went with BouncyCastle instead – osundblad Mar 11 '22 at 15:28
2

DTLS is present in JavaSE 9: SSLContext Algorithm Names

dsh
  • 12,037
  • 3
  • 33
  • 51
  • JEP 219 adds support for DTLS "BUT" according to the JEP does NOT implement the actual transport-specific interfaces (see non-goals #1 of the JEP) (http://openjdk.java.net/jeps/219) So i guess we have to transport the actual data thru a standard DatagramSocket??? – Peter Quiring Mar 30 '18 at 01:56
0

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() );
    }
}
Horcrux7
  • 23,758
  • 21
  • 98
  • 156
0

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.

szymex
  • 131
  • 1
  • 5
  • Yes that is an option but that is not what my question was about . It was about not using any library. And in my case IoT a native lib was completely out of the question. But thanks for the tip. – osundblad Apr 18 '23 at 06:31