PinnedTrustManager differ for API 24+

This commit is contained in:
Benoit Marty 2020-07-01 11:54:03 +02:00
parent f86fa6cb5d
commit b8b79de91c
4 changed files with 217 additions and 17 deletions

View File

@ -175,7 +175,7 @@ internal object CertUtil {
}
}
val trustPinned = arrayOf<TrustManager>(PinnedTrustManager(hsConfig.allowedFingerprints, defaultTrustManager))
val trustPinned = arrayOf<TrustManager>(PinnedTrustManagerProvider.provide(hsConfig.allowedFingerprints, defaultTrustManager))
val sslSocketFactory: SSLSocketFactory

View File

@ -27,26 +27,23 @@ import javax.net.ssl.X509TrustManager
*/
/**
* @param fingerprints An array of SHA256 cert fingerprints
* @param fingerprints Not empty array of SHA256 cert fingerprints
* @param defaultTrustManager Optional trust manager to fall back on if cert does not match
* any of the fingerprints. Can be null.
*/
internal class PinnedTrustManager(private val fingerprints: List<Fingerprint>?,
internal class PinnedTrustManager(private val fingerprints: List<Fingerprint>,
private val defaultTrustManager: X509TrustManager?) : X509TrustManager {
// Set to false to perform some test
private val USE_DEFAULT_TRUST_MANAGER = true
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, s: String) {
try {
if (defaultTrustManager != null && USE_DEFAULT_TRUST_MANAGER) {
if (defaultTrustManager != null) {
defaultTrustManager.checkClientTrusted(chain, s)
return
}
} catch (e: CertificateException) {
// If there is an exception we fall back to checking fingerprints
if (fingerprints.isNullOrEmpty()) {
if (fingerprints.isEmpty()) {
throw UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.cause)
}
}
@ -57,13 +54,13 @@ internal class PinnedTrustManager(private val fingerprints: List<Fingerprint>?,
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, s: String) {
try {
if (defaultTrustManager != null && USE_DEFAULT_TRUST_MANAGER) {
if (defaultTrustManager != null) {
defaultTrustManager.checkServerTrusted(chain, s)
return
}
} catch (e: CertificateException) {
// If there is an exception we fall back to checking fingerprints
if (fingerprints == null || fingerprints.isEmpty()) {
if (fingerprints.isEmpty()) {
throw UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.cause /* BMA: Shouldn't be `e` ? */)
}
}
@ -76,12 +73,11 @@ internal class PinnedTrustManager(private val fingerprints: List<Fingerprint>?,
val cert = chain[0]
var found = false
if (fingerprints != null) {
for (allowedFingerprint in fingerprints) {
if (allowedFingerprint.matchesCert(cert)) {
found = true
break
}
for (allowedFingerprint in fingerprints) {
if (allowedFingerprint.matchesCert(cert)) {
found = true
break
}
}
@ -91,6 +87,6 @@ internal class PinnedTrustManager(private val fingerprints: List<Fingerprint>?,
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return emptyArray()
return defaultTrustManager?.acceptedIssuers ?: emptyArray()
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.network.ssl
import android.os.Build
import androidx.annotation.RequiresApi
import java.net.Socket
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.SSLEngine
import javax.net.ssl.X509ExtendedTrustManager
/**
* Implements a TrustManager that checks Certificates against an explicit list of known
* fingerprints.
*/
/**
* @param fingerprints An array of SHA256 cert fingerprints
* @param defaultTrustManager Optional trust manager to fall back on if cert does not match
* any of the fingerprints. Can be null.
*/
@RequiresApi(Build.VERSION_CODES.N)
internal class PinnedTrustManagerApi24(private val fingerprints: List<Fingerprint>,
private val defaultTrustManager: X509ExtendedTrustManager?) : X509ExtendedTrustManager() {
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String, engine: SSLEngine?) {
try {
if (defaultTrustManager != null) {
defaultTrustManager.checkClientTrusted(chain, authType, engine)
return
}
} catch (e: CertificateException) {
// If there is an exception we fall back to checking fingerprints
if (fingerprints.isEmpty()) {
throw UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.cause)
}
}
checkTrusted(chain)
}
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String, socket: Socket?) {
try {
if (defaultTrustManager != null) {
defaultTrustManager.checkClientTrusted(chain, authType, socket)
return
}
} catch (e: CertificateException) {
// If there is an exception we fall back to checking fingerprints
if (fingerprints.isEmpty()) {
throw UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.cause)
}
}
checkTrusted(chain)
}
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
try {
if (defaultTrustManager != null) {
defaultTrustManager.checkClientTrusted(chain, authType)
return
}
} catch (e: CertificateException) {
// If there is an exception we fall back to checking fingerprints
if (fingerprints.isEmpty()) {
throw UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.cause)
}
}
checkTrusted(chain)
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String, socket: Socket?) {
try {
if (defaultTrustManager != null) {
defaultTrustManager.checkServerTrusted(chain, authType, socket)
return
}
} catch (e: CertificateException) {
// If there is an exception we fall back to checking fingerprints
if (fingerprints.isEmpty()) {
throw UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.cause /* BMA: Shouldn't be `e` ? */)
}
}
checkTrusted(chain)
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String, engine: SSLEngine?) {
try {
if (defaultTrustManager != null) {
defaultTrustManager.checkServerTrusted(chain, authType, engine)
return
}
} catch (e: CertificateException) {
// If there is an exception we fall back to checking fingerprints
if (fingerprints.isEmpty()) {
throw UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.cause /* BMA: Shouldn't be `e` ? */)
}
}
checkTrusted(chain)
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, s: String) {
try {
if (defaultTrustManager != null) {
defaultTrustManager.checkServerTrusted(chain, s)
return
}
} catch (e: CertificateException) {
// If there is an exception we fall back to checking fingerprints
if (fingerprints.isEmpty()) {
throw UnrecognizedCertificateException(chain[0], Fingerprint.newSha256Fingerprint(chain[0]), e.cause /* BMA: Shouldn't be `e` ? */)
}
}
checkTrusted(chain)
}
@Throws(CertificateException::class)
private fun checkTrusted(chain: Array<X509Certificate>) {
val cert = chain[0]
var found = false
for (allowedFingerprint in fingerprints) {
if (allowedFingerprint.matchesCert(cert)) {
found = true
break
}
}
if (!found) {
throw UnrecognizedCertificateException(cert, Fingerprint.newSha256Fingerprint(cert), null)
}
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return defaultTrustManager?.acceptedIssuers ?: emptyArray()
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.network.ssl
import android.os.Build
import javax.net.ssl.X509ExtendedTrustManager
import javax.net.ssl.X509TrustManager
internal object PinnedTrustManagerProvider {
// Set to false to perform some tests
private const val USE_DEFAULT_TRUST_MANAGER = true
fun provide(fingerprints: List<Fingerprint>?,
defaultTrustManager: X509TrustManager?): X509TrustManager {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && defaultTrustManager is X509ExtendedTrustManager) {
PinnedTrustManagerApi24(
fingerprints.orEmpty(),
defaultTrustManager.takeIf { USE_DEFAULT_TRUST_MANAGER }
)
} else {
PinnedTrustManager(
fingerprints.orEmpty(),
defaultTrustManager.takeIf { USE_DEFAULT_TRUST_MANAGER }
)
}
}
}