SSLException for secured endpoints when integrating freeRASP
Describe the bug After adding the library to our project version 8.3.0, when trying to call a secure endpoint, we get SSLException from Retrofit/OkHttp and our api calls fail. We tried updating to 9.0.0 but that didn't fix the problem.
javax.net.ssl.SSLHandshakeException: Read error: ssl=0xb400007aa9f06888: Failure in SSL library, usually a protocol error
error:04000044:RSA routines:OPENSSL_internal:internal error (external/conscrypt/common/src/jni/main/cpp/conscrypt/native_crypto.cc:732 0x7aa3ed3791:0x00000000)
Expected behavior To not receive SSLException from OkHttp.
Please complete the following information:
- Device: Pixel 7 Pro
- OS version: Android 14
- Version of freeRASP: 8.3.0 and 9.0.0
Happen also with all opther test devices we have from Android 10 to 14 (Samsung, Pixel, Huawei, OnePlus, Fairhphone)
Additional context
For the record in our project we have the following libraries Retrofit 2.10.0, Moshi 1.15.1, Koin 3.5.3 , OkHttp 4.12.0, we have proguard, SSL pinning in the app via .certificatePinner on the OkHttpClient and we also have a custom sslSocketFactory with TLS protocol.
App configuration:
- minSdk version is 26
- targetSdk version is 34
The issue is raised when calling an endpoint with SSL Pinning (secure endpoint) Endpoints which are not secured with SSL Pinning, work as expected.
Removing the RASP library fixes our issue with secure endpoints. Here is the code for initialization:
SecurityDetectionHelper.kt
package com.xxx.lib.security.presentation
import android.content.Context
import com.aheaditec.talsec_security.security.api.Talsec
import com.aheaditec.talsec_security.security.api.TalsecConfig
import com.aheaditec.talsec_security.security.api.ThreatListener
import com.xxx.lib.BuildConfig.ANDROID_SUPPORT_MAIL
import com.xxx.lib.security.presentation.SecurityThreatDetectedError.Reason.DEBUGGER_DETECTED
import com.xxx.lib.security.presentation.SecurityThreatDetectedError.Reason.EMULATOR_DETECTED
import com.xxx.lib.security.presentation.SecurityThreatDetectedError.Reason.HOOK_DETECTED
import com.xxx.lib.security.presentation.SecurityThreatDetectedError.Reason.OBFUSCATION_ISSUES_DETECTED
import com.xxx.lib.security.presentation.SecurityThreatDetectedError.Reason.ROOT_DETECTED
import com.xxx.lib.security.presentation.SecurityThreatDetectedError.Reason.TAMPER_DETECTED
import com.xxx.lib.security.presentation.SecurityThreatDetectedError.Reason.UNTRUSTED_INSTALLATION_SOURCE_DETECTED
class SecurityDetectionHelper(
private val context: Context,
) : ThreatListener.ThreatDetected, SecurityDetectionHelperInterface {
private var onThreatDetected: (error: SecurityThreatDetectedError) -> Unit = {}
private var isEnabled: Boolean = false
private val supportedAlternativeStores = arrayOf(
"dev.firebase.appdistribution",
)
private val expectedSigningCertificateHashBase64 = arrayOf(
// App signing
"xxx",
// Upload key certificate
"xxx",
// Internal test certificate
"xxx",
)
private val config: TalsecConfig
get() = TalsecConfig(
PACKAGE_NAME,
expectedSigningCertificateHashBase64,
ANDROID_SUPPORT_MAIL,
supportedAlternativeStores,
isEnabled,
)
override fun onRootDetected() {
onThreatDetected(SecurityThreatDetectedError(ROOT_DETECTED))
}
override fun onDebuggerDetected() {
onThreatDetected(SecurityThreatDetectedError(DEBUGGER_DETECTED))
}
override fun onEmulatorDetected() {
onThreatDetected(SecurityThreatDetectedError(EMULATOR_DETECTED))
}
override fun onTamperDetected() {
onThreatDetected(SecurityThreatDetectedError(TAMPER_DETECTED))
}
override fun onUntrustedInstallationSourceDetected() {
onThreatDetected(SecurityThreatDetectedError(UNTRUSTED_INSTALLATION_SOURCE_DETECTED))
}
override fun onHookDetected() {
onThreatDetected(SecurityThreatDetectedError(HOOK_DETECTED))
}
override fun onDeviceBindingDetected() {
// do nothing
}
override fun onObfuscationIssuesDetected() {
onThreatDetected(SecurityThreatDetectedError(OBFUSCATION_ISSUES_DETECTED))
}
override fun initSecurityDetection(isEnabled: Boolean, onThreatDetected: (error: SecurityThreatDetectedError) -> Unit) {
this.onThreatDetected = onThreatDetected
this.isEnabled = isEnabled
ThreatListener(this).registerListener(context)
Talsec.start(context, config)
}
companion object {
private const val PACKAGE_NAME = "xxx"
}
}
MainActivity.kt
class MainActivity : BaseActivity(R.layout.xxx_activity_main) {
...
private val securityDetectionHelper: SecurityDetectionHelperInterface by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
securityDetectionHelper.initSecurityDetection(THREAT_DETECTION_ENABLED, ::showAppBlockedDialog)
....
}
...
}
Hello @YMonnier , thank you for reporting the issue, we will look at it.
Kind regards, Talsec team
Hello @YMonnier,
I have two ideas on how we could try to resolve this issue.
Talsec also has OkHttp as a dependency. You can try to exclude our (older) dependency and use only the one you already have in your code. You can exclude the dependency by modifying the dependencies block in your build.gradle file.
Replace:
implementation 'com.aheaditec.talsec.security:TalsecSecurity-Community:9.0.2'
with the following lines:
implementation ('com.aheaditec.talsec.security:TalsecSecurity-Community:9.0.2') {
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
}
The second idea is related to the security providers that are used for cryptography. In the older versions, we had spongycastle/bouncycastle as a dependency, and we still use com.google.android.gms.play-services-base. From my experience, there is a possibility that by including these dependencies, the order of the security providers may be modified, which can cause changes in the behavior of the application. Could you please list the order of the providers into the logcat?
You can use the following code to list them:
Security.getProviders().forEach {
Log.e("ProvidersPriority", it.name.toString())
}
If the list/order differs for the version of the app with/without the freeRASP, it could be the cause of your issue and we could continue from that.
Best regards, Ondřej from Talsec
Hello @msikyna @xprikryl2 thanks a lot for your messages.
I tested your first idea, unfortunately it did not work.
About the second idea, we are also using bouncycastle (org.bouncycastle:bcpkix-jdk15to18, version 1.77) for certificate-based authentication etc..
Below you can find the output of the different Security Provider: with freeRASP integrated to the project:
❯ adb logcat | grep ProvidersPriority
04-03 23:42:58.409 9370 9370 D MainActivity: ProvidersPriority: AndroidNSSP
04-03 23:42:58.409 9370 9370 D MainActivity: ProvidersPriority: AndroidOpenSSL
04-03 23:42:58.409 9370 9370 D MainActivity: ProvidersPriority: CertPathProvider
04-03 23:42:58.409 9370 9370 D MainActivity: ProvidersPriority: AndroidKeyStoreBCWorkaround
04-03 23:42:58.409 9370 9370 D MainActivity: ProvidersPriority: BC
04-03 23:42:58.409 9370 9370 D MainActivity: ProvidersPriority: HarmonyJSSE
04-03 23:42:58.409 9370 9370 D MainActivity: ProvidersPriority: AndroidKeyStore
without freeRASP integrated to the project:
❯ adb logcat | grep ProvidersPriority
04-04 13:03:25.230 29982 29982 D MainActivity: ProvidersPriority: AndroidNSSP
04-04 13:03:25.230 29982 29982 D MainActivity: ProvidersPriority: AndroidOpenSSL
04-04 13:03:25.230 29982 29982 D MainActivity: ProvidersPriority: CertPathProvider
04-04 13:03:25.230 29982 29982 D MainActivity: ProvidersPriority: AndroidKeyStoreBCWorkaround
04-04 13:03:25.230 29982 29982 D MainActivity: ProvidersPriority: BC
04-04 13:03:25.230 29982 29982 D MainActivity: ProvidersPriority: HarmonyJSSE
04-04 13:03:25.230 29982 29982 D MainActivity: ProvidersPriority: AndroidKeyStore
Same order unfortunately..
Best Regards, Ysée
Hello, I have additional information, when trying to log in or accessing secure endpoint I have the following error:
Preferred provider doesn't support key:
java.security.InvalidKeyException: Keystore operation failed
at android.security.keystore2.KeyStoreCryptoOperationUtils.getInvalidKeyException(KeyStoreCryptoOperationUtils.java:128)
at android.security.keystore2.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:152)
at android.security.keystore2.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:360)
at android.security.keystore2.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:188)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2985)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2892)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2797)
at javax.crypto.Cipher.chooseProvider(Cipher.java:774)
at javax.crypto.Cipher.init(Cipher.java:1144)
at javax.crypto.Cipher.init(Cipher.java:1085)
at com.android.org.conscrypt.CryptoUpcalls.rsaOpWithPrivateKey(CryptoUpcalls.java:180)
at com.android.org.conscrypt.CryptoUpcalls.rsaSignDigestWithPrivateKey(CryptoUpcalls.java:139)
at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method)
at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:568)
at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095)
at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1079)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712)
at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:896)
at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.-$$Nest$mprocessDataFromSocket(Unknown Source:0)
at com.android.org.conscrypt.ConscryptEngineSocket.doHandshake(ConscryptEngineSocket.java:236)
at com.android.org.conscrypt.ConscryptEngineSocket.startHandshake(ConscryptEngineSocket.java:218)
at jD.k.g(Unknown Source:105)
at jD.k.c(Unknown Source:168)
at jD.e.a(Unknown Source:708)
at jD.a.a(Unknown Source:57)
at kD.f.b(Unknown Source:125)
at hD.a.a(Unknown Source:142)
at kD.f.b(Unknown Source:125)
at kD.a.a(Unknown Source:176)
at kD.f.b(Unknown Source:125)
at kD.g.a(Unknown Source:144)
at kD.f.b(Unknown Source:125)
at jD.i.f(Unknown Source:100)
at jD.f.run(Unknown Source:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)
Caused by: android.security.KeyStoreException: Incompatible purpose (internal Keystore code: -3 message: system/security/keystore2/src/security_level.rs:310
Caused by:
0: system/security/keystore2/src/enforcements.rs:563: the purpose is not authorized.
1: Error::Km(r#INCOMPATIBLE_PURPOSE)) (public error code: 13 internal Keystore code: -3)
at android.security.KeyStore2.getKeyStoreException(KeyStore2.java:416)
at android.security.KeyStoreSecurityLevel.createOperation(KeyStoreSecurityLevel.java:122)
at android.security.keystore2.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:355)
... 36 more
Could not find provider for algorithm: RSA/ECB/NoPadding
Hello @YMonnier,
The issue is related to the cryptography providers, as I mentioned earlier. I don't know how including Talsec could possibly cause this issue (the only thing I could suspect are the dependencies above).
I encountered similar issues a few years ago in a completely different project (without Talsec), and only some BE services were affected (HTTPS to google.com worked fine). I managed to fix the issue by swapping the order of the cryptography providers by using this method Security.insertProviderAt().
I'm not sure if I'm able to provide you with more help in this particular issue. If you get any new information, please let us know.
Best regards, Ondřej from Talsec
Hello @xprikryl2 @msikyna, thank you for your assistance.
Here's an update on our findings:
We've determined that the integration of your library is indeed causing disruptions in our KeyStore management and certificate-based authentication:
- Disabling the initialization function of your library resolves the issue, but this means we cease using your library. For example:
// securityDetectionHelper.initSecurityDetection(THREAT_DETECTION_ENABLED, ::showAppBlockedDialog) - Attempting to move the initialization to the
Application.onCreate()method proved unsuccessful. - Relocating the initialization to another application segment, such as post-login, also failed. Any access to the certificate or KeyStore triggers the previously mentioned issues.
We've also explored modifying the Key Generation parameters based on the stack traces, without success. Attempts to remove potentially interfering libraries like bouncycastle, okhttp, kotlin-stdlib-jdk8, etc., have not been fruitful either.
It appears that initializing freeRASP disrupts the functionality of our keystore/certificate-based authentication significantly.
Could you please provide us with a list of dependencies used in freeRASP? We would like to try excluding them to further investigate the issue.
Hello @YMonnier ,
Thank you for your update and for your diligent investigation work. Dependencies are listed in the POM files for every version of the Android library. You can find the POM file for version 8.3.0 here, for version 9.0.0 here.
We would like to resolve this issue, and your assistance is very appreciated. I could prepare some special versions of the library and we would be really glad if you could test them.
Best regards, Ondřej from Talsec team
Hello @xprikryl2
Thank you for your message! I am free to test some special version of freeRASP. Please, let me know when something is available.
Best Regards, Ysée
Hello @YMonnier,
Could you try the following three versions? Only replace the dependency and use it as it is (without the exclude blocks).
implementation 'com.aheaditec.talsec.security.test:TalsecSecurity-Community:9.0.2-ONLY_DEPENDENCIES'
implementation 'com.aheaditec.talsec.security.test:TalsecSecurity-Community:9.0.2-NO_CHECKS'
implementation 'com.aheaditec.talsec.security.test:TalsecSecurity-Community:9.0.2-BASIC_CHECKS'
Could you please send the result directly to [email protected]? These are testing versions and some of them won't trigger any checks. We really appreciate your help.
Best regards, Ondřej from Talsec
same here
Hello,
We've discovered some conflicts between one of our device binding detection controls and TLS/SSL.
Device binding detection should inform the app that it was cloned/transferred from one device to another (with all the data). In this control, we're using AndroidKeyStore to determine if the private key is still available in the AndroidKeyStore (we store the public key in the SharedPreferences when the key is generated), and we're relying on the fact that the private key cannot be extracted from the keystore (if correctly set up, depending on the keystore type (SE, TEE, ...)). We've discovered that there is a conflict/concurrency issue in the AndroidKeyStore.
Right now, the exact cause of the issue is unknown (whether it's device-related, HW-malfunction, or caused by some nonstandard TLS/SSL implementation), and we'll be carefully monitoring this issue. In the meantime, we've created a special version in which the device binding is disabled. Please, use the following dependency:
implementation 'com.aheaditec.talsec.security:TalsecSecurity-Community:9.6.0-NO_DB'
I would like to thank @YMonnier for the assistance and help during the investigation.
Best regards, Ondřej from Talsec
Hello @YMonnier , @nienienienie ,
could you elaborate if the issue has been resolved with the new dependency? If yes, we will include it into Troubleshooting section in the documentation.
Kind regards, Talsec team
Hello @msikyna
All good on my side. You can close the issue.
Thank you, Ysée