Skip to content

Commit

Permalink
Merge pull request #1308 from nextcloud/clientCert
Browse files Browse the repository at this point in the history
Client cert
  • Loading branch information
tobiasKaminsky authored Jan 25, 2024
2 parents 30bcf05 + fe5b7b4 commit 1da2336
Show file tree
Hide file tree
Showing 13 changed files with 1,090 additions and 23 deletions.
10 changes: 9 additions & 1 deletion library/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@
android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- used by AdvancedX509KeyManager to ask user via a notification to select a
TLS client certificate when context is not able to start an Activity. -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

<application
android:usesCleartextTraffic="true"
tools:targetApi="m" />
tools:targetApi="m">

<activity
android:name=".common.network.SelectClientCertificateHelperActivity"
android:exported="false" />
</application>

</manifest>

42 changes: 32 additions & 10 deletions library/src/main/java/com/nextcloud/common/NextcloudClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.owncloud.android.lib.common.OwnCloudClientFactory.DEFAULT_CONNECTION_
import com.owncloud.android.lib.common.OwnCloudClientFactory.DEFAULT_DATA_TIMEOUT_LONG
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.lib.common.accounts.AccountUtils
import com.owncloud.android.lib.common.network.AdvancedX509KeyManager
import com.owncloud.android.lib.common.network.AdvancedX509TrustManager
import com.owncloud.android.lib.common.network.NetworkUtils
import com.owncloud.android.lib.common.network.RedirectionPath
Expand All @@ -56,16 +57,18 @@ import javax.net.ssl.TrustManager
class NextcloudClient private constructor(
val delegate: NextcloudUriDelegate,
var credentials: String,
val client: OkHttpClient
val client: OkHttpClient,
val context: Context
) : NextcloudUriProvider by delegate {
var followRedirects = true

constructor(
baseUri: Uri,
userId: String,
credentials: String,
client: OkHttpClient
) : this(NextcloudUriDelegate(baseUri, userId), credentials, client)
client: OkHttpClient,
context: Context
) : this(NextcloudUriDelegate(baseUri, userId), credentials, client, context)

