Spring Boot - Reading x509 client certificate from HTTP request - java

How to receive a x509 certificate from client? I'm using Java's Spring-Boot-Framework with embedded tomcat. For protyping I configured this with Java SE:
HttpsExchange httpsExchange = (HttpsExchange) httpReq;
name = httpsExchange.getSSLSession().getPeerPrincipal().getName();
A user gave me a reference to do this here (down below)
#RequestMapping(value = "/grab")
public void grabCert(HttpServletRequest servletRequest) {
Certificate[] certs =
(Certificate[]) servletRequest.getAttribute("javax.servlet.request.X509Certificate");
}
But I'm not able to get some certificate! Maybe because I'm using tomcat, and it is handling all SSL-Connections. So that no certificate is receiving my application. What I have to do, to get the clients certificate? The client certificate is used to get https connection. I need some information from the subject of the certificate. Thanks.

You have to get it from the HttpServletRequest.
You can check the answer to this question: How to get the certificate into the X509 filter (Spring Security)?:
No you can't get it that way. You need to grab it from the HttpServletRequest:
X509Certificate[] certs = (X509Certificate[])HttpServletRequest.getAttribute("javax.servlet.request.X509Certificate");
This was the post I was trying to point you to, written by Gandalf.
And this was the original question

Related

Jetty: How to validate SSL client certs in application code?

