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

Android - cannot connect to SQL Server Express 2019 using any casual SqlClient version (v2.1.4, v4.1.0, v5Preview) #1662

Closed
janseris opened this issue Jun 28, 2022 · 38 comments
Assignees
Labels
🔗 External Issue is in an external component

Comments

@janseris
Copy link

janseris commented Jun 28, 2022

Describe the bug

Edit: please use the test application version added in the latest comment in this issue (more functions, less hardcoded)

Introduction:

  • there is no issue on Windows
  • on Windows, you should be seeing this with the sample app:
    • image
    • run as image

This is a continuation of this issue:
#1656
There, only Android 9 and higher can communicate with a SQL Server 2012 with latest patch and a valid certificate from DigiCert.
The message in SqlException is always the same for both of these issues:
(A connection was successfully established with the server, but then an error occurred during the pre-login handshake.)


In this issue, I have set up a local SQL Server Express 2019 instance and opened it to the local network (port 1433).
TCP/IP connection is enabled and a SQL account for authentication is set.

These versions of SQL Server API from C# fail to communicate to the instance from Android (.NET MAUI):

  • Microsoft.Data.SqlClient 2.1.4 (EF Core 6.0.6)
  • Microsoft.Data.SqlClient 5.0.0-preview2.22096.2 (EF Core 7.0.0-preview.5.22302.2)
  • Microsoft.Data.SqlClient 4.1.0
  • Microsoft.Data.SqlClient 2.0.0
  • Microsoft.Data.SqlClient 5.0.0-preview3.22168.1
  • System.Data.SqlClient 4.8.3

Result of the test:
No Android verison can communicate with the SQL Server Express 2019 instance properly after connection

  • all SqlClient versions have the same result on all Android versions when trying to communicate with the SQL Server 2019 instance -> "...pre-login handshake failed..." SqlException of Class 20 (A connection was successfully established with the server, but then an error occurred during the pre-login handshake.)
    The Android devices do in fact connect to the server, because if they did not, I would have received a different SqlException.
  • using TrustServerCertificate=true; and/or Encrypt=false; does not help solving the error

Test project:

About the test project:

  • note: watch the Debug Output window or place a breakpoint or execute step-by-step with debugger to observe the results (Label UI controls (which are supposed to show the text result) in MAUI are bugged and do not show on Android)
  • it executes a DB call using the list of SqlClients listed above

Here a detailed look into a separate test of Microsoft.Data.SqlClient 2.1.4 (EF Core 6.0.6 = current) alone:

