No mather what i try, in an new .net 5 API project i cannot connect to RavenDB. It is given me an error:

This server requires client certificate for authentication, but none was provided by the client.

The way I connect to the database:

byte[] certificateBytes = _certificateProvider.GetCertificate("CERT-NAME").Result;
string passphrase = _secretProvider.GetSecret("CERT-PASSPHRASE").Result;
X509Certificate2 certificate = new X509Certificate2(certificateBytes, passphrase);

var documentStore = new DocumentStore()
{
    Urls = new[] { databaseOptions.DbUrl },
    Conventions =
    {
        UseOptimisticConcurrency = true,
        FindCollectionName = findCollectionName
    },
    Database = "DBNAME",
    Certificate = certificate,
}.Initialize();

The certificate provider and the secret provider gets the data out of an Azure Key Vault. I validated the X509Certificate and it has the same thumbprint as I get in the admin panel of RavenDB. So that is loaded correctly. Also the certificate has the read/write rights on the requested database

But when i then do the following:

using (IAsyncDocumentSession session = documentStore.OpenAsyncSession())
{
    var entity = await session.Query<EntityDTO>()
        .SingleOrDefaultAsync();
}

Then i get the following error:

This server requires client certificate for authentication, but none was provided by the client.

This while the certificate is given when initializing the document store. Anyone an idea how to continue with this as RavenCloud doesn't give more information then this?

In another project (.net core 3.1) the same code with the same certificate works. But could not find anything in the release notes of .net 5 what can cause this.

2

There are 2 best solutions below

0
On BEST ANSWER

In the RavenDB forum @iftah responded that you should set the storage flag to MachineKeySet because of the private key.

While that was not the case for me, it give me an direction to check. I was loading the certificate from Azure Key Vault using the CertificateClient and GetCertificateAsync. This only gives you the public key. So there never was an private key.

With the help of this post: KeyVault generated certificate with exportable private key. I've found out that i could use the SecretClient with GetSecretAsync. Then use Convert.FromBase64String() and that gives back also the private key.

So for the ones that have similar problems. Please verify when loading the certificate that you have the private key. My new code:

byte[] certificateBytes = Convert.FromBase64String(_secretProvider.GetSecret("CERT-NAME").Result);
X509Certificate2 certificate = new X509Certificate2(certificateBytes, string.Empty);

var documentStore = new DocumentStore()
{
    Urls = new[] { databaseOptions.DbUrl },
    Conventions =
    {
        UseOptimisticConcurrency = true,
        FindCollectionName = findCollectionName
    },
    Database = "DBNAME",
    Certificate = certificate,
}.Initialize();

And don't forget to load you certificate the following way if you want to have it working on Azure as @iftah suggested above:

X509Certificate2 certificate = new X509Certificate2(certificateBytes, string.Empty, X509KeyStorageFlags.MachineKeySet);
3
On

You need to load the certificate with the MachineKeySet flag.

X509Certificate2 certificate = new X509Certificate2(certificateBytes, passphrase, X509KeyStorageFlags.MachineKeySet);

This will solve the problem in Azure but your local dev machine might have a problem after this change. You need to allow full access for the user running RavenDB to the MachineKeySet folder on your local machine.

"C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys"

Please also note that if you don't dispose the certificate afterwards, the private keys pile up in that folder (a key is written to that folder every time the X509Certificate2 constructor opens the certificate).