I have a multi-tenant webservice which I want to use mutual SSL/TLS authentication as well as user authentication. This means that I need to resolve the user and the user's allowed certs, which can only occur after the SSL connection has been established. I will then use PKIXCertPathBuilderResult to valid the trust chain using the client certs passed in the request.
In Tomcat with the openssl connector, it's possible to use optional_no_ca mode, which requests a client cert but does not validate it.
With Jetty 9.x, I've tried configuring the following SslContextFactory options to no avail:
ValidateCerts=false
ValidatePeerCerts=false
TrustAll=true
How can this be achieved in Jetty 9.x?
Edit 2019: The requirement was to demand an SSL certificate from all client devices accessing the system. The validation of the certificate chain and other certificate attributes would then be performed by the application, which also has the ability to lookup missing cert roots from external sources.
This is in contrast to the norm - typically, application servers would perform cert-chain validation during the SSL connection setup using a pre-configured static list of known trusted CAs. If trust can not be found, the SSL connection is rejected.
While TrustAll seems to be the likely solution, it only works if no TrustStore and KeyStore is given. Then you can't connect using a regular client as the server has no certificate to give during the handshake.
To get a sensible trustAll mode, the only options seems to be to extend SslContextFactory:
package media.alu.jetty;
/**
* SslContextFactoryRelaxed is used to configure SSL connectors
* as well as HttpClient. It holds all SSL parameters and
* creates SSL context based on these parameters to be
* used by the SSL connectors.
*
* TrustAll really means trustAll!
*/
#ManagedObject
public class SslContextFactoryRelaxed extends SslContextFactory
{
private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM;
private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM;
#Override
protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls) throws Exception
{
TrustManager[] managers = null;
if (trustStore != null)
{
if (isTrustAll()) {
managers = TRUST_ALL_CERTS;
}
// Revocation checking is only supported for PKIX algorithm
else if (isValidatePeerCerts() && "PKIX".equalsIgnoreCase(getTrustManagerFactoryAlgorithm()))
{
PKIXBuilderParameters pbParams = newPKIXBuilderParameters(trustStore, crls);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams));
managers = trustManagerFactory.getTrustManagers();
}
else
{
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
trustManagerFactory.init(trustStore);
managers = trustManagerFactory.getTrustManagers();
}
}
return managers;
}
}
To use:
Follow Jetty documentation to configure SSL/TLS with client authentication
Compile code above against Jetty 9.x
Install jar in `$jetty.home/lib/ext'
Edit $jetty.home/etc/jetty-ssl-context.xml
i. Change:
<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
to:
<Configure id="sslContextFactory" class="media.alu.jetty.SslContextFactoryRelaxed">
ii. Add <Set name="TrustAll">TRUE</Set> as child of <Configure id="sslContextFactory">
Why? JSSE already validates it. All you need to to is check the authorization of that user. By the time you get access to the certificate, it is already validated for integrity, non-expiry, and trust-anchoring, so you can believe that its SubjectDN refers to who it says it refers to, so all you have to do is decide what roles that SubjectDN has, if any.

Convert X509Certificate to PEM or ASN.1/DER

I have a chain of certificates (X509Certificate []), but I have only one certificate in the chain. I need to get the complete chain.
I have tried the openssl command, but that is not useful here. Can someone please tell me how to:
Convert this X509Certificate to PEM or ASN.1/DER that I can save in my file storage?
Get the complete chain using this certificate?
Edit:
So, code-wise what I'm trying to achieve is something like:
protected static String convertToPem(X509Certificate cert) {
Base64 encoder = new Base64(64);
String cert_begin = "-----BEGIN CERTIFICATE-----\n";
String end_cert = "-----END CERTIFICATE-----";
byte[] derCert = cert.getEncoded();
String pemCertPre = new String(Base64.encodeBase64(derCert));
String pemCert = cert_begin + pemCertPre + end_cert;
return pemCert;
}
But, this is not working. Basically, I'm looking for a method that takes a X509Certificate object and then converts it to a .pem etc, that is saved on the device.
Convert this X509Certificate object to .cer/ .per/ .der that I can save in my file storage?
See, for example, the answer at OpenSSL's rsautl cannot load public key created with PEM_write_RSAPublicKey. It tells you how to convert keys to/from PEM and ASN.1/DER format, and includes a treatment of Traditional Format (a.k.a. SubjectPublicKeyInfo).
If you are not doing it programmatically, then you should search for the answer. There are plenty of off-topic question on how to use the openssl command to convert between ASN.1/DER and PEM. Or ask on Super User, where they specialize in commands and their use.
Get the complete chain using this certificate?
This is a well known problem in PKI called the Which Directory problem. The solution is to have the server or service provide the missing intermediate CA certificates. If you can't validate a web server or service's identity because you are missing intermediate CA certificates, then the server is misconfigured.
Once you have the intermediate CA certificates, you still have to root trust somewhere. You can use the self-signed CA, or one of the intermediates signed by the self-signed CA.
This answer is helpful in troubleshooting a misconfugred server using OpenSSL's s_client: SSL site and browser warning.
Related: if there was a global directory of certificates like the ITU envisioned in X.500, then you would not have the second problem. A relying party or user agent would just fetch the certificate it needed from the directory.
But we lack a central directory, so relying parties and user agents often use the CA Zoo (a.k.a., the local Trust Store or cacerts.pem). This has its own set of problems, like the wrong CA certifying a site or service.
One of the off-shoots is the CA Cartel, where browser are in partnership with the CAs at the CA/Browser Forum. Browser have requirements for inclusion, but they often can't punish a misbehaving CA like Trustwave.
And the browsers have managed to box themselves into a position where the Internet of Things (IoT) will not work because of the browser's reliance/requirements on server certificates signed by a CA.

getAttribute("javax.servlet.request.X509Certificate") not set (Spring,CXF,Jetty,JAX-RSv1.1)

My client implements Two-Way SSL in the following way:
private final static String KEYSTORE = "/security/client.jks";
private final static String KEYSTORE_PASSWORD = "secret";
private final static String KEYSTORE_TYPE = "JKS";
private final static String TRUSTSTORE = "/security/certificates.jks";
private final static String TRUSTSTORE_PASSWORD = "secret";
private final static String TRUSTSTORE_TYPE = "JKS";
...
KeyStore keystore = KeyStore.getInstance(KEYSTORE_TYPE);
FileInputStream keystoreInput = new FileInputStream(new File(KEYSTORE));
keystore.load(keystoreInput, KEYSTORE_PASSWORD.toCharArray());
KeyStore truststore = KeyStore.getInstance(TRUSTSTORE_TYPE);
FileInputStream truststoreIs = new FileInputStream(new File(TRUSTSTORE));
truststore.load(truststoreIs, TRUSTSTORE_PASSWORD.toCharArray());
SSLSocketFactory socketFactory = new SSLSocketFactory(keystore, KEYSTORE_PASSWORD, truststore);
Scheme scheme = new Scheme("https", 8543, socketFactory);
SchemeRegistry registry = new SchemeRegistry();
registry.register(scheme);
ClientConnectionManager ccm = new PoolingClientConnectionManager(registry);
httpclient = new DefaultHttpClient(ccm);
HttpResponse response = null;
HttpGet httpget = new HttpGet("https://mylocalhost.com:8543/test");
response = httpclient.execute(httpget);
...
And I try to retrieve the X.509 certificate on the server's side from the client via javax.servlet.http.HttpServletRequest.getAttribute("javax.servlet.request.X509Certificate") as it is decribed here: http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletRequest.html#getAttribute%28java.lang.String%29.
I get the HttpServletRequest on the server's side via:
HttpServletRequest servletRequest = (HttpServletRequest) msg.get("HTTP.REQUEST"); via the handleMessage(Message msg) method of my interceptor class which extends AbstractPhaseInterceptor<Message>. I have to use JAX-RS 1.1.1 on the server's side because of some Maven dependencies which I am not allowed to change and so I cannot use ContainerRequestFilter (supported from JAX-RS 2.0 on).
My problem is that getAttribute("javax.servlet.request.X509Certificate") on the server's side returns null all the time. If I verify the traffic between server and client, I can see that the certificate from the server is sent to the client, that handshake works. But I cannot see that the client certificate is sent to the server and I think it is the reason why getAttribute("javax.servlet.request.X509Certificate") returns null. Does someone know how I can solve that problem? I tried some other implementations on the client's side already, but with no change.
What am I doing wrong? Many thanks in advance!
Additional information: I have seen on the server's side that javax.servlet.request.ssl_session_id, javax.servlet.request.key_size and javax.servlet.request.cipher_suite are set, but the key javax.servlet.request.X509Certificate is not set. I'm using Jetty Server 8.1.15, Apache CXF 2.7.x and JAX-RS 1.1.1. I tried with Jetty configuration via http://cxf.apache.org/docs/jetty-configuration.html and http://cxf.apache.org/docs/secure-jax-rs-services.html#SecureJAX-RSServices-Configuringendpoints, the attribute still isn't set.
Problem is solved. It wasn't a problem in the code, it was a certificate problem only. My problem was that I was a beginner regarding X509 certificates as well, it was a handshake problem between server and client. In this case, only the SSL/Handshake debug helped me. The debug log told that the server only accepted client certificates from a specific CA, the server told the client the required CA in a certificate request during the ServerHello message. Since the Client didn't have a certificate from that CA, it didn't send something and the connection between client and server was closed then, with the result that javax.servlet.request.X509Certificate was not set.
For all others who might join the same problem sometime (which seems to be a common SSL configuration problem regarding to IBM as it is mentioned in the first link below), the following sources helped me a lot:
- http://www-01.ibm.com/support/docview.wss?uid=swg27038122&aid=1
(pages 16 and 17)
- http://java.dzone.com/articles/how-analyze-java-ssl-errors (shows as the handshake should look)
- need help Debugging SSL handshake in tomcat (shows how to debug ssl errors in Java)
- https://thomas-leister.de/internet/eigene-openssl-certificate-authority-ca-erstellen-und-zertifikate-signieren/ (in German, but maybe you can find an English equivalent)
- https://access.redhat.com/site/documentation/en-US/Red_Hat_JBoss_Fuse/6.0/html/Web_Services_Security_Guide/files/i382674.html (continuation of the German article)
- http://www.webfarmr.eu/2010/04/import-pkcs12-private-keys-into-jks-keystores-using-java-keytool/ (how to create keystore and truststore)
After creating an own CA, a server and client certificate and after creating the keystore and truststore for both, the attribute was set now:
- Here15_1: javax.servlet.request.X509Certificate
- Here16_2: class [Ljava.security.cert.X509Certificate;
- Here16_3: [Ljava.security.cert.X509Certificate;#43b8f002
The server code is able to extract the client certificate information now, too.

How to create own trust manager in java for apache httpclient library to ignore certificate in apache httpclient 4.2?

Can somebody tell me how can i ignore the ssl certificate during web service call.
I am calling https weburl to get api response but getting peer not authenticated error.
Old examples are not working as some of methods are deprecated so can somebody tell me/ provide some sample code so that i will not get this error.
I just came to know that the problem is coming due to Certificate.
I am using 3rd party API for db calls & they have ssl certificate for their domain
i.e. www.dbprovider.com (SSL certificate is *.dbprovider.com)
& they created subdomain for us which look like myapp.dbprovider.com
So now the problem is no peer certificate is available when i try to hit through command
openssl s_client -ssl3 -showcerts -connect myapp.dbprovider.com:443
openssl s_client -tls1 -showcerts -connect myapp.dbprovider.com:443
Can somebody tell me what i should now do with it. Is there any control on dbprovider site so that they can provide me some configuration or i have to write code to ignore their certificate (but for ignoring certificate we are not getting their peer certificate)
Use a custom SSLSocketFactory as described here: http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d5e512. One such factory that ignores self-signed certs is EasySSLProtocolSocketFactory.
ProtocolSocketFactory factory = new EasySSLProtocolSocketFactory();
try {
URI uri = new URI(config.getBaseUrl());
int port = uri.getPort();
if (port == -1) {
port = 443;
}
Protocol easyHttps = new Protocol(uri.getScheme(), factory, port);
hostConfiguration.setHost(uri.getHost(), port, easyHttps);
} catch (URISyntaxException e) {
throw new IOException("could not parse URI " + config.getBaseUrl(), e);
}
Source: http://frightanic.com/software-development/self-signed-certificates-in-apache-httpclient/

SAML 2.0 - How to verify the sender certificate?

I implement a SAML SP in Java.
I send an AuthnRequest to SAML 2.0 IDP and gets an encrypted response.
My question is:
How do I make sure that the response indeed comes from the IDP and not from a hacker?
It is not enough to validate the signature, since this only tells me that the sender has a matching pair of private/public keys, but it could be anyone.
So, I need the IDP to supply me in advance a certificate which I upload to a jks file, and compare it each time to the certificate I extract from the ds:X509Certificate element of the response.
Now, is there a standard way of comparing the sender's certificates with the one stored in my keystore?
I saw the following code:
KeyStore keyStore = getKS();
PKIXParameters params = new PKIXParameters(keyStore);
params.setRevocationEnabled(false);
CertPath certPath = certificateFactory.generateCertPath(Arrays.asList(certFromResponse));
CertPathValidator certPathValidator = CertPathValidator.getInstance(CertPathValidator.getDefaultType());
CertPathValidatorResult result = certPathValidator.validate(certPath, params);
Is it enough? If the validation doesn't throw an exception it verifies the sender's identity?
This is the way i have solved the verification of signatures with OpenSAML
https://blog.samlsecurity.com/2012/11/verifying-signatures-with-opensaml.html
I have also written a book, A Guide to OpenSAML, where I explain in detail encryption and signing and more using OpenSAML.
What is important with the OpenSAML verification methods is that they only verify the cryptographic validity of the signature (That the content has not been changed). It does not however verify that the sender is someone that you trust.
The Signature validator is instantiated with the public key of the sender to validate against, the public key of the sender. This is normally exchanged is the setup of an identity federation using SAML Metadata

Categories