Andrus
Andrus

Reputation: 27899

How to make get request with certificates in Windows

ASP.NET Core 9 MVC controller reads account list from bank.

program.cs:

internal class Program
{
    public const string LhvClient = "lhvclient";

    static void Main(string[] args)
    {
        WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
        const string lhvprivaatvoti = @"-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtlfiLYZ48IwU3cbJA69m5F9xCiamWzEvkzseP8aAuLHDVdUi
AVIzvKZeILr/M9Sc+wowLYz0azBV64YiFI9qoder4BcfkfvznjRKwPUl6wQhOMUG
hkM79VusdcUAiP31f0pVTtEKhmMqJik5asalB1TFdjylaW3VbrkHqdXVFw91Q3aK
ifrc4Z6KDPt6wqDCXe108mG0QyaSGtc0lBuV0ZoWL3ZNHrbh3NRV4Ojb/O3mAog8
rV0FbAufy0qZnaTse52x8KqZDE2rJjNWr23QLk2OAezt6t7a2ZCOLF3OS+tMqyeu
M8N1lqQIORFo18iYxAXt5Rd9g6Ap6KpE8ahzuQIDAQABAoIBABLtFPJ+u6d+vV0L
mKpY7AgWM/McHIuTPl88yqCkp5BZZcSkm6P2y/HvyWNXpXd+wPskN+idHHLSZd34
DSovJ0qRyoA28OBD038p6acU4Duq27vlug342oysAumHv3d4ofZKrodra86Tv7iN
nNe4CJcLNc0A8cul9ST0muwGNYOaup0wOqLOskfvXmhqYT8gu9h8sbsW40WupIip
OFUwlGXBwtWCycJTD/UdsCKQZZ1oTX/oWpXDslzUyeUwssgaFdJwAw2Y1IQDODLk
oV9pSjr2inWfIz0ILbtm131zSTY7BSMspbW1IKT1WJlrJz5gAYJjOddsoZ8RwUtX
OHnPBFUCgYEA3InyQPWue5MU0j2PSNY3WQaMg/jD1zelmWZ1M65mxihfl/Ctf33I
8mtEGoWzwaNgRcyTgd2SqOxHMUqeJ1b6mVSRgnC8GtSV32AG/k3qpZ6BB/rLZu5E
hOcCoqysdF/ng4y1blVoZeJ4u06VI2tSjrp79M2aHh5WNEyEuZ4xSZsCgYEA06mw
9AXg1LgwsBQJpNbMRwK7UVoJgjMiwKfsMkIDe2keeJo5eD68cseyOyvRjcssft8d
Sxi3AAMa0ymHOi7/m013PHkfzVSeT0nmL2iGZ1s+w9EnVzwzkNXrFL9T9/QPTOTS
75tPcbmWvMxwBxgdE5GouVpI+/PSAXYKoVQPxzsCgYEAyAnkECII2xQFPdISxplv
6LN0/zvEv9E8xxDVXERj+neihdoMNWktvR2oz3nZG9oKOCWg4pnNLqXqyX4KSFqv
wiagObXyGVAchVm/3ilknkdQeKF2n+2dfwNfM5j2cDSRdZRK+UJFCK1Mn3Fe+5qc
btQeHWXk1k7kGFoTxZ4EzOMCgYApA3mZX0Y4kdRFWiygN0rv+5SGZ/bttrDmOeOn
vWjlUfIolmHKbgWgDBf6JTx+yD6/+sW3VnunUfKxthtQ5+h5lGIlYqcJ53qhjIVA
7HUWs/SOhwmjerPXPcxGgehoZG9OjAxfh222cKrHvKl8hmyj7RaPi/IWeCFaTSA0
MJC6HwKBgQDVLOevx/XyYiIkOCNn90kyPBNOhMz7vo8vooE8sHCr7y/NerCZfDxG
1c74EBD8aAyLDbQWh3aoi698TvVhbwtsLwh+KqkajSuGnVKVkhMFm+H6Pny/UF16
jMQa/SSC4BBj5Y8IIPXoJC4HNlqocCBKHUgQHAurmXMR4ExHCq8ROg==
-----END RSA PRIVATE KEY-----";

        const string lhvsert = @"-----BEGIN CERTIFICATE-----
MIIE4TCCAsmgAwIBAgIGAZPGzbosMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYT
AkVFMREwDwYDVQQIDAhIYXJqdW1hYTEUMBIGA1UECgwLQVMgTEhWIFBhbmsxDTAL
BgNVBAsMBFBTRDIxITAfBgNVBAMMGExIViBQU0QyIEludGVybWVkaWF0ZSBDQTAe
Fw0yNDEyMTQyMDEzMTFaFw0zNDEyMTIyMDEzMTFaMIGFMR0wGwYDVQRhExRQU0RF
RS1MSFZURVNULThmMjBmZTEeMBwGA1UEAxMVUFNEMiB0ZXN0IGNlcnRpZmljYXRl
MRIwEAYDVQQKEwlQU0QyIFRlc3QxEDAOBgNVBAcTB1RhbGxpbm4xETAPBgNVBAgT
CEhhcmp1bWFhMQswCQYDVQQGEwJFRTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALZX4i2GePCMFN3GyQOvZuRfcQomplsxL5M7Hj/GgLixw1XVIgFSM7ym
XiC6/zPUnPsKMC2M9GswVeuGIhSPaqHXq+AXH5H78540SsD1JesEITjFBoZDO/Vb
rHXFAIj99X9KVU7RCoZjKiYpOWrGpQdUxXY8pWlt1W65B6nV1RcPdUN2ion63OGe
igz7esKgwl3tdPJhtEMmkhrXNJQbldGaFi92TR624dzUVeDo2/zt5gKIPK1dBWwL
n8tKmZ2k7HudsfCqmQxNqyYzVq9t0C5NjgHs7ere2tmQjixdzkvrTKsnrjPDdZak
CDkRaNfImMQF7eUXfYOgKeiqRPGoc7kCAwEAAaNzMHEwbwYIKwYBBQUHAQMEYzBh
BgYEAIGYJwIwVzA5MBEGBwQAgZgnAQIMBlBTUF9QSTARBgcEAIGYJwEDDAZQU1Bf
QUkwEQYHBACBmCcBBAwGUFNQX0lDDBNGaW5hbnRzaW5zcGVrdHNpb29uDAVFRS1G
STANBgkqhkiG9w0BAQsFAAOCAgEAhYm3S+mdyR837aTYk5ZGyXQ5d9ocSiHFDRnU
j0AxDRCqJ70/jmj0vur4NfAeOC4yz3YXr2jBl0yPBDaXATUQClbMyYr4QIQJLa0Y
FJ2iDQtN4j6jvi1GcWAyvlW8XSzi1y7mv6ggvkomhkkwd6Ua1EYqduxfrmbu7xOH
bPd9QgJJkGCY5JmbNAAEL9W12ErZVR5zbJqieTxnetl6clApE4ZuSXWjhyNCbztk
UJ/9UEZekMiLUCaxiFHQR81UxH7gMzjawTVL54xszddZZ8Tya5J3ayQ5XwpA6t0C
u2UfhND2f/V8M5N0PaEaXJq+ekeE/eXx8cf/lFr3kesGWCN395NLAfG+W+Ov7d5b
GBlnLBaZMcQ70Kv3GNHSFsILllJbv92NK28c00/Gyoo8bekCyMcVKv4rEy9vxLW4
zUkDJOZJ5cM+w1jqgpfQG52rYHpRr1uY0rZx2R8DnXKp+rZ/m6u0uuEqwi5jTk2+
zjXGocNBGdh5JsDlw31n/SmgzCn4KLH+RPHKCRtBB20oZBcy0ApVvMRCA45T5NKY
K1Pavic5YCgg2jM2APN6xH6SCdmzJhipOXmYOwYVYW5QYIl8du52xIhEGSzzCiEJ
zho4eB9Cgti+BJTdYOq0AV3Ad2EUckEkXhVG5u4N5MdhDHqk2gC3IsEpMAwb8J3b
sT3qlI0=
-----END CERTIFICATE-----";

        var lhvhandler = new HttpClientHandler
            {
                ClientCertificateOptions = ClientCertificateOption.Manual,
                ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
            };
        var sert1 = X509Certificate2.CreateFromPem(lhvsert, lhvprivaatvoti);
        lhvhandler.ClientCertificates.Add(sert1);

        var lhv = builder.Services.AddHttpClient(LhvClient);
        lhv.ConfigurePrimaryHttpMessageHandler(() => lhvhandler);
     }
}

