I am building a web application using Java and Tomcat 7.0.
I have a self-signed certificate (in the future I'll get an official one) on the server side, and I've added a client's root certificate to its truststore. I've already set a required two-way authentication for https protocol on port 3443 with the following lines on the server.xml file:
<Connector port="3443" scheme="https" secure="true" SSLEnabled="true"
truststoreFile="server.keystore" truststorePass="keystore password"
keystoreFile="server.keystore" keystorePass="keystore password"
clientAuth="true" keyAlias="serverkey"
sslProtocol="TLS"/>
This is working and I can only access the system with a valid certificate.
I was now wondering how I can get a property of this used certificate on my Servlet to log the user in based on his certificate. All certificates used in this context will have a different CN so I want to use that to identify the user.
You will need to import java.security.cert.X509Certificate and . In your doGet(...) method, use the following:
String cn = null;
X509Certificate[] certs = (X509Certificate[]) req
.getAttribute("javax.servlet.request.X509Certificate");
if (certs != null) {
String dn = certs[0].getSubjectX500Principal().getName();
// parse the CN out from the DN (distinguished name)
Pattern p = Pattern.compile("(^|,)CN=([^,]*)(,|$)");
Matcher matcher = p.matcher(dn);
if(matcher.find()) {
cn = matcher.group(2);
}
} else {
// no certificate provided
}
Related
I'm new to spring-boot & Elasticsearch technology stack and I want to establish secure HTTPS connection
between my spring-boot app & elastic search server which runs locally.
These are the configurations that I have done in elasticsearch.yml
Giving credintials for elasticsearch server
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
For secure inter nodes connection inside elasticsearch cluster
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: elastic-certificates.p12
For secure Https connection with clients and elasticsearch clustrer
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: elastic-certificates.p12
xpack.security.http.ssl.truststore.path: elastic-certificates.p12
xpack.security.http.ssl.client_authentication: optional
Enabling PKI authentication
xpack.security.authc.realms.pki.pki1.order: 1
I have generated CA and client certificate which signed by generated CA according to this link
https://www.elastic.co/blog/elasticsearch-security-configure-tls-ssl-pki-authentication
And I have added CA to my java keystore.
This is the java code i'm using to establish connectivity with elasticsearch server.
#Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(RestClientConfig.class);
private static final String CERT_FILE = "client.p12";
private static final String CERT_PASSWORD = "";
private static final String USER_NAME = "elastic";
private static final String USER_PASS = "pwd";
#Override
#Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200") // set the address of the Elasticsearch cluster
.usingSsl(createSSLContext()) // use the SSLContext with the client cert
.withBasicAuth(USER_NAME, USER_PASS) // use the headers for authentication
.build();
return RestClients.create(clientConfiguration).rest();
}
private SSLContext createSSLContext() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManager[] keyManagers = getKeyManagers();
sslContext.init(keyManagers, null, null);
return sslContext;
} catch (Exception e) {
LOG.error("cannot create SSLContext", e);
}
return null;
}
private KeyManager[] getKeyManagers()
throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException, UnrecoverableKeyException {
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(CERT_FILE)) {
KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
clientKeyStore.load(inputStream, CERT_PASSWORD.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(clientKeyStore, CERT_PASSWORD.toCharArray());
return kmf.getKeyManagers();
}
}
And my client certificate called "client.p12" included in resources folder in the spring-boot application. In elasticsearch side everything seems to be fine.
But when I run the spring-boot app it gives this warning & exception
Cannot create index: Host name 'localhost' does not match the certificate subject provided by the peer (CN=instance); nested exception is java.lang.RuntimeException:
Host name 'localhost' does not match the certificate subject provided by the peer (CN=instance)
java.io.IOException: Host name 'localhost' does not match the certificate subject provided by the peer (CN=instance)
Caused by: javax.net.ssl.SSLPeerUnverifiedException: Host name 'localhost' does not match the certificate subject provided by the peer (CN=instance)
I'm doing this because I have an idea to migrate elastic search server to a another VPS hosting later.
this is the command I used for generate client certificate
bin/elasticsearch-certutil cert --ca
config/elastic-stack-ca.p12
-name "CN=something,OU=Consulting Team,DC=mydomain,DC=com"
ENTER
client.p12 ENTER
ENTER
searching this error in internet I figured out there is a issue with my client certificate. It has something to do with SAN names with elastic nodes. But I have very little knowledge
about these certificate stuff. So it's really confusion for me. If Some one can give me in-detailed explanation why this is occurring & solution to this I'm really appreciating it & grateful. A Proper link will also be helpful. This question
already asked in
How to connect 'spring boot 2.1 with elasticsearch 6.6' with cluster node 'https'?
link
But no luck for me.
Your issue looks similar to another issue, see here: Certificate for <localhost> doesn't match any of the subject alternative names
So I would assume that if you add the SAN extension localhost as DNS and the ip address of localhost to the elasticsearch certificate it should work. So adding the following additional parameters: --dns localhost --ip 127.0. 0.1. Can you give the command below a try and share your results here?
bin/elasticsearch-certutil cert --ca config/elastic-stack-ca.p12 -name "CN=something,OU=Consulting Team,DC=mydomain,DC=com" --dns localhost --ip 127.0. 0.1
By the way you have configured Elasticsearch to optionally request the client certificate, but not marked as required see: xpack.security.http.ssl.client_authentication: optional. So it is not required and therefor it does not make sense to create a KeyManager and construct a sslcontext from it. But I think you need to export the public certificate of the certificate of Elasticsearch and supply that to your http client by transforming it to a TrustManager and creating a sslcontext from it. I have a working example here: ElasticSearch RestHighLevelClient SSL example.
In my desktop java application there is a class that connects with other system through HTTPS connection using a client certificate. Part of this connection is the SSLContext which receives the KeyManager[] and the TrustManager[] in order to implement the user certificate and the trsut store to get the handshack with the remote server. This class uses the Windows repository to get the list of certificates available on the machine so the user can pick the right one to connect. The problem is that I'm migrating this application to a cloud web server (tomcat) and the procedute to get the certificate is quite different. With tomcat I'm able to forward the user to a https page that requests a valid certificate issued by a CA. Once the user access this page, the browser pops up a window with the certificates available on the machine, so the user can pick one authenticate. My problem now is to create this SSLContext once, from the browser authentication, I can get only the x509 cert selected by the user, but without the Private Key. My question is. Am I missing something to get the certificate Private Key? I know that windows repository does not share the private key, but when this procedure is called from a desktop application, at least the "resume or header" (RSAPrivateKey[size = 2048 bits, type = Exchange, container = {########}) of the key is provided, which still works. But through the browser, I cannot get this information. Or is there another way to create the KeyManager[] with just the x509 certificate without provide the private key?
here is a piece of the code which creates the connection with the server..
// create the connection
SocketFactoryDinamico socketFactory = new SocketFactoryDinamico(X509certificate, PrivateKey);
socketFactory.setFileCacerts(getClass().getResourceAsStream("cacerts"));
KeyManager[] keyManagers = socketFactory.createKeyManagers();
TrustManager[] trustManagers = socketFactory.createTrustManagers();
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(keyManagers, trustManagers, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslc.getSocketFactory());
String url = "https://someserver.com";
URL obj = new URL(url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
and here is the code which gets the x509 certificate on the .jsp...
X509Certificate[] certs = (X509Certificate[])
request.getAttribute("javax.servlet.request.X509Certificate");
if (null != certs && certs.length > 0) {
X509Certificate cert = certs[0];
}
and here is the server configuration to request the certificate authentication
<Connector
clientAuth="true"
port="8443"
protocol="HTTP/1.1"
SSLEnabled="true"
scheme="https"
secure="true"
keystoreFile="C:/JavaWeb/tomcat"
keystoreType="JKS" keystorePass="pswd"
truststoreFile="C:/JavaWeb/myTrustStore"
truststoreType="JKS" truststorePass="changeit"
SSLVerifyCLient="require" SSLVerifyDepth="10" sslProtocol="TLS"
/>
I´ve tried to make this connection from the client side with javascript, however I get this error:
Access to XMLHttpRequest at 'https://remoteserver.com' from origin 'http://localhost' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Here is my code:
var httpRequest = new XMLHttpRequest();
httpRequest.open("POST", "https://remoteserver.com");
httpRequest.setRequestHeader("Role-Type", "role");
httpRequest.onreadystatechange = function () {
if (this.readyState === 4) {
if (httpRequest.status === 200) {
console.log('Status:', this.status);
console.log('Headers:', this.getAllResponseHeaders());
console.log('Body:', this.responseText);
console.log(httpRequest);
} else {
console.log("Erro");
console.log(httpRequest);
}
}
};
httpRequest.send();
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.
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
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/