The following are results (debug output from Visual Studio) with internal errors on Android versions 5,6,8,9,11,12 for attempted communication with SQL Server Express 2019 (all fail with the same error "pre-login handshake...") for EF COre 6.0.6 (using SqlClient 2.1.4 internally)

  • Android 5 emulator:
    • no internal exception shown
  • Android 6 emulator:
    • no internal exception shown
  • Android 8 emulator:
    • this happens for SQL Server 2012 as well
    •   [System.err] 	at com.android.org.conscrypt.OpenSSLEngineImpl.beginHandshakeInternal(OpenSSLEngineImpl.java:335)
        [System.err] 	at com.android.org.conscrypt.OpenSSLEngineImpl.beginHandshake(OpenSSLEngineImpl.java:325)
        [System.err] 	at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method)
        [System.err] 	at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:30)
        [System.err] 	at android.view.View.performClick(View.java:6256)
        [System.err] 	at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
        [System.err] 	at android.view.View$PerformClick.run(View.java:24701)
        [System.err] 	at android.os.Handler.handleCallback(Handler.java:789)
        [System.err] 	at android.os.Handler.dispatchMessage(Handler.java:98)
        [System.err] 	at android.os.Looper.loop(Looper.java:164)
        [System.err] 	at android.app.ActivityThread.main(ActivityThread.java:6541)
        [System.err] 	at java.lang.reflect.Method.invoke(Native Method)
        [System.err] 	at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        [System.err] 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)```
      
      
  • Android 9 emulator:
    • no internal exception shown
  • Android 11 emulator:
    • this does not happen for SQL Server 2012
    • this happens with Encrypt=false and TrustServerCertificate=true
    • [System.err] 	at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:362)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.convertException(ConscryptEngine.java:1134)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1089)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712)
      [System.err] 	at com.android.org.conscrypt.Java8EngineWrapper.unwrap(Java8EngineWrapper.java:237)
      [System.err] 	at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method)
      [System.err] 	at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:30)
      [System.err] 	at android.view.View.performClick(View.java:7448)
      [System.err] 	at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
      [System.err] 	at android.view.View.performClickInternal(View.java:7425)
      [System.err] 	at android.view.View.access$3600(View.java:810)
      [System.err] 	at android.view.View$PerformClick.run(View.java:28305)
      [System.err] 	at android.os.Handler.handleCallback(Handler.java:938)
      [System.err] 	at android.os.Handler.dispatchMessage(Handler.java:99)
      [System.err] 	at android.os.Looper.loop(Looper.java:223)
      [System.err] 	at android.app.ActivityThread.main(ActivityThread.java:7656)
      [System.err] 	at java.lang.reflect.Method.invoke(Native Method)
      [System.err] 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
      [System.err] 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
      [System.err] Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
      [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:677)
      [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:554)
      [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:510)
      [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:428)
      [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:371)
      [System.err] 	at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:102)
      [System.err] 	at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:106)
      [System.err] 	at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:250)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.verifyCertificateChain(ConscryptEngine.java:1644)
      [System.err] 	at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method)
      [System.err] 	at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:568)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataHeap(ConscryptEngine.java:1115)
      [System.err] 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1087)
      [System.err] 	... 18 more
      [System.err] Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
      [System.err] 	... 32 more```
      
      
  • Android 12 emulator:
    •     [System.err] 	at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:363)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.convertException(ConscryptEngine.java:1134)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1089)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712)
          [System.err] 	at com.android.org.conscrypt.Java8EngineWrapper.unwrap(Java8EngineWrapper.java:237)
          [System.err] 	at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method)
          [System.err] 	at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:30)
          [System.err] 	at android.view.View.performClick(View.java:7441)
          [System.err] 	at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
          [System.err] 	at android.view.View.performClickInternal(View.java:7418)
          [System.err] 	at android.view.View.access$3700(View.java:835)
          [System.err] 	at android.view.View$PerformClick.run(View.java:28676)
          [System.err] 	at android.os.Handler.handleCallback(Handler.java:938)
          [System.err] 	at android.os.Handler.dispatchMessage(Handler.java:99)
          [System.err] 	at android.os.Looper.loopOnce(Looper.java:201)
          [System.err] 	at android.os.Looper.loop(Looper.java:288)
          [System.err] 	at android.app.ActivityThread.main(ActivityThread.java:7839)
          [System.err] 	at java.lang.reflect.Method.invoke(Native Method)
          [System.err] 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
          [System.err] 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
          [System.err] Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
          [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:672)
          [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:549)
          [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:505)
          [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:425)
          [System.err] 	at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:368)
          [System.err] 	at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:102)
          [System.err] 	at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:106)
          [System.err] 	at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:255)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.verifyCertificateChain(ConscryptEngine.java:1638)
          [System.err] 	at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method)
          [System.err] 	at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:569)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataHeap(ConscryptEngine.java:1115)
          [System.err] 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1087)
          [System.err] 	... 19 more
          [System.err] Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
          [System.err] 	... 33 more```
      
      
  • Android 12 device
    - [System.err] javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. [System.err] at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:363) [System.err] at com.android.org.conscrypt.ConscryptEngine.convertException(ConscryptEngine.java:1134) [System.err] at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1089) [System.err] at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876) [System.err] at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747) [System.err] at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712) [System.err] at com.android.org.conscrypt.Java8EngineWrapper.unwrap(Java8EngineWrapper.java:237) [System.err] at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method) [System.err] at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:30) [System.err] at android.view.View.performClick(View.java:7451) [System.err] at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194) [System.err] at android.view.View.performClickInternal(View.java:7425) [System.err] at android.view.View.access$3700(View.java:842) [System.err] at android.view.View$PerformClick.run(View.java:28690) [System.err] at android.os.Handler.handleCallback(Handler.java:938) [System.err] at android.os.Handler.dispatchMessage(Handler.java:99) [System.err] at android.os.Looper.loopOnce(Looper.java:346) [System.err] at android.os.Looper.loop(Looper.java:475) [System.err] at android.app.ActivityThread.main(ActivityThread.java:7889) [System.err] at java.lang.reflect.Method.invoke(Native Method) [System.err] at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) [System.err] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1009) [System.err] Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. [System.err] at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:672) [System.err] at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:549) [System.err] at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:505) [System.err] at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:425) [System.err] at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:368) [System.err] at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:102) [System.err] at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:106) [System.err] at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:255) [System.err] at com.android.org.conscrypt.ConscryptEngine.verifyCertificateChain(ConscryptEngine.java:1638) [System.err] at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method) [System.err] at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:569) [System.err] at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095) [System.err] at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataHeap(ConscryptEngine.java:1115) [System.err] at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1087) [System.err] ... 19 more [System.err] Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. [System.err] ... 33 more

To reproduce

  • have a working SQL Server Express 2019 instance exposed to the network (this tutorial will do: https://www.youtube.com/watch?v=xNmIdFjXzl4)
    • set up a SQL Server Express 2019
    • open port 1433 on firewall
    • configure the SQL Server Express instance to allow SQL authentication
    • enable an account on the instance (e.g. sa)
    • configure the instance to allow TCP/IP connection on port 1433
  • be able to run the MAUI application
    • download latest VS 2022 Preview (because MAUI can only be used in VS 2022 Preview)
    • add the MAUI part in Visual Studio Installer
      • image
    • install some Android emulators (default settings are OK) via Visual Studio ->
      image
      • or use physical device - after connecting via USB and enabling USB debugging, you should be able to see and use it as follows:
        • image

Note:

  • the connection strings which are hardcoded into the sample app, have the following properties:
    • Database Ordinace devRemin
    • SQL account name sa
    • SQL account password sa
    • a table called USER in the default dbo schema
      • the EF test projects require that it has some columns but you can skip using the EF Core projects because they use SqlClient anyways
        • the other projects simply select number of rows from the USER table (could have selected something from a dummy table or be doing SELECT 'Hello World') the current version simply executes SELECT LEN('Hello World')

Note dotnet/maui#2:

  • the Constants.LocalNetworkConnectionString string has a hardcoded private IP of 192.168.0.234
    • this IP will probably be different in every local network
    • the correct IP can be obtained via running the command line command ipconfig on the host machine running the SQL Express (this example requires that the Android device and the machine hosting the SQL Server are in the same local network so that private IP can be used (or you can share your SQL Server Express to the internet and use a public IP address in fact but I did not do that)
      • look for IPv4 Address entry in the output of the ipconfig command, that's the private/local IP address

Expected behavior

Android can communicate with the SQL Server Express.

Further technical details

.NET target: .NET 6.0 (dotnet --version: 6.0.400-preview.22301.10)
SQL Server version: SQL Server Express 2019
Operating system: Windows 10 21H1

@janseris janseris changed the title Android - cannot connect to SQL Server Express v2.1.4, v4.1.0, v5Preview Android - cannot connect to SQL Server Express 2019 using any casual SqlClient version (v2.1.4, v4.1.0, v5Preview) Jun 28, 2022
@janseris
Copy link
Author

janseris commented Jun 29, 2022

Note: EF Core 6.x.x uses Microsoft.Data.SqlClient which uses Encrypt=false by default (Encrypt=true is by default used on Microsoft.Data.SqlClient 4.0.0+)

Day 2:

  • tried older EF Core 6.0.1 Blazor Server application
    • works on the same computer on the same SQL Server Express instance without TrustServerCertificate=true and without Encrypt=false (the connection string is simply Server=LAPTOP-HGEN5Q27\\SQLEXPRESS;Database=Ordinace;Integrated Security=True;
    • this works without any error about certificate validation
  • the same connection string executed from MAUI Windows application requires TrustServerCertificate=true or Encrypt=false else it keeps saying SqlException Class: 20, Number: -2146893019, Message: A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 - The certificate chain was issued by an authority that is not trusted.)
  • the same connection string used from SMSS outputs the error as well (simulating Microsoft.Data.SqlClient behavior using Encrypt=true):
    • image

I read here: https://docs.microsoft.com/en-us/sql/connect/ado-net/introduction-microsoft-data-sqlclient-namespace?view=sql-server-ver15#breaking-changes-in-40 That since verison 4.0.0, Microsoft.Data.SqlClient uses Encrypt=true request on server (which if it can, then encrypts the data transfer communication).

What is strange about this is inconsistency: EF Core 6.0.1 on one project does not output the error and on the other one it does (and incorrectly because Microsoft.Data.SqlClient 2.1.4 should be not encrypting by default.

Why doesn't it work in a way such that this error about certificate on Windows is received only on parts with Microsoft.Data.SqlClient 4.0.0 and newer? Why is this error received with Microsoft.Data.SqlClient 2.1.4 and also with System.Data.SqlClient 4.8,3?

@janseris
Copy link
Author

janseris commented Jun 29, 2022

The app has been updated for faster testing and examination.
The latest version is here: janseris/MAUI_SQLServer_Test@72b40bf janseris/MAUI_SQLServer_Test@52b2a18

It now allows selecting connection string, entering a custom connection string, adding suffixes to the connection string, disable connection pooling (that did not help getting different results on different versions of SqlClient on Windows machine concerning the certificate though) etc.

image

The results for me are (for all versions of SqlClient):

  • SQL Server Express 2019 local instance: no Android device can communicate and Windows devices can communicate only when ignoring the certificate
  • SQL Server 2012 latest patch: Android 9+ devices can communicate and all Windows devices can communicate

What is the expected result:

  • the SQL Server Express 2019 should be fully accessible by any Android version and on Windows the certificate should not be checked when older SqlClient than 4.0.0 is used (because then Encryption is not enabled)
  • the SQL Server 2012 should be fully accessible by any Android version

P.S. results are printed to Debug Output as well.

@JRahnama
Copy link
Contributor

Hi @janseris is this a duplication of #1656?

@janseris
Copy link
Author

janseris commented Jun 30, 2022

Hi @janseris is this a duplication of #1656?

Hi, no it is not but the sample app can be used to test both.
This is mostly about that no Android can connect to SQL Express 2019 with errors varying between Android versions, the newest versions outputting Trust anchor for certification path not found. even when the certificate is explicitly ignored in connection string as mentioned earlier.

And also about inconsistent certificate issues (randomly yes and no) with SQL Express 2019 when connecting from Windows but there is a workaround for that by ignoring the certificate so it is not important.

There is no workaround for the Android issue though and it's fatal.

I believe there is an issue with various other versions of SQL Server or SQL Server Express called from Android because I saw multiple threads about that and none with a solution but I don't have access to more versions than these which I documented so I cannot tell for other SQL Server versions.

@janseris janseris reopened this Jun 30, 2022
@JRahnama
Copy link
Contributor

JRahnama commented Jul 4, 2022

one other step to check before going deeper into the issue. Can you look into Sql Server Configuration Manager , expand Sql Server Network Configuration right click on Protocols for <your instance name> and select property. You will see a window similar to below:

image

and make sure the Force Encryption is set to No. Also when you look into Protocols for <your instance name>, on the right hand side column, can you let me know what protocol names are enabled?

image

@stegl83
Copy link

stegl83 commented Jul 5, 2022

Hi @janseris,

we spend a LOT of hours to get this working.

You have to use certificates, without them there is no connection possible.
This is our way to achieve the successful connection:

Setup the Device and Server

  1. create a selfsigned Cert, consider this:
    • the Flag "CA:TRUE" must be set
    • set the commonName to the ServerName like this: [PCName].[Domain].local
    • Build a CRT-File and a PFX
  2. install the CRT-File on the device and the PFX on the Server
    (we have installed the cert in the Store "Computer/own Cert" and make a copy to "trustworthy root... " (in german it was "Vertrauenswürdige Stammzertifizierungsstellen")
  3. Set Permission -> "Manage Private Keys" see Link below (2)
  4. you can now select the cert in the certicate-Tab in SQL Configuration Mgr
    (SQL-Network config -> Right click on Protocols)
  5. restart SqlService

Setup the MAUI-App (You have to explicitly allow User Certificates)

  1. AndroidManifest (in application-tag): android:networkSecurityConfig="@xml/network_security_config"

  2. include "network_security_config.xml" in project
    network_security_config.zip

  3. ConnectionString like this:
    Data Source=[PCName].[Domain].local; ... ;Trust Server Certificate=True

Give a Try.

Links:
(1) https://codekabinett.com/rdumps.php?Lang=2&targetDoc=create-install-ssl-tls-certificate-sql-server
(2) https://stackoverflow.com/questions/36830411/how-can-i-give-sql-server-permission-to-read-my-ssl-key

@DavoudEshtehari
Copy link
Contributor

@janseris were you able to make a connection to server with the provided information above?

@janseris
Copy link
Author

@janseris were you able to make a connection to server with the provided information above?

Hi, I had days off last week. I hope to get to this later this week. I haven't tried yet but saw your tips. Thank you.

@FikretAkin
Copy link

Hello, I've been dealing with this error for 2-3 days, SQL SERVER 2019 Express is installed and all settings are done, even if I install the certificate, it is not accepted, I am trying to connect with DbCOntext via EF CORE.
Error detail;

[System.err] javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. [System.err] at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:363) [System.err] at com.android.org.conscrypt.ConscryptEngine.convertException(ConscryptEngine.java:1134) [System.err] at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1089) [System.err] at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876) [System.err] at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747) [System.err] at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712) [System.err] at com.android.org.conscrypt.Java8EngineWrapper.unwrap(Java8EngineWrapper.java:237) [System.err] at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method) [System.err] at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:30) [System.err] at android.view.View.performClick(View.java:7509) [System.err] at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194) [System.err] at android.view.View.performClickInternal(View.java:7486) [System.err] at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0) [System.err] at android.view.View$PerformClick.run(View.java:29358) [System.err] at android.os.Handler.handleCallback(Handler.java:942) [System.err] at android.os.Handler.dispatchMessage(Handler.java:99) [System.err] at android.os.Looper.loopOnce(Looper.java:201) [System.err] at android.os.Looper.loop(Looper.java:288) [System.err] at android.app.ActivityThread.main(ActivityThread.java:7892) [System.err] at java.lang.reflect.Method.invoke(Native Method) [System.err] at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) [System.err] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) [System.err] Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. [System.err] at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:670) [System.err] at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:547) [System.err] at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:503) [System.err] at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:423) [System.err] at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:366) [System.err] at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:102) [System.err] at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:106) [System.err] at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:255) [System.err] at com.android.org.conscrypt.ConscryptEngine.verifyCertificateChain(ConscryptEngine.java:1638) [System.err] at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method) [System.err] at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:569) [System.err] at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095) [System.err] at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataHeap(ConscryptEngine.java:1115) [System.err] at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1087) [System.err] ... 19 more [System.err] Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. [System.err] ... 33 more **Microsoft.Data.SqlClient.SqlException:** 'A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 35 - An internal exception was caught)'

@janseris
Copy link
Author

janseris commented Aug 18, 2022

@DavoudEshtehari, @stegl83
Hi, @FikretAkin tried the certificate and didn't work.
I am too busy, sorry.

@FikretAkin
Copy link

@janseris
Hi thank you,
Let's face it, we will now provide a solution with the RESTFULL API.

@janseris
Copy link
Author

@FikretAkin thank you for tip but currently I am not planning this solution because this is a very expensive workaround.

@chandra-arifin
Copy link

i know the security of "direct" communicate with SQL Server Database. but we just "connect" locally, and no security issues here with small team like me. and too expensive with Restfull API, because we are not big team or corporate.
so maybe someone can fixed this bug? please

@janseris
Copy link
Author

janseris commented Sep 15, 2022

Hi, any update, please? There are multiple people in MAUI repo reporting this.
@stegl83 @JRahnama

@lcheunglci
Copy link
Contributor

lcheunglci commented Sep 15, 2022

Thanks @janseris for your thorough analysis. I'm currently investigating dotnet/maui#1412 and it seems like a similar issue. I'm able to reproduce the connection failure issue using Sql Server 2019 express edition, and my .NET6 MAUI app running with MDS v5.0 on the Android Emulator running Android 12.1. Also did some digging and saw the issue also mentioned here dotnet/maui#3522 that the project needs to set it's SSL/TLS Implementation to Native TLS 1.2+ ; however, in .NET6's MAUI project, the option no longer exists and I read there was a mention that TLS 1.2 is enabled by default https://techcommunity.microsoft.com/t5/app-development/enable-tls-1-2-on-net-maui-app-on-android/m-p/3267534 . In the same issue, someone also mentioned that it worked on AzureSqlServer, but only local/on-prem instances of SqlServer seems to run into the issue which makes me suspect, it might be a configuration issue, but I'll have to dig deeper to figure out the root cause.

@janseris
Copy link
Author

janseris commented Sep 15, 2022

@lcheunglci
Yes I believe that the issue you linked to is the same issue.
For me it's SQL Server 2012 with latest patch working for Android 9+ only and SQL Server Express 2019 working on none.
With WinForms, ASP.NET or Console, I never had any issue with any SQL Server version.

in .NET6's MAUI project, the option for TLS no longer exists

Yes you are right. It disappeared with one of last MAUI release candidates in May/April 2022 and was there because of HttpClient issues I think.

@lcheunglci
Copy link
Contributor

Thanks for the insight @janseris, I'll keep that in mind when I'm doing my testing.

@lcheunglci lcheunglci self-assigned this Sep 15, 2022
@janseris
Copy link
Author

janseris commented Sep 15, 2022

btw. there are very similar issues with SSL in SMTP, WebSocket and gRPC to this one also for MAUI Android.
Mainly affecting older Android versions, sometimes precisely matching the Android versions in this issue -> 8.1 and lower vs 9.0 and higher

SMTP: dotnet/maui#9587 probably related and matching Android versions, too
gRPC: dotnet/android#7274 not sure if directly related
WebSocket: dotnet/runtime#83118
with very familiar error

java.lang.IllegalStateException: Handshake has already been started
W System.err: at com.android.org.conscrypt.OpenSSLEngineImpl.beginHandshakeInternal(OpenSSLEngineImpl.java:335)

@stegl83
Copy link

stegl83 commented Sep 19, 2022

Hi folks,

sorry for replying so late. Here are some information about creating a valid cert.
The guy who created the cert for me wrote one cnf-File and used openssl- tool to create the cert.
Maybe there are some unnecessary entries, but at the end this works for me.

this is the first call to openssl-Tool:
openssl req -config openssl.cnf -new -x509 -days 825 -out mycert27.crt

(of course, you have to replace every "[PCName].[Domain].local" in cnf-File)

Second call to openssl to create PFX from CRT and KEY:

openssl pkcs12 -export -in mycert27.crt -inkey mykey27.key -out mycert27.pfx

After creating these to files, you can install them on server and device as described in my previous post.
Good luck.

openssl.cnf.txt

@janseris
Copy link
Author

@stegl83
Thanks, now please why is this even required on Android when it is not on Windows?
Did it solve the connection problem?

@lcheunglci
Copy link
Contributor

Hi, I managed to get it working on the android emulator running Android 21 with Sql Server 2019 and M.D.SqlClient 5.0 GA.

After I generated the self signed certs and installing them on both the sql server and android emulator by following the instructions from @stegl83 on creating the self signed certificate here and installing certs i.e. installing pfx file on the my local sql server and the crt file onto the android device by copying it using adb push <path_to_self_signed_crt_not_pfx> /sdcard/download/ directory and on the device, going into settings and installing the certificate.

Go into Settings > Security > Encryptions & credentials > Install a certificate > CA Certificate
install_cert_on_android
Select Install Anyway and locate your certificate in your Downloads folder
install_cert_on_android_2
Once installed, the notification will give you warning that there's a CA installed from an unknown third party, which means your self-signed cert was installed successfully
install_cert_on_android_3

Since my android emulator and the sql server were both on the same machine. I found this article on the MAUI documentation and use the 10.0.2.2 ip address instead of my machine's hostname and adding the following to the network_security_config.xml in addition to the previous instructions on the MAUI side.

    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">10.0.2.2</domain>
    </domain-config>

Now boot up your app, and connect to 10.0.2.2,1433 or whichever port you chose for your Sql Server and it should get passed the connection.Open()

and the connection string I used is:
Data Source=tcp:10.0.2.2,1433;Database=Northwind;uid=sa;pwd=***;encrypt=false;Connection Timeout = 30;TrustServerCertificate=true;

note: don't use trusted_connection=true; in the connection string as it'll override the uid and pwd as I tested it and resulted in a System.Data.Security.Native unable to load dll error with a stack trace identical to this.

As soon as I removed the self-signed cert from either end i.e. android or sql server, the error 35 returns. I recall reading that the Azure Sql Server doesn't run into this issue, which I'm going to guess is because it's the url i.e. *.database.windows.net might already have proper ssl certificate issued by an official CA installed on the server and the client automatically trusts the certificate, so it doesn't this setup.

Let me know if this works for you.

@janseris
Copy link
Author

@lcheunglci Perfect.
Do you have any clue why a certificate is required on Android while it is not on Windows and especially why it cannot be ignored by TrustServerCertificate true which should bypass the certificate validity check?

@lcheunglci
Copy link
Contributor

From a brief look at how the TrustServerCertificate connection parameter is used, it seems like it's only referenced when encrypt parameter is set to true or mandatory during the enabling of ssl, and will require more investigation in the MDS.SNI to figure out how it works.

@stegl83
Copy link

stegl83 commented Sep 21, 2022

I'm glad that I could help you.
It's annoying that we have so much effort for securing a connection in private network. Not only to spend a lot of hours to get this running, as far as there is no easier solution, we had additional effort for maintaining certificates on server and devices in each production environment we set up.

The next step is to find if its possible to rollout a certificate by MDM (Android Enterprise).

@thevirtualdj
Copy link

thevirtualdj commented Sep 28, 2022

Managed to solve it with cert and IP address.
How?

  1. Create a cert with powershell for your IP address: New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname '192.168.0.15','localhost' -KeySpec KeyExchange -FriendlyName '192.168.0.15' -NotAfter (Get-Date).AddMonths(240)
    (Will work with any IP address of your sql server later).

  2. Set your cert in SQL Server Configuration manager.

  3. Export that certificate as Base-64 encoded X.509 (.CER) file.

  4. Import that cer in Platforms\Android\Resources\raw folder (if it does not exist create it).

  5. In Platforms\Android\xml folder create nsc.xml (for me was not working if I name it network_security_config.xml)
    with content: (change the data ofc IP and domain names and @raw/certname mine was called razvoj1.cer in \Platforms\Android\Resources\raw folder and my SQL server IP was 192.168.0.15 )

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
	<base-config cleartextTrafficPermitted="true">
		<trust-anchors>
			<certificates src="system" />
			<certificates src="user" />
			<certificates src="@raw/razvoj1"/>
		</trust-anchors>
	</base-config>
    <domain-config cleartextTrafficPermitted="true">
		<domain includeSubdomains="true">localhost</domain>
		<domain includeSubdomains="true">192.168.0.15,1433</domain>
		<domain includeSubdomains="true">192.168.0.15</domain>
        <domain includeSubdomains="true">RAZVOJ1</domain>
		<domain includeSubdomains="true">RAZVOJ1.localhost</domain>
		<trust-anchors>
			<certificates src="system" />
			<certificates src="user" />
			<certificates src="@raw/razvoj1"/>
		</trust-anchors>
    </domain-config>
</network-security-config>
  1. In AndroidManifest add android:usesCleartextTraffic="true" and android:networkSecurityConfig = "@xml/nsc" to application tag :
<?xml version="1.0" encoding="utf-8"?>
<manifest android:targetSandboxVersion="1" xmlns:android="http://schemas.android.com/apk/res/android">
	<application android:allowBackup="true" 
				 android:icon="@mipmap/appicon" 
				 android:roundIcon="@mipmap/appicon_round"
				 android:usesCleartextTraffic="true"
			         android:networkSecurityConfig="@xml/nsc"
				 android:supportsRtl="true"></application>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.INTERNET" />
</manifest>
  1. Use this connection string:
    string connectionString = @"Server=192.168.0.15,1433;Database=YOURDB;User Id=sa;Password=YOURPASS;Persist Security Info=True;Encrypt=True;TrustServerCertificate=True";

  2. You should be able to open SQLConnection with Microsoft.Data.SQLClient 5.0.0 and .net 7 MAUI

@lcheunglci
Copy link
Contributor

Thanks @thevirtualdj for providing the steps in concise and organized manner. It'll definitely make it easier for others to follow. As for the TrustServerCertificate, I found that once the self-signed cert is installed on the Android device/emulator, it actually doesn't matter if it's set to true or false, which is a bug that I'll further investigate.

@janseris
Copy link
Author

From a brief look at how the TrustServerCertificate connection parameter is used, it seems like it's only referenced when encrypt parameter is set to true or mandatory during the enabling of ssl, and will require more investigation in the MDS.SNI to figure out how it works.

The SSL/TLS moment is here during SQL Server login process. This part of the connection is always encrypted even when Encrypt=false. Otherwise, plaintext login and password would be sent over the network because SQL Server communication is not encrypted by default. AFAIK what we are talking all the time about here is that the SSL problem is in the login process because everything fails at this moment (the Encrypt true /false option isn't used at all because the actual data transfers aren't even initiated when login fails because of Android errors)

@thevirtualdj
Copy link

thevirtualdj commented Sep 29, 2022

From a brief look at how the TrustServerCertificate connection parameter is used, it seems like it's only referenced when encrypt parameter is set to true or mandatory during the enabling of ssl, and will require more investigation in the MDS.SNI to figure out how it works.

The SSL/TLS moment is here during SQL Server login process. This part of the connection is always encrypted even when Encrypt=false. Otherwise, plaintext login and password would be sent over the network because SQL Server communication is not encrypted by default. AFAIK what we are talking all the time about here is that the SSL problem is in the login process because everything fails at this moment (the Encrypt true /false option isn't used at all because the actual data transfers aren't even initiated when login fails because of Android errors)

Glad you managed to get it rolling :) Hope that .NET team will manage to solve usesCleartextTraffic case. Because in most cases apps are hidden behind VPN or some private net and does not need for cert encryption on this level.

@janseris
Copy link
Author

janseris commented Sep 29, 2022

Because in most cases apps are hidden behind VPN or some private net and does not need for cert encryption on this level.

Clear text is quite dangerous. Small businesses which use direct communication to db to save api development aren't probably the case you describe. Going plaintext on sending password to the DB over the network is the worst solution.

@thevirtualdj
Copy link

From a brief look at how the TrustServerCertificate connection parameter is used, it seems like it's only referenced when encrypt parameter is set to true or mandatory during the enabling of ssl, and will require more investigation in the MDS.SNI to figure out how it works.

The SSL/TLS moment is here during SQL Server login process. This part of the connection is always encrypted even when Encrypt=false. Otherwise, plaintext login and password would be sent over the network because SQL Server communication is not encrypted by default. AFAIK what we are talking all the time about here is that the SSL problem is in the login process because everything fails at this moment (the Encrypt true /false option isn't used at all because the actual data transfers aren't even initiated when login fails because of Android errors)

Glad you managed to get it rolling :) Hope that .NET team will manage to solve usesCleartextTraffic case. Because in most cases apps are hidden behind VPN or some private net and does not need for cert encryption on this level.

Clear text is quite dangerous. Small businesses which use direct communication to db to save api development aren't probably the case you describe. Going plaintext on sending password to the DB over the network is the worst solution.

We understand that, but we are having a lot of troubles with certificates.
Example.. mine example is working fine with android 10,11,12 but on 7.1 we have error like:

[System.err] java.lang.IllegalStateException: Handshake has already been started
[System.err] at com.android.org.conscrypt.OpenSSLEngineImpl.beginHandshake(OpenSSLEngineImpl.java:139)
[System.err] at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.n_onClick(Native Method)
[System.err] at crc64fcf28c0e24b4cc31.ButtonHandler_ButtonClickListener.onClick(ButtonHandler_ButtonClickListener.java:31)
[System.err] at android.view.View.performClick(View.java:5637)
[System.err] at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
[System.err] at android.view.View$PerformClick.run(View.java:22433)
[System.err] at android.os.Handler.handleCallback(Handler.java:751)
[System.err] at android.os.Handler.dispatchMessage(Handler.java:95)
[System.err] at android.os.Looper.loop(Looper.java:154)
[System.err] at android.app.ActivityThread.main(ActivityThread.java:6121)
[System.err] at java.lang.reflect.Method.invoke(Native Method)
[System.err] at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
[System.err] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)

@janseris
Copy link
Author

janseris commented Sep 30, 2022

@thevirtualdj
The issue as you can see, is not with certificate, but is an issue with SSL handshake in SSL implementation used by Android 8.1 and lower in C#.

@AdrianHx2
Copy link

We still have no solution to make it works without a cert right now, right?

@lcheunglci
Copy link
Contributor

We still have no solution to make it works without a cert right now, right?

Yes, that is correct for a local/on-prem SQL Server. I believe it works for an Azure SQL Database without manually trusting the self-signed certificate on the device because the certificate comes from a trusted certificate authority. My assumption is that implementation to Trust Server Certificate is overwritten by the OS security levels on the device, but further investigation is required.

@AdrianHx2
Copy link

2. SQL Server Configuration manager

I followed your steps but still getting the same error with SQL connection, Microsoft.Data.SQLClient 5.0.1 on SQL server 2019 but still get the error 31 T_T btw i tested on Android 9 and Android 11 real devices.

@stegl83
Copy link

stegl83 commented Jan 18, 2023

There are good news about the issue:
dotnet/runtime#77386 has been merged.

Maybe we have a solution in .NET 8.

@AdrianHx2
Copy link

There are good news about the issue: dotnet/runtime#77386 has been merged.

Maybe we have a solution in .NET 8.

I followed that post while ago, the bad thing is that we have to wait 10 months, Right? xD

@stegl83
Copy link

stegl83 commented Jan 18, 2023

There are good news about the issue: dotnet/runtime#77386 has been merged.
Maybe we have a solution in .NET 8.

I followed that post while ago, the bad thing is that we have to wait 10 months, Right? xD

Yeah, right.
But better than never get it because its moved to the "backlog" AKA recycle bin.
I'm waiting for the first preview of .NET 8 for MAUI ...

@lcheunglci lcheunglci added the 🔗 External Issue is in an external component label Feb 6, 2023
@lcheunglci
Copy link
Contributor

I'm closing this issue as it's an issue caused by the runtime and is fixed when a new version of .NET is released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔗 External Issue is in an external component
Projects
None yet
Development

No branches or pull requests

9 participants