Skip to main content

How to Verify a MySQL Server Certificate in .NET C# with VerifyCA

· 6 min read
Jagdish Kumawat
Founder @ Dewiride

SslMode=Required encrypts the connection, and doesn't check who's at the other end. VerifyCA adds that check: your app only connects to a server whose certificate was signed by a CA you trust. It takes one file, and two options.

What You Need​

Step 1: Get the CA File​

For MySQL in Docker, it's in the data folder. Run this from your project folder, and add certs/ to .gitignore first:

Terminal
mkdir certs
docker compose cp mysql:/var/lib/mysql/ca.pem certs/ca.pem

A managed MySQL server's provider publishes its CA file. If it holds several certificates, keep only the one you need, because this driver reads only the first.

The CA file isn't secret, so it's safe to ship with your app.

Step 2: Add Two Options​

Connection string
…;SslMode=VerifyCA;SslCa=/full/path/to/certs/ca.pem

Use a full path, because a relative one depends on where the app starts. Here, $PWD fills it in from your current folder:

Terminal
export MYSQL_CONNECTION_STRING="Server=127.0.0.1;Port=3306;Database=inventory;User ID=inventory_app;Password=$(grep '^MYSQL_PASSWORD=' ../dewiride-inventory-api/.env | cut -d= -f2-);SslMode=VerifyCA;SslCa=$PWD/certs/ca.pem"
dotnet run test-connection.cs
Output
Connected as inventory_app@% to MySQL 9.7.4
TLS: TLSv1.2

The test program with SslMode=VerifyCA and SslCa printing Connected as inventory_app@% to MySQL 9.7.4, with TLS: TLSv1.2.

In an ASP.NET Core app, it's the same options in the same connection string. With user-secrets, run this from the solution folder:

Terminal
dotnet user-secrets set "ConnectionStrings:Inventory" "Server=127.0.0.1;Port=3306;Database=inventory;User ID=inventory_app;Password=$(grep '^MYSQL_PASSWORD=' .env | cut -d= -f2-);SslMode=VerifyCA;SslCa=$PWD/certs/ca.pem" --project src/Dewiride.Inventory.Api

Step 3: Prove It Refuses the Wrong Server​

A check you haven't seen fail isn't proven. Start a second MySQL with the same password, on another port:

Terminal
docker run -d --name inventory-other-mysql --env-file .env -e MYSQL_DATABASE=inventory -e MYSQL_USER=inventory_app -p 127.0.0.1:3307:3306 mysql:9.7

Give it about 30 seconds, then point the tester at port 3307, with the first server's CA:

Output
Unhandled exception. System.Exception: The provided key is invalid
---> Org.BouncyCastle.Security.InvalidKeyException: Public key presented not for certificate signature

The test program refusing the second server on port 3307 with System.Exception: The provided key is invalid.

Refused. The same server with SslMode=Required connected. Stop it when you're done, with docker stop inventory-other-mysql, and docker rm inventory-other-mysql removes it.

What VerifyCA Doesn't Check​

It proves your CA signed the certificate, and not that it was issued to this server. If your CA signs only this server's certificate, as MySQL's automatic one does, that's enough. If it signs for many, as a cloud provider's does, use VerifyFull.

It also checks the certificate's dates: an expired one is refused, with certificate expired on.

When the CA Changes​

docker compose down -v deletes the data volume, so the next start makes a new CA, with the same name. Your copy no longer matches, and you get The provided key is invalid. Copy it again.

Common Mistakes​

MistakeWhat happensDo this instead
Copying server-cert.pem as the CAThe check failsCopy ca.pem
A relative path in SslCaIt fails when the app starts somewhere elseUse a full path
Copying the CA's key as wellYou've spread the key that can sign anythingCopy only ca.pem
Setting SslCa and leaving SslMode=RequiredPEM certificates are only checked with VerifyCA or VerifyFullChange SslMode too

Troubleshooting​

SymptomLikely causeFix
No file path has been provided … SslCaSslCa is missingThe fix
Failed to read file at the specified locationThe path is wrongThe fix
The certificate authority (CA) does not matchA different CA, or a file with severalThe fix
The provided key is invalidA different CA with the same nameThe fix
The remote certificate was rejected…A .pfx fileThe fix

Conclusion​

Copy the server's CA, add SslMode=VerifyCA;SslCa=… with a full path, and test it against a server it should refuse.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.