Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java.security.NoSuchProviderException: no such provider: BC on Android 7 #1119

Open
hexise opened this issue Mar 15, 2023 · 4 comments
Open

Comments

@hexise
Copy link

hexise commented Mar 15, 2023

A small number of crashes occur in my apps, they only occur in some models of Android 7, mostly LG devices.

I am using org.bouncycastle.jce.provider.BouncyCastleProvider, as following code in my Android app:

private void setupBouncyCastle() {
    Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    Security.addProvider(new BouncyCastleProvider());
}

But it seems conscrypt library cannot find the provider. Is this a bug or usage error? Thank you.

Fatal Exception: java.lang.AssertionError: java.security.NoSuchProviderException: no such provider: BC
at com.android.org.conscrypt.NativeCrypto.X509_NAME_hash(NativeCrypto.java:399)
at com.android.org.conscrypt.NativeCrypto.X509_NAME_hash_old(NativeCrypto.java:380)
at android.security.net.config.DirectoryCertificateSource.getHash(DirectoryCertificateSource.java:187)
at android.security.net.config.DirectoryCertificateSource.findCerts(DirectoryCertificateSource.java:141)
at android.security.net.config.DirectoryCertificateSource.findAllByIssuerAndSignature(DirectoryCertificateSource.java:116)
at android.security.net.config.SystemCertificateSource.findAllByIssuerAndSignature(SystemCertificateSource.java)
at android.security.net.config.CertificatesEntryRef.findAllCertificatesByIssuerAndSignature(CertificatesEntryRef.java:65)
at android.security.net.config.NetworkSecurityConfig.findAllCertificatesByIssuerAndSignature(NetworkSecurityConfig.java:146)
at android.security.net.config.TrustedCertificateStoreAdapter.findAllIssuers(TrustedCertificateStoreAdapter.java:46)
at com.android.org.conscrypt.TrustManagerImpl.findAllTrustAnchorsByIssuerAndSignature(TrustManagerImpl.java:767)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:452)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:508)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:401)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:375)
at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:304)
at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)
at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:178)
at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:599)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(NativeCrypto.java)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360)
at com.android.okhttp.Connection.connectTls(Connection.java:309)
at com.android.okhttp.Connection.connectSocket(Connection.java:272)
at com.android.okhttp.Connection.connect(Connection.java:240)
at com.android.okhttp.Connection.connectAndSetOwner(Connection.java:444)
at com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:133)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:368)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:285)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:474)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:139)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:89)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java)
at com.google.firebase.crashlytics.internal.network.HttpGetRequest.execute(HttpGetRequest.java:78)
at com.google.firebase.crashlytics.internal.settings.DefaultSettingsSpiCall.invoke(DefaultSettingsSpiCall.java:112)
at com.google.firebase.crashlytics.internal.settings.SettingsController$1.then(SettingsController.java:200)
at com.google.firebase.crashlytics.internal.settings.SettingsController$1.then(SettingsController.java:193)
at com.google.android.gms.tasks.zzo.run(zzo.java:1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at com.google.firebase.crashlytics.internal.common.ExecutorUtils$1$1.onRun(ExecutorUtils.java:67)
at com.google.firebase.crashlytics.internal.common.BackgroundPriorityRunnable.run(BackgroundPriorityRunnable.java:27)
at java.lang.Thread.run(Thread.java:761)
Caused by java.security.NoSuchProviderException: no such provider: BC
at sun.security.jca.GetInstance.getService(GetInstance.java:83)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
at java.security.Security.getImpl(Security.java:590)
at java.security.MessageDigest.getInstance(MessageDigest.java:242)
at com.android.org.conscrypt.NativeCrypto.X509_NAME_hash(NativeCrypto.java:386)
at com.android.org.conscrypt.NativeCrypto.X509_NAME_hash_old(NativeCrypto.java:380)
at android.security.net.config.DirectoryCertificateSource.getHash(DirectoryCertificateSource.java:187)
at android.security.net.config.DirectoryCertificateSource.findCerts(DirectoryCertificateSource.java:141)
at android.security.net.config.DirectoryCertificateSource.findAllByIssuerAndSignature(DirectoryCertificateSource.java:116)
at android.security.net.config.SystemCertificateSource.findAllByIssuerAndSignature(SystemCertificateSource.java)
at android.security.net.config.CertificatesEntryRef.findAllCertificatesByIssuerAndSignature(CertificatesEntryRef.java:65)
at android.security.net.config.NetworkSecurityConfig.findAllCertificatesByIssuerAndSignature(NetworkSecurityConfig.java:146)
at android.security.net.config.TrustedCertificateStoreAdapter.findAllIssuers(TrustedCertificateStoreAdapter.java:46)
at com.android.org.conscrypt.TrustManagerImpl.findAllTrustAnchorsByIssuerAndSignature(TrustManagerImpl.java:767)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:452)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:508)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:401)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:375)
at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:304)
at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)
at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:178)
at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:599)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(NativeCrypto.java)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360)
at com.android.okhttp.Connection.connectTls(Connection.java:309)
at com.android.okhttp.Connection.connectSocket(Connection.java:272)
at com.android.okhttp.Connection.connect(Connection.java:240)
at com.android.okhttp.Connection.connectAndSetOwner(Connection.java:444)
at com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:133)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:368)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:285)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:474)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:139)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:89)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java)
at com.google.firebase.crashlytics.internal.network.HttpGetRequest.execute(HttpGetRequest.java:78)
at com.google.firebase.crashlytics.internal.settings.DefaultSettingsSpiCall.invoke(DefaultSettingsSpiCall.java:112)
at com.google.firebase.crashlytics.internal.settings.SettingsController$1.then(SettingsController.java:200)
at com.google.firebase.crashlytics.internal.settings.SettingsController$1.then(SettingsController.java:193)
at com.google.android.gms.tasks.zzo.run(zzo.java:1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at com.google.firebase.crashlytics.internal.common.ExecutorUtils$1$1.onRun(ExecutorUtils.java:67)
at com.google.firebase.crashlytics.internal.common.BackgroundPriorityRunnable.run(BackgroundPriorityRunnable.java:27)
at java.lang.Thread.run(Thread.java:761)

@prbprbprb
Copy link
Collaborator

Well that's weird.

I can't find any Conscrypt source on cs.android.com for Android 7 that exactly matches the line numbers in your stack trace, but then on that release it gets jarjar-ed from org.conscrypt to com.android.org.conscrypt without being checked in, so that's not too unexpected. I didn't check every version, but between 7.0.0-r1 through 7.1.2-r39 this code seems unchanged, e.g. https://cs.android.com/android/platform/superproject/+/android-7.1.2_r39:external/conscrypt/src/main/java/org/conscrypt/NativeCrypto.java;l=366-383

But the key point is (on all versions I looked at) the MessageDigest.getInstance() call doesn't specify a Provider (because why would it) so the NoSuchProviderException is very unexpected. It's possible LG modified the source, but I don't want to just assume that.

My best guess (and it's a guess) is that there's some kind of caching going on at the Provider level. On a stock Android 7, I believe both Conscrypt and BC should be providing MessageDigest.MD5 (which is what X509_NAME_hash_old() uses... because it's old), so it should really be able to find it.

Is it possible that the the version of Bouncy Castle you're swapping in doesn't provide MD5?

Thinking about it, it's probably not a good idea to install your Bouncy Castle with the same name as the one that shipped with the Platform... So (a) don't remove BouncyCastleProvider.PROVIDER_NAME and (b) install your new version with a different name (you might have to build from source and adda new BC constructor to do this). And choose whether to install it at a high or low priority depending on how you plan to use it.

@hexise
Copy link
Author

hexise commented Mar 16, 2023

I am adding SFTP support to my app, org.bouncycastle.jce.provider.BouncyCastleProvider supports more algorithms than Android native one. The problems are only happens on Android 7 and LG devices. So I agree maybe LG has modified some code which cause this problem. The version Bouncy Castle I am using should support MD5 without problem, otherwise other devices such as devices made by Samsung should have similar problem but they did not. For LG and Android 8.0+ releases I also did not receive similar bug report. I am not sure if there is a way to work around this problem. If not, I am ok with it since Android 7 is an old platform and LG has not big market share. Thanks for your reply.

@prbprbprb
Copy link
Collaborator

prbprbprb commented Mar 16, 2023

Supporting more algorithms is not necessarily better, e.g. the ones that have been removed from Conscrypt have been because they are no longer considered secure enough.

Note that in your stack trace above, you have okhttp making TLS connections via the Android platform's copy of Conscrypt (i.e. the platform default) which is why it has com.android.org.conscrypt rather than org.conscrypt in the stack trace, so:

  • If you're expecting to use BC's TLS implementation, you need to need to configure okhttp accordingly
  • If you're expecting to use a more recent version version of Conscrypt then follow the instructions at https://github.com/google/conscrypt and install Conscrypt as the highest priority Provider (okhttp should have instructions for this). In this configuration it will also be easier to debug any issues you have inter-working with BC.

@hexise
Copy link
Author

hexise commented Mar 18, 2023

I think the android native BC provider is not complete. I am using sshj library. If I do not use BouncyCastleProvider, then I will receive this error when I access my SFTP server: java.security.NoSuchAlgorithmException: no such algorithm: ECDSA for provider BC.

After I added BouncyCastleProvider to Android app, then I can access the SFTP server fine.

As I mentioned, the app sftp function works on every Android device except LG devices on Android 6 and 7. I think on Android 8+ LG also fixed the problem. From the crash I think the problem happens from conscrypt library, for unknown reason, it can not find the BC provider on these devices. I wonder if there is a possible fix or work around for LG devices with Android 6 and 7. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants