diff --git a/bramble-core/build.gradle b/bramble-core/build.gradle
index 63162934221bc3bbed27d3399f97131217b39128..f8bbfcf5ac1c95979ff8e30efc86f1f5c8a0de8a 100644
--- a/bramble-core/build.gradle
+++ b/bramble-core/build.gradle
@@ -17,6 +17,7 @@ dependencies {
 	implementation 'net.i2p.crypto:eddsa:0.2.0'
 	implementation 'org.whispersystems:curve25519-java:0.5.0'
 	implementation 'org.briarproject:jtorctl:0.5'
+	implementation 'org.briarproject:socks-socket:0.1'
 
 	//noinspection GradleDependency
 	implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksModule.java b/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksModule.java
index 5d4d7939c9c5fa27cd0d2a433305ef5586680150..420739e8aaffee7cabe0984585d9f8ad6444d320 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksModule.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksModule.java
@@ -1,6 +1,7 @@
 package org.briarproject.bramble.socks;
 
 import org.briarproject.bramble.api.plugin.TorSocksPort;
+import org.briarproject.socks.SocksSocketFactory;
 
 import java.net.InetSocketAddress;
 
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksSocket.java b/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksSocket.java
deleted file mode 100644
index e7b15786ee3adba824227d00c8f3c3193f015ca7..0000000000000000000000000000000000000000
--- a/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksSocket.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package org.briarproject.bramble.socks;
-
-import org.briarproject.bramble.util.ByteUtils;
-import org.briarproject.bramble.util.IoUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.util.Arrays;
-
-class SocksSocket extends Socket {
-
-	private static final String[] ERRORS = {
-			"Succeeded",
-			"General SOCKS server failure",
-			"Connection not allowed by ruleset",
-			"Network unreachable",
-			"Host unreachable",
-			"Connection refused",
-			"TTL expired",
-			"Command not supported",
-			"Address type not supported"
-	};
-
-	@SuppressWarnings("MismatchedReadAndWriteOfArray")
-	private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
-
-	private final SocketAddress proxy;
-	private final int connectToProxyTimeout;
-	private final int extraConnectTimeout, extraSocketTimeout;
-
-	SocksSocket(SocketAddress proxy, int connectToProxyTimeout,
-			int extraConnectTimeout, int extraSocketTimeout) {
-		this.proxy = proxy;
-		this.connectToProxyTimeout = connectToProxyTimeout;
-		this.extraConnectTimeout = extraConnectTimeout;
-		this.extraSocketTimeout = extraSocketTimeout;
-	}
-
-	@Override
-	public void connect(SocketAddress endpoint, int timeout)
-			throws IOException {
-
-		// Validate the endpoint
-		if (!(endpoint instanceof InetSocketAddress))
-			throw new IllegalArgumentException();
-		InetSocketAddress inet = (InetSocketAddress) endpoint;
-		InetAddress address = inet.getAddress();
-		if (address != null
-				&& !Arrays.equals(address.getAddress(), UNSPECIFIED_ADDRESS)) {
-			throw new IllegalArgumentException();
-		}
-		String host = inet.getHostName();
-		if (host.length() > 255) throw new IllegalArgumentException();
-		int port = inet.getPort();
-
-		// Connect to the proxy
-		super.connect(proxy, connectToProxyTimeout);
-		OutputStream out = IoUtils.getOutputStream(this);
-		InputStream in = IoUtils.getInputStream(this);
-
-		// Request SOCKS 5 with no authentication
-		sendMethodRequest(out);
-		receiveMethodResponse(in);
-
-		// Use the supplied timeout temporarily, plus any configured extra
-		int oldTimeout = getSoTimeout();
-		setSoTimeout(timeout + extraConnectTimeout);
-
-		// Connect to the endpoint via the proxy
-		sendConnectRequest(out, host, port);
-		receiveConnectResponse(in);
-
-		// Restore the old timeout, plus any configured extra
-		setSoTimeout(oldTimeout + extraSocketTimeout);
-	}
-
-	private void sendMethodRequest(OutputStream out) throws IOException {
-		byte[] methodRequest = new byte[] {
-				5, // SOCKS version is 5
-				1, // Number of methods is 1
-				0  // Method is 0, no authentication
-		};
-		out.write(methodRequest);
-		out.flush();
-	}
-
-	private void receiveMethodResponse(InputStream in) throws IOException {
-		byte[] methodResponse = new byte[2];
-		IoUtils.read(in, methodResponse);
-		byte version = methodResponse[0];
-		byte method = methodResponse[1];
-		if (version != 5)
-			throw new IOException("Unsupported SOCKS version: " + version);
-		if (method == (byte) 255)
-			throw new IOException("Proxy requires authentication");
-		if (method != 0)
-			throw new IOException("Unsupported auth method: " + method);
-	}
-
-	private void sendConnectRequest(OutputStream out, String host, int port)
-			throws IOException {
-		byte[] connectRequest = new byte[7 + host.length()];
-		connectRequest[0] = 5; // SOCKS version is 5
-		connectRequest[1] = 1; // Command is 1, connect
-		connectRequest[3] = 3; // Address type is 3, domain name
-		connectRequest[4] = (byte) host.length(); // Length of domain name
-		for (int i = 0; i < host.length(); i++)
-			connectRequest[5 + i] = (byte) host.charAt(i);
-		ByteUtils.writeUint16(port, connectRequest, connectRequest.length - 2);
-		out.write(connectRequest);
-		out.flush();
-	}
-
-	private void receiveConnectResponse(InputStream in) throws IOException {
-		byte[] connectResponse = new byte[4];
-		IoUtils.read(in, connectResponse);
-		int version = connectResponse[0] & 0xFF;
-		int reply = connectResponse[1] & 0xFF;
-		int addressType = connectResponse[3] & 0xFF;
-		if (version != 5)
-			throw new IOException("Unsupported SOCKS version: " + version);
-		if (reply != 0) {
-			if (reply < ERRORS.length)
-				throw new IOException("Connection failed: " + ERRORS[reply]);
-			else throw new IOException("Connection failed: " + reply);
-		}
-		if (addressType == 1) IoUtils.read(in, new byte[4]); // IPv4
-		else if (addressType == 4) IoUtils.read(in, new byte[16]); // IPv6
-		else throw new IOException("Unsupported address type: " + addressType);
-		IoUtils.read(in, new byte[2]); // Port number
-	}
-}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksSocketFactory.java b/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksSocketFactory.java
deleted file mode 100644
index 6f1edc8a0760f9ec09a0320598ebae8c535de848..0000000000000000000000000000000000000000
--- a/bramble-core/src/main/java/org/briarproject/bramble/socks/SocksSocketFactory.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.briarproject.bramble.socks;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-
-import javax.net.SocketFactory;
-
-class SocksSocketFactory extends SocketFactory {
-
-	private final SocketAddress proxy;
-	private final int connectToProxyTimeout;
-	private final int extraConnectTimeout, extraSocketTimeout;
-
-	SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout,
-			int extraConnectTimeout, int extraSocketTimeout) {
-		this.proxy = proxy;
-		this.connectToProxyTimeout = connectToProxyTimeout;
-		this.extraConnectTimeout = extraConnectTimeout;
-		this.extraSocketTimeout = extraSocketTimeout;
-	}
-
-	@Override
-	public Socket createSocket() {
-		return new SocksSocket(proxy, connectToProxyTimeout,
-				extraConnectTimeout, extraSocketTimeout);
-	}
-
-	@Override
-	public Socket createSocket(String host, int port) throws IOException {
-		Socket socket = createSocket();
-		socket.connect(InetSocketAddress.createUnresolved(host, port));
-		return socket;
-	}
-
-	@Override
-	public Socket createSocket(InetAddress host, int port) {
-		throw new UnsupportedOperationException();
-	}
-
-	@Override
-	public Socket createSocket(String host, int port, InetAddress localHost,
-			int localPort) {
-		throw new UnsupportedOperationException();
-	}
-
-	@Override
-	public Socket createSocket(InetAddress address, int port,
-			InetAddress localAddress, int localPort) throws IOException {
-		throw new UnsupportedOperationException();
-	}
-}
diff --git a/bramble-core/witness.gradle b/bramble-core/witness.gradle
index f1af7ac8fee094f42ba112e739b068649de39c41..99b4a7033052ae3bfe931ee1e5b184caf6f099e5 100644
--- a/bramble-core/witness.gradle
+++ b/bramble-core/witness.gradle
@@ -36,6 +36,8 @@ dependencyVerification {
         'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
         'org.bouncycastle:bcprov-jdk15to18:1.70:bcprov-jdk15to18-1.70.jar:7df4c54f29ce2dd616dc3b198ca4db3dfcc79e3cb397c084a0aff97b85c0bf38',
         'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
+        'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
+        'org.briarproject:socks-socket:0.1:socks-socket-0.1.jar:e5898822d10f5390363c5dddb945891648c92cf93ba50709e07f0d173ec0eb4b',
         'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
         'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
         'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',