将库 freeRASP Android(版本 8.3.0)添加到我们的项目后,当尝试调用安全端点时,我们从 Retrofit/OkHttp 或 KeyStore 异常中收到 SSLException,并且我们的 api 调用失败。 我们尝试更新到 9.0.0,但这并没有解决问题。
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)
使用以下设备进行测试
应用程序配置:
我们拥有的 Android 10 至 14 的所有其他测试设备(三星、Pixel、华为、OnePlus、Fairphone)也会发生这种情况
为了记录在我们的项目中,我们有以下库 Retrofit 2.10.0、Moshi 1.15.1、Koin 3.5.3、OkHttp 4.12.0、BouncyCastle,我们通过
.certificatePinner
在应用程序中安装了 proguard、SSL 固定 OkHttpClient
,我们还有一个带有 TLS 协议的自定义 sslSocketFactory
。
此外,在尝试登录应用程序时,我们还遇到以下问题。
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
我们的应用程序中有基于证书的身份验证。在登录/注册期间,我们创建证书并将其存储到密钥库中。然后,我们通过 OkHttp 等方式使用证书...
删除 RASP 库解决了我们的安全端点问题。 这是初始化代码:
安全检测助手.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)
....
}
...
}
我尝试:
Security.insertProviderAt(BouncyCastleProvider(), 1)
implementation (libs.talsecSecurityCommunity) {
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
exclude group: 'org.bouncycastle'
}
freeRASP 识别并解决了其设备绑定检测控制与 TLS/SSL 之间的冲突。他们发现 AndroidKeyStore 存在问题,这可能会导致设备绑定检测错误。作为临时解决方案,他们发布了禁用设备绑定的特殊软件版本。
请使用以下依赖项:
implementation 'com.aheaditec.talsec.security:TalsecSecurity-Community:9.6.0-NO_DB'
请参阅https://github.com/talsec/Free-RASP-Android/issues/31了解详细信息