Update 2:
Got the complete certificate chain from Swish support and the production servers root cert. It still does not work. Works fine locally and on VM with all updates as well.
Tested with ServicePointManager.Expect100Continue = false;
Setting the following values in Web.config
:
<system.web>
<compilation debug="true" targetFramework="4.8"/>
<httpRuntime targetFramework="4.8" enableVersionHeader="false" maxRequestLength="102400" executionTimeout="3600"/>
Loading certificates:
var certThumbprint = "1234"; //1234567890 Swish number cert
var root1Thumbprint = "1234"; //Handelsbanken Customer CA1 v2 for Swish
var root2Thumbprint = "1234"; //Handelsbanken Root CA v2 for Swish
var root3Thumbprint = "49f2334991c4a48dfc6862095e965229b7cee457"; //Swish Root CA v2
var root4Thumbprint = "A8985D3A65E5E5C4B2D7D66D40C6DD2FB19C5436"; //DigiCert Global Root CA
var thumbprints = new[] { certThumbprint, root1Thumbprint, root2Thumbprint, root3Thumbprint, root4Thumbprint };
Exception is now:
{
"Message": "An error has occurred.",
"ExceptionMessage": "One or more errors occurred.",
"ExceptionType": "System.AggregateException",
"StackTrace": " at Project.Web.Services.SwishService.SendPaymentRequest(PaymentRequest paymentRequest) in C:\\Users\\oscar\\source\\repos\\Project\\src\\Project.Web\\Services\\SwishService.cs:line 131\r\n at Project.Web.Services.SwishService.PopulateAndSendPaymentRequest(String userMobileNumber, Contract contract) in C:\\Users\\oscar\\source\\repos\\Project\\src\\Project.Web\\Services\\SwishService.cs:line 159\r\n at Project.Web.Controllers.Employees.EmployeeController.<SignAndPayContract>d__16.MoveNext() in C:\\Users\\oscar\\source\\repos\\Project\\src\\Project.Web\\Controllers\\Employees\\EmployeeController.cs:line 256",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "An error occurred while sending the request.",
"ExceptionType": "System.Net.Http.HttpRequestException",
"StackTrace": null,
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "The underlying connection was closed: An unexpected error occurred on a send.",
"ExceptionType": "System.Net.WebException",
"StackTrace": " at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)\r\n at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.",
"ExceptionType": "System.IO.IOException",
"StackTrace": " at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)\r\n at System.Net.PooledStream.EndWrite(IAsyncResult asyncResult)\r\n at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "An existing connection was forcibly closed by the remote host",
"ExceptionType": "System.Net.Sockets.SocketException",
"StackTrace": " at System.Net.Sockets.Socket.BeginReceive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, AsyncCallback callback, Object state)\r\n at System.Net.Sockets.NetworkStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)"
}
}
}
}
}
Update:
More than six years later I had to do this again and it was still not straight forward. However now it should finally work in an Azure Web App and it seems to have been working for a while looking at @AndersEkdahl answer.
The developer environment has a lot of good information and certificates:
https://developer.swish.nu/documentation/environments
Production certificates can be downloaded here:
https://portal.swish.nu/company/login?redirectPath=/company/certificates
When I had completed the Swish Certificate Request tutorial on https://portal.swish.nu and downloaded my .pem
file I completed it via IIS Manager
and "Complete Certificate Request". Of course on the same computer that created the certificate request.
My downloaded .pem
file was called:
swish_certificate_202306201123.pem
After doing this I could export my certificate to .pfx
.
However this did not work neither locally or in my Azure Web App. If I tried to use the certificate I got the same error as in this question:
Could not create SSL/TLS secure channel
Looking at the certification path this became quite clear since the issuer could not be found.
Go back to the swish_certificate_202306201123.pem
file and open this is notepad or similar text editor. You should see three -----BEGIN CERTIFICATE-----
and -----END CERTIFICATE-----
. Create one file for each of these certificates. I named them 1.cer
, 2.cer
and 3.cer
. 1.cer
is the certificate you have already imported. However 2.cer
and 3.cer
needs to be imported to Trusted Root Certification Authorities
.
After doing this the certificate chain is now trusted.
Now I could follow Microsoft guide for Use a TLS/SSL certificate in your code in Azure App Service - Load certificate in Windows apps
The code for loading a certificate is from Microsoft:
Example:
string certThumbprint = "E661583E8FABEF4C0BEF694CBC41C28FB81CD870";
bool validOnly = false;
using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(
X509FindType.FindByThumbprint,
// Replace below with your certificate's thumbprint
certThumbprint,
validOnly);
// Get the first cert with the thumbprint
X509Certificate2 cert = certCollection.OfType<X509Certificate2>().FirstOrDefault();
if (cert is null)
throw new Exception($"Certificate with thumbprint {certThumbprint} was not found");
// Use certificate
Console.WriteLine(cert.FriendlyName);
// Consider to call Dispose() on the certificate after it's being used, avaliable in .NET 4.6 and later
}
Now Swish for production worked locally.
However after uploading the .pfx
file and the two .cer
files from Trusted Root Certification Authorities
and including them all via WEBSITE_LOAD_CERTIFICATES
it still did not work.
I verified that the app could load my certificates via Development Tools -> Advanced Tools -> Debug console:
dir cert:/CurrentUser/My
Code for loading certificates looks like this:
var thumbprints = new[] { certThumbprint, root1Thumbprint, root2Thumbprint };
var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
var certificates = new X509Certificate2Collection();
foreach (var thumbprint in thumbprints)
{
var certs = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
certificates.AddRange(certs);
}
var handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
return true;
}
};
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
foreach (var cert in certificates)
{
handler.ClientCertificates.Add(cert);
}
client = new HttpClient(handler);
When running this code locally I had to copy the two new certificates in Trusted Root Certification Authorities
to Personal
since the certificate chain is fetched from there.
I thought handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
would be the error but I suspect I need to load Swish Root CA v2
.
Source for troubleshooting:
https://johan.driessen.se/posts/Calling-the-Swish-Payment-API-from-Azure-AppService/
I have contacted Swish support about the missing certificate chain since they state on their web page:
The complete certificate chain of the Swish server TLS certificate is available through Swish Certificate Management.
I can not find it there though.
If you decide to host via VM/IIS remember to use StoreLocation.LocalMachine
and grant access to the certificate for the user running the application pool.
https://serverfault.com/a/132791/293367
I solved it like this to use StoreLocation.CurrentUser
locally:
var swishCertificateStoreLocation = ConfigurationManager.AppSettings["SwishCertificateStoreLocation"];
Enum.TryParse(swishCertificateStoreLocation, out StoreLocation storeLocation);
var certStore = new X509Store(StoreName.My, storeLocation);
Original:
Before writing this question I have gone through a lot of questions and answers but I can't seem to find a solution. What I'm trying to do is host an application as a Azure App Service that needs to make a call to the Swish API.
Please see this thread for how my implementation runs locally which works fine:
System diagnostics log from Azure:
I have tried the solutions from Microsoft forums and SO but none seem to do the trick:
//Tested both, none of them work
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11
Since a lot of the questions are based on accessing an external service and not sending a client certificate the complexity rises a bit as well.
What I have done is in the SSL certificates
tab on Azure import the Test certificate. Since .p12
and .pfx
are both PKCS #12 files I just renamed the .p12
-file. The application runs as B1 Basic
App Service Plan so most functionality should be present.
https://stackoverflow.com/a/6821061/3850405
I have also tried this guide to add the certificate to the certificate store in Azure -> Application settings -> App Settings:
https://azure.microsoft.com/en-us/blog/using-certificates-in-azure-websites-applications/
When this did not work I tried to add WEBSITE_LOAD_CERTIFICATES
to appSettings
in my application but it resulted in a HTTP 503.
Swish certificate and English guide:
https://www.getswish.se/content/uploads/2015/06/Guide-Testverktyg_20151210.zip