Controller:

[Authorize]
public class LhvController(IHttpClientFactory httpClientFactory) : Controller
{
    public async Task<IActionResult> Index()
    { 
        HttpClient client = httpClientFactory.CreateClient(Program.LhvClient);
        client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "application/hal+json;charset=UTF-8");
        client.DefaultRequestHeaders.TryAddWithoutValidation("PSU-Corporate-ID", "PSDEE-LHVTEST-8f20fe");
        client.DefaultRequestHeaders.TryAddWithoutValidation("X-Request-ID", "99391c7e-ad88-49ec-a2ad-99ddcb1f7721");
        client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer Liis-MariMnnik");

        var tulem = await client.GetStringAsync("https://api.lhv.eu/psd2/v1/accounts-list?onlyActive=true");

        return new ContentResult() { Content = tulem, ContentType = "text/json" };
    }
}

In Windows line

var tulem = await client.GetStringAsync("https://api.lhv.eu/psd2/v1/accounts-list?onlyActive=true");

the code throws an error:

No credentials are available in the security package

with stack trace

System.Net.Http.HttpRequestException: An error occurred while sending the request.

System.IO.IOException: The read operation failed, see inner exception.

System.Security.Authentication.AuthenticationException: Authentication failed because the platform does not support ephemeral keys.

