PCF Container TrustManager adds certificates, but TrustManagerFactory loads certificates twice - spring-boot

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.

Related

IsCA certificate setting in Go x509 package

When creating an intermediate root certificate, do you set the "IsCA" property of the certificate template? What exactly does this property do?
I'm creating a certificate chain for an application. In this application I need to use intermediate certificates to sign some client certificates. I've found some guides on creating Root CA certificates and they indicate to set IsCA = true when creating the certificate template, but I cannot find any information on creating intermediate roots. Does the IsCA property still need to be set? Are there any other changes to the certificate template when creating an intermediate certificate vs. a root certificate?
You must set the cA basic constraint for intermediate certificates. This bool indicates that a certificate can be used to verify other certificate signatures.
A certificate without cA set to true is a leaf certificate.
Here is the relevant portion of the "basic constraints" section of RFC 5280:
The cA boolean indicates whether the certified public key may be used
to verify certificate signatures. If the cA boolean is not asserted,
then the keyCertSign bit in the key usage extension MUST NOT be
asserted. If the basic constraints extension is not present in a
version 3 certificate, or the extension is present but the cA boolean
is not asserted, then the certified public key MUST NOT be used to
verify certificate signatures.

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.

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.

SNIMatcher not returning correct certificate

I have a server and I've added an SNIMatcher based on this example
http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SNIExamples
SSLServerSocket sslServerSocket = ...;
SNIMatcher matcher = SNIHostName.createSNIMatcher("www\\.example\\.(com|org)");
Collection<SNIMatcher> matchers = new ArrayList<>(1);
matchers.add(matcher);
SSLParameters params = sslServerSocket.getSSLParameters();
params.setSNIMatchers(matchers);
sslServerSocket.setSSLParameters(params);
I've added example.com and example.org in my hosts file to be 127.0.0.1
When I go to example.com and example.org they both get the same localhost certificate (I've created a separate certificate for example.org)
My question is what's the correct way to attach a SNIMatcher
do I need to create a separate matcher for each virtual host that has its own certificate? (I did that but I got an IllegalArgumentException because both hosts had the same type 0 (StandardConstants.SNI_HOST_NAME)
http://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLParameters.html#setSNIMatchers-java.util.Collection-
so I changed the way I am attaching and just did it the way example does (com|org) but it's returning the same certificate for both. Just wondering what's the correct way?
thank you
I believe it has to do with the x509 stuff (distinguished name) in the certs of the keys in the keystore used for the sslcontext that issued the sslserversocketfactory.
Choosing the key and presenting the x509 cert (given the "merely accepted" sni hostname) has to do with some DN in the x509 because that's the only mapping knowledge available. The host mapping is certainly not done with the keystore alias! The mapping information from the keystore has to be in the cert, a hostname that a certificate authority has signed.
AFAIK, the SNI server-side mechanism doesn't do that key selection, it only restricts the hostnames allowed for the entire keystore/keymanager hooked up.
PS: If you wanted to switch keystores, then you would have to essentially build an sslcontext with a X509KeyManager of your own that can provide all the material of all key stores. You would likely wrap an array of key managers out of a KeyManagerFactory... and the support ends here for me because it gets ugly too fast for what I recall!

Windows keystores and certificates

I've recently inherited a project with minimal documentation that performs digital signatures of documents and I've received a change request that has left me a little baffled.
The application is Java based, and makes use of Java Keystores (JKS) and uses the private key of the alias specified as a command line operation to digitally sign an input document. This all appears fairly straightforward to me, however the change request has left me confused.
The client has requested the ability to use "Windows" keystores (more specifically, the Windows-MY keystore which relates to personal certificates as far as I can tell). Now, my initial assumption is that what the client is requesting is simply not possible as this key store will only ever contain certificates, which can not be used for signing documents in any capacity. Am I incorrect, or will the Windows-MY keystore only ever contain a public certificate? I don't believe a private key would ever be embedded within one of these certificates.
Unfortunately there are a some communication difficulties so I'd like to make sure my reasoning is correct before proceeding any further.
Here's some more evidence to support my case (communication from the client):
Creating Windows Key Store (Exporting from Java Keystore ) steps are here -
generate RSA key
keytool -genkey -alias mykey -keyalg RSA -keystore my.jks -keysize 2048
Export Certificate from the above keystore:
keytool -export -alias mykey -file mykey.crt -keystore my.jks
Enter keystore password: temp123
Certificate stored in file <mykey.crt>
Install the above certificate in windows keystore.
a. Double click on “mykey.crt” and click on Install certificate
b. Select “Place all certificates in the following store” radio button and click “Browse” button to Added it in windows Certificate store.
c. Check this certificate in WindowsMy store.
Unless I'm wrong, keytool will only ever generate a certificate type rather than an actual public/private key pair?
Any assistance or even affirmation would be greatly appreciated, apologies for the lack of clarity in the question but unfortunately this is all I have to work with at the moment.
Java or Windows specific answers would be helpful but even just confirmation of the basic principles would be appreciated.
Thanks in advance
Maybe you want to take a look at Oracle's documentation [1] on the SunMSCAPI provider, that can be used to access certificates and keys stored in the Windows-MY (Personal) and Windows-ROOT (Trusted Root Certification Authorities) stores.
There is a little code snippet as well, which seems to match your needs quite reasonably:
KeyStore ks = KeyStore.getInstance("Windows-MY");
// Note: When a security manager is installed,
// the following call requires SecurityPermission
// "authProvider.SunMSCAPI".
ks.load(null, null);
byte[] data = ...
String alias = "myRSA";
PrivateKey privKey = (PrivateKey) ks.getKey(alias, null);
Certificate cert = ks.getCertificate(alias);
Provider p = ks.getProvider();
Signature sig = Signature.getInstance("SHA1withRSA", p);
sig.initSign(privKey);
sig.update(data);
byte[] signature = sig.sign();
System.out.println("\tGenerated signature...");
sig.initVerify(cert);
sig.update(data);
if (sig.verify(signature)) {
System.out.println("\tSignature verified!");
}
Summarizing: The Windows-My store holds certificates as well as private keys, both can be read from Java using the SunMSCAPI provider and can be used to sign digital documents.
If simply wanting to use a keytool created (java) keystore's certificate w/ its private key so that you can import it into a windows, then would you just export it to PKCS12 format (PFX). (i.e.; when exporting, use paramter -storetype=pkcs12)

Resources