SNIMatcher not returning correct certificate - java-8

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!

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.

NET::ERR_CERT_COMMON_NAME_INVALID security certificate does not specify Subject Alternative Names

I apologize if my english is not too good. I am trying to create a certificate request from my IIS server, but everytime i completed the request. The client still don't trust the web server.
I am from a development background not really into infrastructure operation. So my question might be incorrect, due to misconception, if so, please feel free to educate me. Below are my questions:
Do I need to import the root certificate into the window IIS server before starting a certificate request? If so, how do i create or export a root certificate from Window Certificate Authority?
Although this answer is not Windows-specific, I found this page through a search for my problem, and hopefully the following information will be of use to someone having this problem on Linux, like me:
I have encountered this problem too and, although the accepted answer is probably correct, the process for generating a CSR (certificate signing request) with the SAN (Subject Alternative Name) requires a bit of explanation.
There are several articles detailing this, but basically, you need to create a file, let's call it ssl.conf, which contains the necessary information, including the SAN details, which you will pass via a parameter to the openssl command when creating the CSR.
(This assumes you have already generated a key).
The contents of ssl.conf can be as simple as follows. Note the section at the bottom detailing the SAN. Adjust all the parameters to suit your requirements.
[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
C=US
ST=New York
L=Rochester
O=End Point
OU=Testing Domain
emailAddress=your-administrative-address#your-awesome-existing-domain.com
CN = www.your-new-domain.com
[ req_ext ]
subjectAltName = #alt_names
[ alt_names ]
DNS.1 = your-new-domain.com
DNS.2 = www.your-new-domain.com
The rest of the file contains other X.509 information that you would be prompted for during the CSR creation (country, state etc.)
Now you can create your CSR as follows:
openssl req -new -sha256 -out private.csr -key private.key -config ssl.conf
Where private.csr refers to your new CSR file, private.key refers to the key you already should have generated and ssl.conf is the file above.
You can verify the contents of your new CSR as follows:
openssl req -text -noout -verify -in private.csr
Then you will see, on the console, the details of the CSR, including the SAN details.
References:
https://www.endpoint.com/blog/2014/10/30/openssl-csr-with-alternative-names-one
https://gist.github.com/croxton/ebfb5f3ac143cd86542788f972434c96
https://geekflare.com/san-ssl-certificate/
The error can be because of multiple reasons
a) You are using an IP address to access the website (assuming you have a certificate based on a URL). So try accessing using the URL for which you have procured the certificate
b) You have procured certificate for xyz.com but have binded the same to abc.com . Hence when accessing abc.com you receive the above error. Bind the correct certificate if have one.
c) YOu have procured a multi domain certificate , but the url you are trying to access is not added as SAN to the certificate. Have a word with your SSL provider and ask him to add the URL as SAN to the certificate.
Should not the answer be that Subject Alternative Name (SAN) is mandated by Chrome for Certificate Validation check? Here is a link which speaks more about comparison between Common Name (CN) and SAN

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.

X509 Certificate Purpose Setting

I would like to ask when is the purpose of a certificate, like Server Authentication, Client Authentication, set for the certificate.
Is it when we generate the CSR or when it is signed by the CA?
The CSR is a Certificate Signing Request. If it is a PKCS#10 request (by far the most common type) it can indicate which extensions are requested and that can include the Extended Key Usage (aka purpose). But the CA ultimately decides what to include when it creates and signs the cert. It could choose not to issue a cert. It could issue a cert with a subset of the requested attributes. It could issue a cert that is completely different. It could issue a cert that is exactly what the CSR requested.

Resources