System.ComponentModel.Win32Exception (0x8009030E): No credentials are available in the security package

at System.Net.SSPIWrapper.AcquireCredentialsHandle(ISSPIInterface secModule, String package, CredentialUse intent, SCH_CREDENTIALS* scc)
at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(CredentialUse credUsage, SCH_CREDENTIALS* secureCredential)
at System.Net.Security.SslStreamPal.AcquireCredentialsHandleSchCredentials(SslAuthenticationOptions authOptions)
at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions, Boolean newCredentialsRequested)
--- End of inner exception stack trace ---
at System.Net.Security.SslStreamPal.AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions, Boolean newCredentialsRequested)
at System.Net.Security.SslStream.AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions, Boolean newCredentialsRequested)
at System.Net.Security.SslStream.AcquireClientCredentials(Byte[]& thumbPrint, Boolean newCredentialsRequested)
at System.Net.Security.SslStream.GenerateToken(ReadOnlySpan1 inputBuffer, Int32& consumed) at System.Net.Security.SslStream.NextMessage(ReadOnlySpan1 incomingBuffer, Int32& consumed)
at System.Net.Security.SslStream.ProcessTlsFrame(Int32 frameSize)
at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
at System.Net.Security.SslStream.ReplyOnReAuthenticationAsync[TIOAdapter](Byte[] buffer, CancellationToken cancellationToken)
at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory1 buffer, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory1 buffer, CancellationToken cancellationToken)
at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder1.StateMachineBox1.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
at System.Net.Http.HttpConnection.InitialFillAsync(Boolean async)
at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.g__Core|4_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.g__Core|4_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.GetStringAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
at LhvController.Index() in LhvController.cs

Documentation at

https://learn.microsoft.com/en-us/dotnet/core/extensions/sslstream-troubleshooting

recommends to use workarounds described in

https://github.com/dotnet/runtime/issues/23749

It tried workaround from this using

   var sert1 = X509Certificate2.CreateFromPem(lhvsert, lhvprivaatvoti);
    var sert = sert1.Export(X509ContentType.Pfx);

    // laaditud value is null. How to load sert?
    var laaditud = X509CertificateLoader.LoadCertificate(sert);
    lhvhandler.ClientCertificates.Add(laaditud);

    var lhv = builder.Services.AddHttpClient(LhvClient);

    lhv.ConfigurePrimaryHttpMessageHandler(() => lhvhandler);

but

X509CertificateLoader.LoadCertificate(sert);

returns null as described in How to create HttpClient with certificate?

How to use HttpClient with certificates in Windows? In Debian Linux this code works.

You can run this code, it is valid for test environment.

Upvotes: 0

Views: 28

Answers (0)

Related Questions