Get a client certificate off of a DoD CAC Card - https

I have a C application that uses LibCurl (LibCurl is a C API that makes an HTTP connection to a web server). Using LibCurl I need to download a file from an HTTPS server that requires a client certificate.
So far our technical solution works great.
My problem is that the client certificate that we need to use resides on a DoD CAC card. I need to be able to pull the client certificate off of the DOD CAC card (from within my C app) and either write it to a file or just reference the file on the CAC. This written or referenced file will then be specified as my client certificate in my HTTPS connection.
I do not know how to locate or reference the client certificate off the DoD CAC Card. Any help is very much appreciated. Thanks.

When activeClient publishes the CAC card certs to windows it should export the certs to the store. You may need to automate the exporting of the certificate from your local cert store to a file like .pfx or .p7b format. Maybe .cer, I don't know if that's possible. It'll need to be pwd protected.
I don't think you can do it directly from the CAC card without an intermediary middle layer (like the cert store).

This is the method for C# it may help with C I'm really not familiar with C code.
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
private static X509Certificate GetClientCert()
{
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySubjectName, "Integration Client Certificate", true);
if (certs.Count == 1)
{
var cert = certs[0];
return cert;
}
}
finally
{
if (store != null)
store.Close();
}
return null;
}
The code to get and export the cert is
//This will bring up the selection prompt to select your cert
X509Certificate c = GetClientCert();
//The password should be the pin converted to a secure string variable.
//note the code above will not prompt for a pin if you want this you will have to build the prompt yourself. It will only select the certificate.
c.Export(X509ContentType.Cert, securestring password);
The export method has various types to export to I am not sure if one will be the format you are referring to. This is something you will need to play with. I am not even sure you will be able to use those libraries in C but just in case you could I posted them.

Related

PCF Container TrustManager adds certificates, but TrustManagerFactory loads certificates twice

I run a spring boot app on PCF with an apache-httpclient 4.x. The client creates a ssl context:
final SSLContext sslcontext = SSLContext.getInstance(algorithm);
sslcontext.init(keymanagers, trustmanagers, params.getSecureRandom());
I get the trustmanagers as follows:
final TrustManagerFactory tmfactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init((KeyStore) null);
I log the number of certificates it finds and I log the found CN's. However I found it list every certificate twice!
If I provide a keystore with 1 certificate:
tmfactory.init((KeyStore) truststore);
It will log that certificate + all environment certificates. As follows:
private static void logAcceptedIssuers(TrustManager[] trustmanagers) {
log.info("Adding the following trusted certificates to the SSL context: ");
Arrays.stream(trustmanagers)
.filter(X509TrustManagerWrapper.class::isInstance)
.map(X509TrustManagerWrapper.class::cast)
.map(X509TrustManagerWrapper::getAcceptedIssuers)
.forEach(SSLContextFactory::logAcceptedIssuers);
}
private static void logAcceptedIssuers(final X509Certificate[] certificates) {
final int certificatesCount = certificates.length;
final String prefix = "Trusted certificates (total=" + certificatesCount + "): \n";
final String certDNs = Arrays.stream(certificates)
.map(X509Certificate::getSubjectDN)
.map(Principal::getName)
.map(SSLContextFactory::extractCommonName)
.collect(Collectors.joining(" |#| ", prefix, "\n"));
log.info(certDNs);
}
#VisibleForTesting
static String extractCommonName(String principalName) {
... Some code for extracting commonname from principal name...
return cn;
}
Where does the TrustManagerFactory find the pcf trusted certificates?
How can I check if those pcf certificates are available & loaded, or where can I get the pcf-certificates-only TrustManager. My worry is that it might cause issues if I load it twice, but I have no indication it is causing issues (but now I have 288 instead of 144 certificates in my SSLContext, does that impact performance? Can it cause issues?)).
Regards,
Rick
The Java buildpack adds a component called the Container Security Provider. This is what adds the functionality to automatically load Platform/Bosh trusted CA certificates, as well as the container instance ID cert and key.
To ensure platform/Bosh trusted CA certificates are trusted, the Container Security Provider adds a TrustManagerFactory. This looks at the file /etc/ssl/certs/ca-certificates.crt and trusts everything in that file.
This is in addition to the default behavior of the JVM.
I log the number of certificates it finds and I log the found CN's. However I found it list every certificate twice!
You didn't share the code to show how you're doing this, so I can only speculate. My suspicion is that you're seeing duplicates because the Container Security Provider does not replace the default behavior for trusting CA certs in the JVM, it adds an additional TrustManager.
Pair that with the fact that there is probably a lot of overlap between what's in /etc/ssl/certs/ca-certificates.crt and what's in the JVM's default trust store. I guess I could see there being duplicates.
If I provide a keystore with 1 certificate:
It will log that certificate + all environment certificates.
This further's my suspicion because when you override the default JVM truststore, it sounds like the duplicates go away. That would mean you're left with your custom truststore plus what's added by the CSP.
Where does the TrustManagerFactory find the pcf trusted certificates.
https://github.com/cloudfoundry/java-buildpack-security-provider/blob/main/src/main/java/org/cloudfoundry/security/CloudFoundryContainerTrustManagerFactory.java#L41
How can I check if those pcf certificates are available & loaded, or where can I get the pcf-certificates-only TrustManager. I want this to prevent loading the certificates twice.
Is there a larger problem here? I get what you're saying about seeing certificates listed twice, but is that actually causing a problem with your application? If so, please update your question and explain what is happening? That might help to provide some more context.
Aside from that:
You can disable the CSP. Set and env variable JBP_CONFIG_CONTAINER_SECURITY_PROVIDER='{trust_manager_enabled: false}'.
https://github.com/cloudfoundry/java-buildpack/blob/main/docs/framework-container_security_provider.md#security-provider
Do understand that by disabling this, your application will not automatically trust platform/Bosh deployed CA certificates. This could cause failures. For example, if your application is binding to a CF marketplace service that is using certificates signed by a CA that is distributed through the platform/Bosh trusted CA certificates.
You could do the opposite & set the default Java truststore to point to an empty keystore. You'd need to create an empty keystore, probably under your application, then set -Djavax.net.ssl.keyStore=/home/vcap/app/empty.jks (/home/vcap/app is the root of your application, change the rest to point to where you store the empty keystore file).
https://stackoverflow.com/a/2138670/1585136
If my suspicion is true, either of those would result in the duplicates going away.

