Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Skip certificate validation for tor connections #242

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/src/main/java/zapsolutions/zap/HomeActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import zapsolutions.zap.baseClasses.BaseAppCompatActivity;
import zapsolutions.zap.channelManagement.ManageChannelsActivity;
import zapsolutions.zap.connection.RemoteConfiguration;
import zapsolutions.zap.connection.establishConnectionToLnd.LndConnection;
import zapsolutions.zap.connection.lndConnection.LndConnection;
import zapsolutions.zap.connection.internetConnectionStatus.NetworkChangeReceiver;
import zapsolutions.zap.connection.manageWalletConfigs.WalletConfigsManager;
import zapsolutions.zap.customView.CustomViewPager;
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package zapsolutions.zap.connection.lndConnection;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

/**
* This HostnameVerifier trust all host names. No verification will take place.
* In our context we only use it for tor connections and in debug builds to simplify the regtest setup.
*/
public class BlindHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package zapsolutions.zap.connection.lndConnection;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

/**
* This TrustManager trust ALL certificates. No validation takes place.
* In our context we use it only for tor connections.
*/
public class BlindTrustManager implements X509TrustManager {

public X509Certificate[] getAcceptedIssuers() {
return null;
}

public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {

}

public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {

}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package zapsolutions.zap.connection.establishConnectionToLnd;
package zapsolutions.zap.connection.lndConnection;


import com.google.common.io.BaseEncoding;

import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;

import io.grpc.ManagedChannel;
import io.grpc.okhttp.OkHttpChannelBuilder;
Expand Down Expand Up @@ -42,7 +39,6 @@ public class LndConnection {

private static LndConnection mLndConnectionInstance;

private SSLSocketFactory mSSLFactory;
private MacaroonCallCredential mMacaroon;
private ManagedChannel mSecureChannel;
private LndAutopilotService mLndAutopilotService;
Expand Down Expand Up @@ -111,50 +107,26 @@ private void readSavedConnectionInfo() {

// Generate Macaroon
mMacaroon = new MacaroonCallCredential(mConnectionConfig.getMacaroon());

mSSLFactory = null;

// Generate certificate if one was supplied
if (mConnectionConfig.getCert() != null) {
// We have a certificate, try to load it.

String certificateBase64UrlString = mConnectionConfig.getCert();
byte[] certificateBytes = BaseEncoding.base64Url().decode(certificateBase64UrlString);

try {
mSSLFactory = CustomSSLSocketFactory.create(certificateBytes);
} catch (RuntimeException e) {
ZapLog.e(LOG_TAG, "Error creating certificate");
}

}
}

private void generateChannelAndStubs() {
String host = mConnectionConfig.getHost();
int port = mConnectionConfig.getPort();

HostnameVerifier hostnameVerifier = null; // null = default hostnameVerifier
if (BuildConfig.BUILD_TYPE.equals("debug")) {
HostnameVerifier hostnameVerifier = null;
if (BuildConfig.BUILD_TYPE.equals("debug") || mConnectionConfig.isTor()) {
// Disable hostname verification on debug build variant. This is is used to prevent connection errors to REGTEST nodes.
hostnameVerifier = new HostnameVerifierAllowAll();
// On Tor we do not need it, as tor already makes sure we are connected with the correct host.
hostnameVerifier = new BlindHostnameVerifier();
}

// Channels are expensive to create. We want to create it once and then reuse it on all our requests.
if (mSSLFactory == null) {
// BTCPay
mSecureChannel = OkHttpChannelBuilder
.forAddress(host, port)
.hostnameVerifier(hostnameVerifier)
.build();

} else {
mSecureChannel = OkHttpChannelBuilder
.forAddress(host, port)
.hostnameVerifier(hostnameVerifier)
.sslSocketFactory(mSSLFactory)
.build();
}
mSecureChannel = OkHttpChannelBuilder
.forAddress(host, port)
.hostnameVerifier(hostnameVerifier) // null = default hostnameVerifier
.sslSocketFactory(LndSSLSocketFactory.create(mConnectionConfig)) // null = default SSLSocketFactory
.build();


mLndAutopilotService = new RemoteLndAutopilotService(mSecureChannel, mMacaroon);
mLndChainNotifierService = new RemoteLndChainNotifierService(mSecureChannel, mMacaroon);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package zapsolutions.zap.connection.lndConnection;

import com.google.common.io.BaseEncoding;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import zapsolutions.zap.connection.manageWalletConfigs.WalletConfig;
import zapsolutions.zap.util.ZapLog;

/**
* Creates an SSLSocketFactory instance for use with a self signed Certificate,
* which would otherwise be considered "not trustworthy".
* This can be fed into HttpsURLConnection, as well as networking libraries such as OkHttp's OkHttpClient.
*/
public class LndSSLSocketFactory {

private static final String LOG_TAG = LndSSLSocketFactory.class.getName();

private LndSSLSocketFactory() {
throw new AssertionError();
}

public static SSLSocketFactory create(WalletConfig walletConfig) {
SSLContext sslCtx = null;

try {
sslCtx = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}

if (walletConfig.isTor()) {
// Always trust the certificate on Tor connection
try {
sslCtx.init(null, new TrustManager[]{new BlindTrustManager()}, null);
} catch (KeyManagementException e) {
e.printStackTrace();
return null;
}
return sslCtx.getSocketFactory();

} else {
// On clearnet we want to validate the certificate.
if (walletConfig.getCert() != null && !walletConfig.getCert().isEmpty()) {
//try to create a trustmanager that trust the certificate that was transmitted with the lndconnect string.
try {
InputStream caInput = null;
String certificateBase64UrlString = walletConfig.getCert();
byte[] certificateBytes = BaseEncoding.base64Url().decode(certificateBase64UrlString);

// Generate the CA Certificate from the supplied byte array
caInput = new ByteArrayInputStream(certificateBytes);
Certificate ca = CertificateFactory.getInstance("X.509").generateCertificate(caInput);

// Load the key store using the CA
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

// Initialize the TrustManager with this CA
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);

// Create an SSL context that uses the created trust manager
sslCtx.init(null, tmf.getTrustManagers(), new SecureRandom());
return sslCtx.getSocketFactory();

} catch (Exception e) {
ZapLog.e(LOG_TAG, "Error while initializing self signed certificate.");
e.printStackTrace();
}
}
}

// If the above failed, use the default TrustManager which is used when set to null
// This will be the case for btc pay for example as no self signed certificates are used
try {
sslCtx.init(null, null, new SecureRandom());
} catch (KeyManagementException e) {
e.printStackTrace();
return null;
}
return sslCtx.getSocketFactory();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package zapsolutions.zap.connection.establishConnectionToLnd;
package zapsolutions.zap.connection.lndConnection;

import java.util.concurrent.Executor;

Expand Down Expand Up @@ -30,7 +30,6 @@ public void applyRequestMetadata(
final MetadataApplier metadataApplier
) {
String authority = requestInfo.getAuthority();
// System.out.println(authority);
executor.execute(new Runnable() {
public void run() {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ public WalletConfig (String id) {
this.id = id;
}

public boolean isTor() {
if (getHost() == null) {
return false;
}
return getHost().toLowerCase().endsWith(".onion");
}

@Override
public int compareTo(WalletConfig walletConfig) {
WalletConfig other = walletConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import com.google.common.io.BaseEncoding;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;

import zapsolutions.zap.connection.establishConnectionToLnd.CustomSSLSocketFactory;
import zapsolutions.zap.connection.parseConnectionData.BaseConnectionParser;
import zapsolutions.zap.util.ZapLog;

Expand Down Expand Up @@ -81,10 +85,13 @@ public LndConnectStringParser parse() {
try {
byte[] certificateBytes = BaseEncoding.base64Url().decode(cert);
try {
CustomSSLSocketFactory.create(certificateBytes);
} catch (RuntimeException e) {

ZapLog.e(LOG_TAG, "certificate creation failed");
// Generate the CA Certificate from the supplied byte array
InputStream caInput = null;
caInput = new ByteArrayInputStream(certificateBytes);
Certificate ca = CertificateFactory.getInstance("X.509").generateCertificate(caInput);
} catch (CertificateException e) {
e.printStackTrace();
ZapLog.e(LOG_TAG, "certificate validation failed");
mError = ERROR_INVALID_CERTIFICATE;
return this;
}
Expand Down Expand Up @@ -113,7 +120,7 @@ public LndConnectStringParser parse() {
}
}

// everything is ok, initiate connection
// everything is ok
LndConnectConfig lndConnectConfig = new LndConnectConfig();
lndConnectConfig.setHost(connectURI.getHost());
lndConnectConfig.setPort(connectURI.getPort());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
import zapsolutions.zap.GeneratedRequestActivity;
import zapsolutions.zap.R;
import zapsolutions.zap.channelManagement.ManageChannelsActivity;
import zapsolutions.zap.connection.establishConnectionToLnd.LndConnection;
import zapsolutions.zap.connection.lndConnection.LndConnection;
import zapsolutions.zap.connection.manageWalletConfigs.WalletConfigsManager;
import zapsolutions.zap.customView.NumpadView;
import zapsolutions.zap.util.HelpDialogUtil;
Expand Down
Loading