var userId: String
get() = delegate.userId!!
Expand All @@ -79,10 +82,11 @@ class NextcloudClient private constructor(

private fun createDefaultClient(context: Context): OkHttpClient {
val trustManager = AdvancedX509TrustManager(NetworkUtils.getKnownServersStore(context))
val keyManager = AdvancedX509KeyManager(context)

val sslContext = NetworkUtils.getSSLContext()

sslContext.init(null, arrayOf<TrustManager>(trustManager), null)
sslContext.init(arrayOf(keyManager), arrayOf<TrustManager>(trustManager), null)
val sslSocketFactory = sslContext.socketFactory

var proxy: Proxy? = null
Expand Down Expand Up @@ -116,25 +120,43 @@ class NextcloudClient private constructor(
userId: String,
credentials: String,
context: Context
) : this(baseUri, userId, credentials, createDefaultClient(context))
) : this(baseUri, userId, credentials, createDefaultClient(context), context)

@Suppress("TooGenericExceptionCaught")
fun <T> execute(remoteOperation: RemoteOperation<T>): RemoteOperationResult<T> {
return try {
remoteOperation.run(this)
} catch (ex: Exception) {
RemoteOperationResult(ex)
val result =
try {
remoteOperation.run(this)
} catch (ex: Exception) {
RemoteOperationResult(ex)
}
if (result.httpCode == HttpStatus.SC_BAD_REQUEST) {
val url = remoteOperation.client.hostConfiguration.hostURL
Log_OC.e(TAG, "Received http status 400 for $url -> removing client certificate")
AdvancedX509KeyManager(context).removeKeys(url)
}
return result
}

@Throws(IOException::class)
fun execute(method: OkHttpMethodBase): Int {
return method.execute(this)
val httpStatus = method.execute(this)
if (httpStatus == HttpStatus.SC_BAD_REQUEST) {
val uri = method.uri
Log_OC.e(TAG, "Received http status 400 for $uri -> removing client certificate")
AdvancedX509KeyManager(context).removeKeys(uri)
}
return httpStatus
}

internal fun execute(request: Request): ResponseOrError {
return try {
val response = client.newCall(request).execute()
if (response.code == HttpStatus.SC_BAD_REQUEST) {
val url = request.url
Log_OC.e(TAG, "Received http status 400 for $url -> removing client certificate")
AdvancedX509KeyManager(context).removeKeys(url)
}
ResponseOrError(response)
} catch (ex: IOException) {
ResponseOrError(ex)
Expand Down
4 changes: 3 additions & 1 deletion library/src/main/java/com/nextcloud/common/PlainClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import android.content.Context
import android.text.TextUtils
import com.owncloud.android.lib.common.OwnCloudClientFactory.DEFAULT_DATA_TIMEOUT_LONG
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.lib.common.network.AdvancedX509KeyManager
import com.owncloud.android.lib.common.network.AdvancedX509TrustManager
import com.owncloud.android.lib.common.network.NetworkUtils
import com.owncloud.android.lib.common.utils.Log_OC
Expand All @@ -55,10 +56,11 @@ class PlainClient(context: Context) {

private fun createDefaultClient(context: Context): OkHttpClient {
val trustManager = AdvancedX509TrustManager(NetworkUtils.getKnownServersStore(context))
val keyManager = AdvancedX509KeyManager(context)

val sslContext = NetworkUtils.getSSLContext()

sslContext.init(null, arrayOf<TrustManager>(trustManager), null)
sslContext.init(arrayOf(keyManager), arrayOf<TrustManager>(trustManager), null)
val sslSocketFactory = sslContext.socketFactory

var proxy: Proxy? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@

package com.owncloud.android.lib.common;

import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;

import com.nextcloud.common.DNSCache;
import com.nextcloud.common.NextcloudUriDelegate;
import com.owncloud.android.lib.common.accounts.AccountUtils;
import com.owncloud.android.lib.common.network.AdvancedX509KeyManager;
import com.owncloud.android.lib.common.network.RedirectionPath;
import com.owncloud.android.lib.common.utils.Log_OC;

Expand Down Expand Up @@ -72,15 +74,18 @@ public class OwnCloudClient extends HttpClient {
private OwnCloudCredentials credentials = null;
private int mInstanceNumber;

private AdvancedX509KeyManager keyManager;

/**
* Constructor
*/
public OwnCloudClient(Uri baseUri, HttpConnectionManager connectionMgr) {
public OwnCloudClient(Uri baseUri, HttpConnectionManager connectionMgr, Context context) {
super(connectionMgr);

if (baseUri == null) {
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
}
this.keyManager = new AdvancedX509KeyManager(context);
nextcloudUriDelegate = new NextcloudUriDelegate(baseUri);

mInstanceNumber = sInstanceCounter++;
Expand Down Expand Up @@ -156,7 +161,13 @@ public int executeMethod(HttpMethodBase method, int readTimeout, int connectionT
if (connectionTimeout >= 0) {
getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);
}
return executeMethod(method);
int httpStatus = executeMethod(method);
if (httpStatus == HttpStatus.SC_BAD_REQUEST) {
URI uri = method.getURI();
Log_OC.e(TAG, "Received http status 400 for " + uri + " -> removing client certificate");
keyManager.removeKeys(uri);
}
return httpStatus;
} finally {
getParams().setSoTimeout(oldSoTimeout);
getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout);
Expand Down Expand Up @@ -191,6 +202,10 @@ public int executeMethod(HttpMethod method) throws IOException {

if (status >= 500 && status < 600 && DNSCache.isIPV6First(hostname)) {
return retryMethodWithIPv4(method, hostname);
} else if (status == HttpStatus.SC_BAD_REQUEST) {
URI uri = method.getURI();
Log_OC.e(TAG, "Received http status 400 for " + uri + " -> removing client certificate");
keyManager.removeKeys(uri);
}

if (followRedirects) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public static OwnCloudClient createOwnCloudClient(Uri uri, Context context, bool
Log_OC.e(TAG, "The local server truststore could not be read. Default SSL management" +
" in the system will be used for HTTPS connections", e);
}
OwnCloudClient client = new OwnCloudClient(uri, NetworkUtils.getMultiThreadedConnManager());
OwnCloudClient client = new OwnCloudClient(uri, NetworkUtils.getMultiThreadedConnManager(), context);
client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
client.setFollowRedirects(followRedirects);

Expand Down
Loading

0 comments on commit 1da2336

Please sign in to comment.