How do I find my server's public key info to do certificate pinning in OkHttp?

I have a server with a custom certificate on it issued by my own personal certificate authority. It's not on a public domain so it's not possible to use a standard certificate authority. I want to get an Android client to connect to this server using OkHttp. According to the docs, the code should look like this:
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("myserver.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
.build();
Where the SHA256 hash is, quoting the docs: "a hash of a certificate’s Subject Public Key Info, base64-encoded and prefixed with either sha256/ or sha1/". I have the cert and the key files for the server, but how can I get the required hash?
I'm using OkHttp 3.
Try making a request to your server with the configuration above to see how that fails. The exception should tell you which pins were found.
You must do certificate pinning in coordination with your server team! Otherwise a change they make will prevent your client from being able to reach the server.

How to disable credentials input for HTTPS call to my WCF hosted in windows service

I'm just creating my first WCF project, so I have a lot of deficiencies in knowledge in this field. My problem is that when I'm calling my WCF url in web browser, I have to enter the credentials but I cannot even use my domain name and password, but I have to choose my personal chip card certificate and enter it's password. After that, everything work like a charm.
My final product should be installed on every user workstation in our domain for IT operations purposes only. So there will be some AD authorization after that.
About certificate... We have our own company root CA certificate, and every workstation have it's own certificate which is it's grandchild:
Example of our certificate tree:
COMPANYROOTCA >> COMPANYSUBCA1 >> WORKSTATIONNAME.DOMAIN (this one is used as WCF service cert)
This is what I have right now for hosting the WCF in my Windows service running under NetworkService Account:
serviceHost.Dispose(); //extension for close() and set to null
Uri httpsUrl = new Uri("baseAdress");
serviceHost = new ServiceHost(typeof(Service.myService), httpsUrl);
WSHttpBinding wsHttpBinding = new WSHttpBinding();
wsHttpBinding.Security.Mode = SecurityMode.Transport;
wsHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
wsHttpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
WebHttpBinding webHttpBinding = new WebHttpBinding();
webHttpBinding.Security.Mode = WebHttpSecurityMode.Transport;
webHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
webHttpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
ServiceMetadataBehavior smb = new ServiceMetadataBehavior
{
HttpGetEnabled = false,
HttpsGetEnabled = true,
};
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = store.Certificates;
X509Certificate2 cert = collection.OfType<X509Certificate2>().First(c => c.SubjectName.Name == "CN=WorkstationName.Domain");
store.Close();
serviceHost.Credentials.ServiceCertificate.Certificate = cert;
ServiceThrottlingBehavior throttleBehavior = new ServiceThrottlingBehavior
{
MaxConcurrentCalls = 16,
MaxConcurrentInstances = 26,
MaxConcurrentSessions = 10
};
serviceHost.Description.Behaviors.Add(throttleBehavior);
ServiceEndpoint soapEndpoint = serviceHost.AddServiceEndpoint(typeof(Contract.IMyService), wsHttpBinding, "soap");
ServiceEndpoint restEndpoint = serviceHost.AddServiceEndpoint(typeof(Contract.IMyService), webHttpBinding, "rest");
ServiceEndpoint mexEndpoint = serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpsBinding(), "mex");
restEndpoint.Behaviors.Add(new WebHttpBehavior());
tempAdminHost.Open();
So my question is: Is there any way, how to, for example, automaticaly get domain account which use the browser and call the url or any alternative how to still use HTTPS but without putting any credentials?
I didn’t see the way you use the credential to authenticate the client. the client credential type of the two endpoints you use to host the service are None. How does the browser ask you to input the credential? Besides, by default, If the server set up the ClientCredentialType to Windows, the client would use the current user as the credential. The current user’s password and account will be default credential when need to provide a credential.
One more thing to note, if you are simply prompted in the browser to select a certificate instead of the credential(user/password), as follows,
We may have configured the following parameter(clientcertnegotiation parameter).
netsh http add sslcert ipport=127.0.0.1:8000 certhash=c20ed305ea705cc4e36b317af6ce35dc03cfb83d appid={c9670020-5288-47ea-70b3-5a13da258012} clientcertnegotiation=enable
Because the way you use to provide a certificate to encrypt the communication is not correct.
serviceHost.Credentials.ServiceCertificate.Certificate = cert;
We need to bind the certificate to Port.
https://learn.microsoft.com/en-us/windows/desktop/http/add-sslcert
when hosting the service in IIS, we accomplish it by the below UI.
And the parameter configuration depends on the below.
So I suspect the process that binds the certificate to the specified port is completed by IIS. and the parameter should be ignored.
Feel free to let me know if there is anything I can help with.

