WCF Certificate Authentication without installing on the Client - wcf-security

Our setup includes a WCF service and a number of clients written by us. Some of the clients include Silverlight applications, whereas others include Web and Windows applications.
I (think) I would like to authenticate clients based on X.509 certificates. Typically you would install a private key on the client to encrypt (aka digitaly sign) the messages. The server can the use the clients public key to de-crypt it to ensure the message has not been changed and prove the message is from who we expect (aka authenticated).
I dont want to install a certificate on a client machine. Its a hassel to deploy, and we cant really ask our clients to do it. I was speaking to someone the other day who sugested embeding the cert in a client assembly, reading it and using that. Is that possible?
It would be great if someone could point me to an example.
Thanks in advance,
David

Yes, you can load X509certificate2 by passing a certificate byte array with a password like
var certificate = new X509Certificate2(theByteArrary, "password");
To get the certificate byte array, you can simply copy paste the contents in .pfx file, which is a combination of .cer (public key) and .pvk (private key)
and then you can load this certificate on your client by doing:
var channelFactory = new ChannelFactory<IYourService>();
channelFactory.Credentials.ClientCertificate.Certificate =
clientCertificate;
If you use auto-generated client proxy, or you prefer configure the certificate via .config file then you might want to have a look at this from codeproject

Here is a suggestion. Could also be tweaked to use an embedded certificate.
http://www.codeproject.com/KB/WCF/wcfcertificates.aspx

Related

How should I sign a CSR using a signature created in HSM, in C# .NET Core?

I'm exhausted after looking for an answer for 3 days. I don't know if my suggested flow is wrong or my Google skills have really deteriorated.
My API needs to create a valid certificate from a CSR it received, by signing it with a private key that exists ONLY inside an HSM-like service (Azure KeyVault), which unfortunately doesn't offer Certificate Authority functions BUT does offer signing data with a key that exists there. My CA certificate's private key is stored in the HSM. I'm using ECDSA.
My suggested flow:
Client generates Key Pair + CSR and sends CSR to API
API creates a certificate from the CSR
API asks HSM to sign the CSR data and receives back a signature
API appends the signature to the certificate and returns a signed (and including CA in chain) certificate to the Client
I'm using C# .NET Core and would like to keep it cross-platform (as it runs in Linux containers), so I have to keep it as native as possible or using Bouncy Castle (which I'm still not sure if runs in Linux .NET Core).
I really appreciate your help!
I had faced a similar issue and found a solution. You'll have to use the PKCS11Interop.X509Store library.
The solution uses dotnet core native System.Security.Cryptography.X509Certificates.CertificateRequest::Create method
for generating a certificate.
As per the docs:
Pkcs11Interop is managed library written in C# that brings the
full power of PKCS#11 API to the .NET environment
Pkcs11Interop.X509Store is managed library built on top of
Pkcs11Interop. It's main goal is to provide easy to use PKCS#11 based
read-only X.509 certificate store that can be easily integrated with
standard .NET ecosystem.
Till v0.3.0, implementation for issuing a certificate (i.e signing a CSR) is not available.
With minor modifications in the PKCS11Interop library, I was able to sign the CSR.
Mentioned in Issue #30, the code is now added in the PKCS11Interop.X509Store library version 0.4.0.
The below code is taken from test cases for BasicEcdsaCertificateRequestTest. Test cases for RSA CertificateRequest are also there.
// Load PKCS#11 based store
using (var pkcs11Store = new Pkcs11X509Store(SoftHsm2Manager.LibraryPath, SoftHsm2Manager.PinProvider))
{
// Find signing certificate (CA certificate)
Pkcs11X509Certificate pkcs11CertOfCertificateAuthority = Helpers.GetCertificate(pkcs11Store, SoftHsm2Manager.Token1Label, SoftHsm2Manager.Token1TestUserEcdsaLabel);
// Generate new key pair for end entity
ECDsa ecKeyPairOfEndEntity = ECDsa.Create(ECCurve.NamedCurves.nistP256);
// Define certificate request
CertificateRequest certificateRequest = new CertificateRequest(
new X500DistinguishedName("C=SK,L=Bratislava,CN=BasicEcdsaCertificateRequestTest"),
ecKeyPairOfEndEntity,
HashAlgorithmName.SHA256);
// Define certificate extensions
certificateRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true));
certificateRequest.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));
certificateRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, false));
// Issue X.509 certificate for end entity
X509Certificate2 certificateOfEndEntity = certificateRequest.Create(
pkcs11CertOfCertificateAuthority.Info.ParsedCertificate.SubjectName,
X509SignatureGenerator.CreateForECDsa(pkcs11CertOfCertificateAuthority.GetECDsaPrivateKey()),
DateTimeOffset.UtcNow,
DateTimeOffset.UtcNow.AddDays(365),
new BigInteger(1).ToByteArray());
// Verify signature on X.509 certificate for end entity
Assert.IsTrue(CaCertSignedEndEntityCert(pkcs11CertOfCertificateAuthority.Info.ParsedCertificate.RawData, certificateOfEndEntity.RawData));
// Asociate end entity certificate with its private key
certificateOfEndEntity = certificateOfEndEntity.CopyWithPrivateKey(ecKeyPairOfEndEntity);
// Export end entity certificate to PKCS#12 file
string basePath = Helpers.GetBasePath();
string pkcs12FilePath = Path.Combine(basePath, "BasicEcdsaCertificateRequestTest.p12");
File.WriteAllBytes(pkcs12FilePath, certificateOfEndEntity.Export(X509ContentType.Pkcs12, "password"));
}
Hope this helps.

