I would like to be able to use a certificate that I have in memory and not from the "Current User" or "Current Machine" certificate store.
Our desire is to have multiple WCF clients (with the same service) that we connect to and store their public key in a database. Then, by some convention, pick which certificate to encrypt the message with. I do not want to have this stored in the Web.config nor have it stored in the local machines keystore. We are needing this to be agnostic of where it is deployed (Azure vs on-prem vs whatever).
I know that this would work:
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.CurrentUser, StoreName.TrustedPeople, X509FindType.FindByThumbprint, cert.Thumbprint);
But I want to do:
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(x509Certificate);
Is this possible for an app to have it's own keystore, like in Java?
X509CertificateRecipientClientCredential defines a DefaultCertificate property which is settable.
According to http://referencesource.microsoft.com/#System.ServiceModel/System/ServiceModel/Security/X509CertificateRecipientClientCredential.cs,eaccbfb7a59b7226,references the property sets the same backing field as SetDefaultCertificate.
Related
I'm working with X509 certificates and have a MyCert.pfx file that contains the certificate and both public and private keys. It happens that I exported this certificate from a certificate store on my local machine.
My goal is to pretend I'm on a different machine and read the PFX file and make a new X509Certificate2 from the data and password that I have.
I can do that successfully on my own machine as long as I add the UserKeySet flag to the constructor call.
var flags = X509KeyStorageFlags.UserKeySet;
var cert = new X509Certificate2(certKeyData, certPwd, flags);
What bothers me is that my constructor call fails ("Access denied.") without the UserKeySet flag. I know I need the flag to point to my local (personal) certificates in the machine store, but the real question is why the constructor is looking into my personal or machine key stores at all. I'm trying to pretend I'm on a different machine that has no such store.
Q. Should I be able to create an X509Certificate2 WITHOUT looking in a store on my local machine? If not, why not? Isn't the certificate just a pile of bytes that is unrelated to any key store on any particular machine? Can they exist without reference to, or accessing, a local store? Thank you.
I'm currently working on refining communications between mutually authenticated client/server applications using HTTPS. I am currently building validation logic into a C# client to help identify configuration issues when a TLS connection fails. In verifying the connection, I validate that the root CA certificate presented by the server is installed on the client, in the appropriate store, and is valid. I'm using X509Store to pull the X509Certificate2, and validating it using X509Chain.
My issue is that the certificate will report as valid even if the certificate has been disabled via MMC. So the TLS connection will fail, despite the chain reporting as valid.
It's an unlikely case, but one I'd like to handle by reporting something like "Could not connect because root CA is disabled."
Could anyone point me in the direction of a .NET or Win32 call that could be made to determine the value of "Certificate Purposes" for a certificate? Or to read the "Certificate Status" for a cert?
I read through MSDN's listing of what's in the System.Security.Cryptography namespace, and started looking into CryptoAPI and CNG, but didn't find anything so far.
Thanks!
That dialog does not "disable" a certificate it disables it "for all purposes". What this means is it counts as having an empty Enhanced Key Usage extension for purposes of EKU validation.
Normally a root certificate (or an intermediate CA certificate) will not have an EKU extension, so if you do a chain build with any ApplicationPolicy value it will be a match. Once you set it to Disable for all purposes you'll get a chain error X509ChainStatusFlags.NotValidForUsage.
If you want to build something for validating TLS you'd check either the Server Authentication or Client Authentication EKUs (depending on what you're checking):
// Server EKU or Client EKU
Oid eku = forServer ? new Oid("1.3.6.1.5.5.7.3.1") : new Oid("1.3.6.1.5.5.7.3.2");
// Test if it's valid for that purpose
chain.ChainPolicy.ApplicationPolicy.Add(eku);
If you want to "Disable" a root CA altogether, add a copy of the certificate to the Disallowed store (called "Untrusted Certificates" in the UI). That will result in a chain build producing X509ChainStatusFlags.ExplicitDistrust.
I am trying to connect to the remote queues using secured SSL connection. I have all the details provided by third party regarding SSL connection and Queue Manager details. I have V8 version of MQ client installed on my windows machine.
SSL folder that thirdparty has shared contains jks,kdb,rdb and sth files.
I am using below code to initialize the properties in .net console application
const string SslKeyRepository = #"ssl folder location with key name included";
const string CipherSpec = "TLS_RSA_WITH_3DES_EDE_CBC_SHA";
const string CipherSuite = "SSL_RSA_WITH_3DES_EDE_CBC_SHA";
const string SslPeerName = "Peername";
const string ConnectionType = MQC.TRANSPORT_MQSERIES_CLIENT;
static Hashtable init(String connectionType)
{
Hashtable connectionProperties = new Hashtable
{
{MQC.TRANSPORT_PROPERTY, connectionType},
{MQC.PORT_PROPERTY, 1496},
{MQC.SSL_CERT_STORE_PROPERTY, SslKeyRepository},
{MQC.SSL_CIPHER_SPEC_PROPERTY, CipherSpec},
{MQC.SSL_PEER_NAME_PROPERTY, $"CN=\"{SslPeerName}\""}
};
// Add the connection type
// SSL
// Set up the rest of the connection properties, based on the
// connection type requested
switch (connectionType)
{
case MQC.TRANSPORT_MQSERIES_BINDINGS:
break;
case MQC.TRANSPORT_MQSERIES_CLIENT:
case MQC.TRANSPORT_MQSERIES_XACLIENT:
case MQC.TRANSPORT_MQSERIES_MANAGED:
connectionProperties.Add(MQC.HOST_NAME_PROPERTY, HostName);
connectionProperties.Add(MQC.CHANNEL_PROPERTY, Channel);
break;
}
return connectionProperties;
}
I have tried few things but I end up getting exception "MQRC_SSL_INITIALIZATION_ERROR"
I would appreciate if you can help me with this.
There are many reasons for MQRC_SSL_INITIALIZATION_ERROR. Some of them are mechanical issues such as whether the keystore files can be accessed. Some are procedural such as whether the handshake fails. The best way to diagnose is methodically checking the configuration and then performing differential testing.
For the first part of this, perform the following checks. If you have already done so, don't cut corners. Do it again.
Verify that the certificates are accessible by issuing runmqakm -cert -list against the KDB to verify that it is structurally intact and the stash file is present with the proper password.
Verify that the kdb file is not in a world-readable directory is that the files are not marked world-readable.
Verify that the service account that runs the app is the owner of the KDB files and containing folder and has write access. (Not sure why but GSKit insists that the KDB must be writeable at run time.)
Issue runmqakm -cert -details to verify that the certificate(s) corresponding to the queue manager is/are present and the details. If the QMgr uses a self-signed cert there will be only one. If the QMgr uses a CA-signed cert there should be an intermediate and a root signer.
Find out from the 3rd party whether they have specified SSLCAUTH(OPTIONAL) or SSLCAUTH(REQUIRED). If OPTIONAL then the KDB should have no personal certs, only signers. If REQUIRED then the KDB must have a personal cert and the label must be ibmwebspheremq[serviceaccount] in lower case.
For the differential testing, try some of the following tests:
Test the app by connecting to a local QMgr using TLS (Note: MQ hasn't used SSL for years. It's TLS now. The old field names still retain SSL labels, though.) until you know that it is correctly configured. Go grab a copy of MQ Advanced for Developers and you can do integration testing on the desktop with your own QMgr, fully licensed for free.
Test using one of the sample programs. Use amqsputc or amqsgetc, depending on whether the real app is supposed to have PUT or GET on the queue. These use the same KDB, samme certs, etc. the main difference being they are known-good code.
Ask your business partner to let you test without SSL to make sure the "mechanical" parts of the configuration are correct. This includes things like the firewall routing, host, port and channel name, QMgr name, etc. If you can't connect with plaintext channels, you definitely won't succeed with TLS channels.
Once that works, test with SSL enabled and SSLCAUTH(OPTIONAL) set at the QMgr. This demonstrates that the client can validate the QMgr's cert.
Once that works, and if the objective is to use mutual authentication, test with SSLCAUTH(REQUIRED) set at the QMgr and a personal cert in the local KDB. This demonstrates that the QMgr can validate the client's cert.
Then, and only then, turn on SSLPEER locally to filter on the QMgr cert's DN.
If these don't help, please update the question with detailed results of your testing. The most common issues include cert labels and KDB permissions. If the business partner gave you the JKS and KDB, these should generally not come with a personal cert, only trusted certs.
I've crafted a WCF service that accesses a remote CA using CERTENROLL and CERTCLIENT libs. My GenerateCertificate request takes a password that's used in the enrollment request. I return metadata about the generated cert and the Base64 string of the raw bytes in the response XML.
If I take the raw cert string and save it as a local .pfx file I can install it easy breezy on a Win10 or 2012 machine. However, when I try it on a Win7 or 2008 machine the password doesn't match.
I'm supplying an X509CertificateRequestCmc request that's intitialized from an inner X509CertificateRequestPkcs10 request which is initialized from a template.
Upon first encountering this error I tried to apply an OS Version CryptAttribute to either one of the request layers without success.
CX509AttributeOSVersionClass os = new CX509AttributeOSVersionClass();
os.InitializeEncode("6.1.7601");
CX509AttributesClass attribs = new CX509AttributesClass();
attribs.Add((CX509Attribute)os);
CCryptAttributeClass atty = new CCryptAttributeClass();
atty.InitializeFromValues(attribs);
request.CryptAttributes.Add(atty);
I'm only responsible for the WebService tier, I don't have access to the PKI store directly. My dev server app pool has been set to run as an allowed AD account on the PKI server and I don't have that account's password.
Is it me or the store?
I invoked a ticket to MS on this. They said there's a known issue where Base64 pfx files will not install on Win7 & 2008 systems. The answer is to decode the Base64 and install via binary. Careful not to convert to .cer, you'll lose the private key.
I'm trying to digitally sign a PDF document using Syncfusion PDF 10.4, like so:
PdfLoadedDocument document = new PdfLoadedDocument(inputStream);
PdfCertificate certificate = PdfCertificate.FindBySubject(certificateStoreType, certificateSubjectName);
PdfSignature signature = new PdfSignature(document, document.Pages[0], certificate, "Signatur");
signature.Bounds = new RectangleF(new PointF(5, 5), new SizeF(100, 100));
This works great for my local user account after installing a suitable certificate using MMC (adding the Certificates snap-in for My user account and storing it in Personal), but not for a service (choosing Service account this time, and picking my service). Running the same code results in no suitable certificate being found, i.e. certificate is null. Furthermore, PdfCertificate.GetCertificates() throws an AccessViolationException, which I assume is a bug on Syncfusion's end.
I can, however, reproduce the same problem without Syncfusion code:
var store = new System.Security.Cryptography.X509Certificates.X509Store("My");
store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);
foreach (var item in store.Certificates)
{
…
}
Run as my own user, the certificate shows up (as do all the others shown in MMC under Personal), but if I debug the service (by running it, then invoking System.Diagnostics.Debugger.Launch()), I only get a "CN=LOCAL SERVICE" certificate, which doesn't show up in MMC at all.
I'm assuming that I need to A) tell it to open the correct certificate store, or B) change something about the way the service is installed or run, such as giving it a different identity, enabling UserInteraction, etc. Currently, it runs using LocalService and with UserInteraction disabled.
From what I remember, Windows machine accounts (like LocalService) use the machine certificate store. This means that in your code, you have to access the store with StoreLocation.LocalMachine.
var store =
new System.Security.Cryptography.X509Certificates.X509Store(StoreLocation.LocalMachine);
Note that if you decide to run the service under specific identity, you should rather first login as the identity, then import the certificate to the Personal store and then, use StoreLocation.CurrentUser.
The answer appears to be that .NET doesn't support accessing service account certificate stores without P/Invoke or the like:
I don't think that any of the .NET APIs allow access to the Services Certificate store.
However, you can install the certificate into the CurrentUser store of the account that the service runs under.
I've changed the service to run under its own user (which doesn't need admin rights), ran mmc.exe as that user using runas, and imported the certificate to that user's personal store.
I ran into this problem, and to solve it had to allow the "Local Service" account to access the "Local Computer" certificate store using the tool "WinHttpCertCfg"
It is described in detail here:
https://support.microsoft.com/en-us/help/901183/how-to-call-a-web-service-by-using-a-client-certificate-for-authentication-in-an-asp-net-web-application