Changing Fiddler root certificate to successfully decrypt HTTPS

Is there a way to change the Fiddler's root certificate. I want it for a scenario when the client app uses certificate pinning and I have access to the keystore, whose certificate is being trusted by the client app.
I think you're asking "Can I change the certificate Fiddler uses for a particular site" rather than the root certificate, which is used for all sites.
Yes, if you really do have the private key for the certificate, you can configure Fiddler to use it. Inside Fiddler's Rules > Customize Rules > OnBoot function, you can call either:
CertMaker.StoreCert("example.com", certMyCert);
or
CertMaker.StoreCert("example.com", "C:\\temp\\cert.pfx", "TopSecretPassword");
The first call requires that your X509Certificate2 variable (certMyCert in this case) refer to a certificate that is already installed in your computer's Certificate Manager (certmgr.msc), so its private key can be found, while the second allows you to specify a PFX file from disk.

RestSharp - Ignore SSL errors

Is there any whay that I can get RestSharp to ignore errors in SSL certificates? I have a test client, and the service I connect to does not yet have a valid cetificate.
When I make a request now I get the error:
The underlying connection was closed: Could not establish trust
relationship for the SSL/TLS secure channel.
As John suggested:
ServicePointManager.ServerCertificateValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => true;
You can bypass ssl check
In object level:
(Using RestSharp v106.0.0 but before v107)
//bypass ssl validation check by using RestClient object
var restClient = new RestClient(baseUrl);
restClient.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
(Using RestSharp 107 according to the migration guide)
//bypass ssl validation check by using RestClient object
var options = new RestClientOptions(baseurl) {
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
};
var restClient = new RestClient(options);
OR
In application level:
//bypass ssl validation check globally for whole application.
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
There is a better solution than modifying your code. Ideally you want a solution that will simulate the conditions you will see in production and modifying your code won't do that and could be dangerous if you forget to take the code out before you deploy it.
You will need a self-signed certificate of some sort. If you're using IIS Express you will have one of these already, you'll just have to find it. If you don't have it already, open Firefox or whatever browser you like and go to your website. You should be able to view the certificate information from the URL bar and depending on your browser you should be able to export the certificate.
Next, open MMC.exe, and add the Certificate snap-in. Import your certificate file into the Trusted Root Certificate Authorities store and that's all you should need.
Now, your computer as a whole will implicitly trust any certificates that it has generated itself and you won't need to add code to handle this specially. When you move to production it will continue to work provided you have a proper valid certificate installed there.

Resources