BizTalk 2016: How to use HTTP Send adapter with API token

I need to make calls to a rest API service via BizTalk Send adapter. The API simply uses a token in the header for authentication/authorization. I have tested this in a C# console app using httpclient and it works fine:
string apiUrl = "https://api.site.com/endpoint/<method>?";
string dateFormat = "dateFormat = 2017-05-01T00:00:00";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("token", "<token>");
client.DefaultRequestHeaders.Add("Accept", "application/json");
string finalurl = apiUrl + dateFormat;
HttpResponseMessage resp = await client.GetAsync(finalurl);
if (resp.IsSuccessStatusCode)
{
string result = await resp.Content.ReadAsStringAsync();
var rootresult = JsonConvert.DeserializeObject<jobList>(result);
return rootresult;
}
else
{
return null;
}
}
however I want to use BizTalk to make the call and handle the response.
I have tried using the wcf-http adapter, selecting 'Transport' for security (it is an https site so security is required(?)) with no credential type specified and placed the header with the token in the 'messages' tab of the adapter configuration. This fails though with the exception: System.IO.IOException: Authentication failed because the remote party has closed the transport stream.
I have tried googling for this specific scenario and cannot find a solution. I did find this article with suggestions for OAUth handling but I'm surprised that even with BizTalk 2016 I still have to create a custom assembly for something so simple.
Does anyone know how this might be done in the wcf-http send adapter?
Yes, you have to write a custom Endpoint Behaviour and add it to the send port. In fact with the WCF-WebHttp adapter even Basic Auth doesn't work so I'm currently writing an Endpoint Behaviour to address this.
One of the issues with OAuth, is that there isn't one standard that everyone follows, so far I've had to write 2 different OAuth behaviours as they have implemented things differently. One using a secret and time stamp hashed to has to get a token, and the other using Basic Auth to get a token. Also one of them you could get multiple tokens using the same creds, whereas the other would expire the old token straight away.
Another thing I've had to write a custom behaviour for is which version of TLS the end points expects as by default BizTalk 2013 R2 tries TLS 1.0, and then will fail if the web site does not allow it.
You can feedback to Microsoft that you wish to have this feature by voting on Add support for OAuth 2.0 / OpenID Connect authentication
Maybe someone will open source their solution. See Announcement: BizTalk Server embrace open source!
Figured it out. I should have used the 'Certificate' for client credential type.
I just had to:
Add token in the Outbound HTTP Headers box in the Messages tab and select 'Transport' security and 'Certificate' for Transport client credential type.
Downloaded the certificate from the API's website via the browser (manually) and installed it on the local servers certificate store.
I then selected that certificate and thumbprint in the corresponding fields in the adapter via the 'browse' buttons (had to scroll through the available certificates and select the API/website certificate I was trying to connect to).
I discovered this on accident when I had Fiddler running and set the adapter proxy setting to the local Fiddler address (http://localhost:8888). I realized that since Fiddler negotiates the TLS connection/certificate (I enabled tls1.2 in fiddler) to the remote server, messages were able to get through but not directly between the adapter and the remote API server (when Fiddler WASN'T running).

WCF Message Level Security using Certificate (X.509) Message Signed and Encrypted

I have a common use case to implement a web service (WCF) and a web client which is consuming this web service. Both Client and Service are sending and receiving Signed and Encrypted messages (Message Level Security).
I can have multiple clients those are using my service, hence multiple client certificate need to be installed on Server where Service project is running.
I have installed 2 Certificates (Service Certificate and Client Certificate) in local machine store under personal and trusted root certification authorities.
makecert -sr LocalMachine -ss My -a sha1 -n "CN=WCFServer" -len 2048 -sky exchange -pe
makecert -sr LocalMachine -ss My -a sha1 -n "CN=WCFClient" -len 2048 -sky exchange -pe
Although Certificate give me error of "integrity of licence can not be guaranteed" but now i dont care about this issue.
Web Client need to Sign the request message through its own Private Key and Encrypt message through Service Certificate Public key.
Service receive the request and verify the signature of message through client Public Key and Decrypt the contents through own Private Key
Than process the request and create a response object.
Now Service must sign the response by own private key and encrypt the message through client public key.
Now client get Signed and Encrpted response. Now it verify the response through Service Public Key and Decrypt the message by its own private key.
This is whole Szenerio . I have installed both(Client and Service) X.509 Certificates on my local development machine. I am using wsHttpBinding and communication is working fine.
I am using [ServiceContract(ProtectionLevel=ProtectionLevel.EncryptAndSign)] for ServuceContract and [OperationContract(ProtectionLevel = ProtectionLevel.EncryptAndSign, IsOneWay = false)] for OperationContract.
I have following question:
do i need to Sign and Encrypt/Decrypt Request or Response message in my Code (once in client and once in service code) or Configurations in web.config on Service Project and Client project are enough to do all this stuff?
I go through following code project and its working fine.
http://www.codeproject.com/Articles/36683/simple-steps-to-enable-X-certificates-on-WCF
My Source code (Visual Studio Project) Including both certificates is available on Bitbucket for download here
Can any one tell me, do i need to do all this stuff in code or its done through my current configurations.
Thanks in advance.
i got following answer from MSDN forum.
do i need to Sign and Encrypt/Decrypt Request or Response message in my Code (once in client and once in service code) or Configurations in web.config on Service Project and Client project are enough to do all this stuff?
If we have configured the service to use the certificate authentication in the config file, then as you said all the response and request message will be Encrypted/Decrypted by the client certficate's private key/ public key and service certificate's private key/ public key. So in my mind it is enough to do all the stuff in the configure file.
For more information, please try to refer to the following articles:
Certificate authentication:
https://msdn.microsoft.com/en-us/library/ff648360.aspx .
Message and Transport Security:
https://msdn.microsoft.com/en-us/library/ff648863.aspx .
i have few more questions which may be any one can answer me.
I have multiple clients (external web applications ) which will access my service. do each client need to create their own certificate? client will deliver us certificate without private key which need to be install on Service Host server? is this a correct way?
Each client certificate need to be configured in web.config ?
I need to export my service certificate without private key and send to clients. clients must install and configure certificate on their application server? is this correct?
here i got another ver good answer (step by step guide) from MSDN Team.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/a9d85e9f-6d35-425c-ac6d-b3cd883760e4/wcf-message-level-security-using-certificate-x509-message-signed-and-encrypted?forum=wcf&prof=required

SSL Certificate will not save

I am trying to install an SSL Certificate (from GoDaddy.com) onto IIS 7. I can add the certificate following the directions found here and it shows up in my list of Server Certificates but once I leave the Server Certificates page and return to it, the certificate no longer shows up in the list.
This certificate is also used on an Apache box to authenticate our website (I am using IIS for a WCF Service API). The certificate is assigned to *.mydomain.com so I thought I could use it for the Apache website server (www.mydomain.com) as well as my API (services.mydomain.com). Am I incorrect in this assumption? Do I need to do something different then the instructions I referenced above to install?
Brian,
I assume you just grabbed the .cer file and imported this onto your IIS box. Sadly this is not possible as your certificate will not contain a private key so it won't be an option in IIS.
The GlobalSign instruction you referenced are fine but that is if you created the CSR on the IIS server itself.
What you will have to do is to create a PFX from from your .key and .cer files from the Apache server and then you can import this PFX onto your IIS Server followed by assigning it for use.
So use this to create your PFX file;
https://support.globalsign.com/customer/portal/articles/1229676-back-up-certificate---apache-openssl
Followed by;
https://support.globalsign.com/customer/portal/articles/1217281
As for everyone else if you just have a .CER file you and did not create the request you need a private key. So create a new CSR and request a reissue from your relevant CA.
The fact you cannot assign a certificate in IIS is because you do not have a private key that corresponds to the certificate.
You have two options:
If you have access to the old machine you first created the certificate just go to that and export ".pfx" file from the certificate, then in the new machine you could easily import it and Done.
If you don't have access to the old machine, so you must reissue the SSL certificate with the help of the seller company.
Hope this help.
See how godaddy response for this case:
https://sg.godaddy.com/community/SSL-And-Security/completing-certificate-request-disappears-from-server/td-p/36299
But it doesn't work.
I tried in my way :
- Use https://www.sslshopper.com/ssl-converter.html to convert my CRT file to PFX file.
- Then go to the IIS -> Server certificates -> Import -> Choose the generated PFX file in previous step.
It worked with me.

Certificate validation failed

Actors
-Asp.net site - Client
-Wcf services - Server
Both applications runs on IIS-7.
I want to make integration test between the two applications. The client access the Server through 'https'.
I have created a certificate and assigned it to the server. I also added the certificate to the 'Trusted Root Certification Authorities' to be considered a valid certificate. When I 'hit' the server's services through my browser (IE, chrome...) the certificate appears to be valid. But when my client application tries to access the server then I get the following error:
Could not establish trust relationship for the SSL/TLS secure channel with authority **** --->
The remote certificate is invalid according to the validation procedure.
Is there any way to skip the validation procedure or to make the certificate valid for my client application?
Just to know:
1. I cannot purchase a certificate because I will only use it for testing purposes.
2. I cannot make any changes on any of the application's code (server-client)
I finally managed to figured it out.
The problem was a previous (expired) certificate with the same name that was already added to the 'Trusted Root Certification Authorities'. Every time I was installing my new certificate through the 'Certificate Import Wizard' (or through MMC) the wizard informed me that it was successfully added. However, it was keeping the instance of the previous certificate without overwriting it.
Modify the validation callback to always return true:
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, policyErrors) => true;
Or does that violate the 'no changes to code' condition?
How did you install the certificate into your trusted root store?
If you went through a browser to do it, most likely you only added it to the current user. Try adding it through the MMC snap-in for the Local Computer Account instead; this is where we install our self-signed IIS Express certificates and WCF seems happy with them.

Resources