Skip to content

Commit 94bd7df

Browse files
authored
Merge pull request TooTallNate#839 from BroHammie/sslEngineWebsocketFactory
SSLEngineWebSocketServerFactory allows more customization
2 parents d06b640 + a3dc144 commit 94bd7df

File tree

4 files changed

+291
-1
lines changed

4 files changed

+291
-1
lines changed

src/main/example/SSLClientExample.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ public static void main( String[] args ) throws Exception {
111111
while ( true ) {
112112
String line = reader.readLine();
113113
if( line.equals( "close" ) ) {
114-
chatclient.close();
114+
chatclient.closeBlocking();
115+
} else if ( line.equals( "open" ) ) {
116+
chatclient.reconnect();
115117
} else {
116118
chatclient.send( line );
117119
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
2+
3+
/*
4+
* Copyright (c) 2010-2019 Nathan Rajlich
5+
*
6+
* Permission is hereby granted, free of charge, to any person
7+
* obtaining a copy of this software and associated documentation
8+
* files (the "Software"), to deal in the Software without
9+
* restriction, including without limitation the rights to use,
10+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the
12+
* Software is furnished to do so, subject to the following
13+
* conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be
16+
* included in all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25+
* OTHER DEALINGS IN THE SOFTWARE.
26+
*/
27+
28+
import org.java_websocket.server.SSLParametersWebSocketServerFactory;
29+
30+
import javax.net.ssl.KeyManagerFactory;
31+
import javax.net.ssl.SSLContext;
32+
import javax.net.ssl.SSLParameters;
33+
import javax.net.ssl.TrustManagerFactory;
34+
import java.io.File;
35+
import java.io.FileInputStream;
36+
import java.security.KeyStore;
37+
38+
/**
39+
* Copy of SSLServerExample except we use @link SSLEngineWebSocketServerFactory to customize clientMode/ClientAuth to force client to present a cert.
40+
* Example of Two-way ssl/MutualAuthentication/ClientAuthentication
41+
*/
42+
public class TwoWaySSLServerExample {
43+
44+
/*
45+
* Keystore with certificate created like so (in JKS format):
46+
*
47+
*keytool -genkey -keyalg RSA -validity 3650 -keystore "keystore.jks" -storepass "storepassword" -keypass "keypassword" -alias "default" -dname "CN=127.0.0.1, OU=MyOrgUnit, O=MyOrg, L=MyCity, S=MyRegion, C=MyCountry"
48+
*/
49+
public static void main( String[] args ) throws Exception {
50+
ChatServer chatserver = new ChatServer( 8887 ); // Firefox does allow multible ssl connection only via port 443 //tested on FF16
51+
52+
// load up the key store
53+
String STORETYPE = "JKS";
54+
String KEYSTORE = "keystore.jks";
55+
String STOREPASSWORD = "storepassword";
56+
String KEYPASSWORD = "keypassword";
57+
58+
KeyStore ks = KeyStore.getInstance( STORETYPE );
59+
File kf = new File( KEYSTORE );
60+
ks.load( new FileInputStream( kf ), STOREPASSWORD.toCharArray() );
61+
62+
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
63+
kmf.init( ks, KEYPASSWORD.toCharArray() );
64+
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
65+
tmf.init( ks );
66+
67+
SSLContext sslContext = SSLContext.getInstance( "TLS" );
68+
sslContext.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );
69+
70+
SSLParameters sslParameters = new SSLParameters();
71+
// This is all we need
72+
sslParameters.setNeedClientAuth(true);
73+
chatserver.setWebSocketFactory( new SSLParametersWebSocketServerFactory(sslContext, sslParameters));
74+
75+
chatserver.start();
76+
77+
}
78+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2010-2019 Nathan Rajlich
3+
*
4+
* Permission is hereby granted, free of charge, to any person
5+
* obtaining a copy of this software and associated documentation
6+
* files (the "Software"), to deal in the Software without
7+
* restriction, including without limitation the rights to use,
8+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the
10+
* Software is furnished to do so, subject to the following
11+
* conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23+
* OTHER DEALINGS IN THE SOFTWARE.
24+
*/
25+
26+
package org.java_websocket.server;
27+
28+
import org.java_websocket.SSLSocketChannel2;
29+
30+
import javax.net.ssl.SSLContext;
31+
import javax.net.ssl.SSLEngine;
32+
import javax.net.ssl.SSLParameters;
33+
import java.io.IOException;
34+
import java.nio.channels.ByteChannel;
35+
import java.nio.channels.SelectionKey;
36+
import java.nio.channels.SocketChannel;
37+
import java.util.concurrent.ExecutorService;
38+
import java.util.concurrent.Executors;
39+
40+
/**
41+
* WebSocketFactory that can be configured to only support specific protocols and cipher suites.
42+
*/
43+
public class SSLParametersWebSocketServerFactory extends DefaultSSLWebSocketServerFactory {
44+
45+
private final SSLParameters sslParameters;
46+
47+
/**
48+
* New CustomSSLWebSocketServerFactory configured to only support given protocols and given cipher suites.
49+
*
50+
* @param sslContext - can not be <code>null</code>
51+
* @param sslParameters - can not be <code>null</code>
52+
*/
53+
public SSLParametersWebSocketServerFactory(SSLContext sslContext, SSLParameters sslParameters) {
54+
this(sslContext, Executors.newSingleThreadScheduledExecutor(), sslParameters);
55+
}
56+
57+
/**
58+
* New CustomSSLWebSocketServerFactory configured to only support given protocols and given cipher suites.
59+
*
60+
* @param sslContext - can not be <code>null</code>
61+
* @param executerService - can not be <code>null</code>
62+
* @param sslParameters - can not be <code>null</code>
63+
*/
64+
public SSLParametersWebSocketServerFactory(SSLContext sslContext, ExecutorService executerService, SSLParameters sslParameters) {
65+
super(sslContext, executerService);
66+
if (sslParameters == null) {
67+
throw new IllegalArgumentException();
68+
}
69+
this.sslParameters = sslParameters;
70+
}
71+
72+
@Override
73+
public ByteChannel wrapChannel(SocketChannel channel, SelectionKey key) throws IOException {
74+
SSLEngine e = sslcontext.createSSLEngine();
75+
e.setUseClientMode(false);
76+
e.setSSLParameters(sslParameters);
77+
return new SSLSocketChannel2(channel, e, exec, key);
78+
}
79+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package org.java_websocket.server;
2+
3+
import org.java_websocket.WebSocket;
4+
import org.java_websocket.WebSocketAdapter;
5+
import org.java_websocket.WebSocketImpl;
6+
import org.java_websocket.drafts.Draft;
7+
import org.java_websocket.drafts.Draft_6455;
8+
import org.java_websocket.handshake.Handshakedata;
9+
import org.junit.Test;
10+
11+
import javax.net.ssl.SSLContext;
12+
import javax.net.ssl.SSLParameters;
13+
import java.io.IOException;
14+
import java.net.InetSocketAddress;
15+
import java.nio.ByteBuffer;
16+
import java.nio.channels.ByteChannel;
17+
import java.nio.channels.NotYetConnectedException;
18+
import java.nio.channels.SocketChannel;
19+
import java.security.NoSuchAlgorithmException;
20+
import java.util.Collections;
21+
import java.util.concurrent.Executors;
22+
23+
import static org.junit.Assert.assertNotNull;
24+
import static org.junit.Assert.fail;
25+
26+
public class SSLParametersWebSocketServerFactoryTest {
27+
28+
@Test
29+
public void testConstructor() throws NoSuchAlgorithmException {
30+
try {
31+
new SSLParametersWebSocketServerFactory(null, null);
32+
fail("IllegalArgumentException should be thrown");
33+
} catch (IllegalArgumentException e) {
34+
// Good
35+
}
36+
try {
37+
new SSLParametersWebSocketServerFactory(SSLContext.getDefault(), null);
38+
fail("IllegalArgumentException should be thrown");
39+
} catch (IllegalArgumentException e) {
40+
}
41+
try {
42+
new SSLParametersWebSocketServerFactory(SSLContext.getDefault(), new SSLParameters());
43+
} catch (IllegalArgumentException e) {
44+
fail("IllegalArgumentException should not be thrown");
45+
}
46+
try {
47+
new SSLParametersWebSocketServerFactory(SSLContext.getDefault(), Executors.newCachedThreadPool(), new SSLParameters());
48+
} catch (IllegalArgumentException e) {
49+
fail("IllegalArgumentException should not be thrown");
50+
}
51+
}
52+
@Test
53+
public void testCreateWebSocket() throws NoSuchAlgorithmException {
54+
SSLParametersWebSocketServerFactory webSocketServerFactory = new SSLParametersWebSocketServerFactory(SSLContext.getDefault(), new SSLParameters());
55+
CustomWebSocketAdapter webSocketAdapter = new CustomWebSocketAdapter();
56+
WebSocketImpl webSocketImpl = webSocketServerFactory.createWebSocket(webSocketAdapter, new Draft_6455());
57+
assertNotNull("webSocketImpl != null", webSocketImpl);
58+
webSocketImpl = webSocketServerFactory.createWebSocket(webSocketAdapter, Collections.<Draft>singletonList(new Draft_6455()));
59+
assertNotNull("webSocketImpl != null", webSocketImpl);
60+
}
61+
62+
@Test
63+
public void testWrapChannel() throws IOException, NoSuchAlgorithmException {
64+
SSLParametersWebSocketServerFactory webSocketServerFactory = new SSLParametersWebSocketServerFactory(SSLContext.getDefault(), new SSLParameters());
65+
SocketChannel channel = SocketChannel.open();
66+
try {
67+
ByteChannel result = webSocketServerFactory.wrapChannel(channel, null);
68+
} catch (NotYetConnectedException e) {
69+
//We do not really connect
70+
}
71+
channel.close();
72+
}
73+
74+
@Test
75+
public void testClose() {
76+
DefaultWebSocketServerFactory webSocketServerFactory = new DefaultWebSocketServerFactory();
77+
webSocketServerFactory.close();
78+
}
79+
80+
private static class CustomWebSocketAdapter extends WebSocketAdapter {
81+
@Override
82+
public void onWebsocketMessage(WebSocket conn, String message) {
83+
84+
}
85+
86+
@Override
87+
public void onWebsocketMessage(WebSocket conn, ByteBuffer blob) {
88+
89+
}
90+
91+
@Override
92+
public void onWebsocketOpen(WebSocket conn, Handshakedata d) {
93+
94+
}
95+
96+
@Override
97+
public void onWebsocketClose(WebSocket ws, int code, String reason, boolean remote) {
98+
99+
}
100+
101+
@Override
102+
public void onWebsocketClosing(WebSocket ws, int code, String reason, boolean remote) {
103+
104+
}
105+
106+
@Override
107+
public void onWebsocketCloseInitiated(WebSocket ws, int code, String reason) {
108+
109+
}
110+
111+
@Override
112+
public void onWebsocketError(WebSocket conn, Exception ex) {
113+
114+
}
115+
116+
@Override
117+
public void onWriteDemand(WebSocket conn) {
118+
119+
}
120+
121+
@Override
122+
public InetSocketAddress getLocalSocketAddress(WebSocket conn) {
123+
return null;
124+
}
125+
126+
@Override
127+
public InetSocketAddress getRemoteSocketAddress(WebSocket conn) {
128+
return null;
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)