I'm trying to get my embedded Jetty 8.1.4 (no I can go higher!) server to work with https. I generate my keystore with the following command :
keytool -genkey -alias sitename -keyalg RSA -keystore keystore -keysize 2048
My problem is that when I run this, I get a nice exception!
WARN - AbstractLifeCycle - FAILED SslContextFactory#160b6dff(null,null): java.io.IOException: Invalid keystore format
java.io.IOException: Invalid keystore format
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:633)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:38)
at java.security.KeyStore.load(KeyStore.java:1183)
I tried generating the keystore following different online guides but to no avail.
Any ideas?
Many Thanks,
AW
Here's the server code :
public static void main(String[] args) throws Exception {
Server server = new Server();
server.setStopAtShutdown(true);
server.setGracefulShutdown(ALLOWED_SHUTDOWN_TIME);
Resource keystore = Resource.newClassPathResource("/keystore");
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStoreResource(keystore);
sslContextFactory.setKeyStorePassword("wicket");
sslContextFactory.setTrustStoreResource(keystore);
sslContextFactory.setKeyManagerPassword("wicket");
SslSelectChannelConnector connector = new SslSelectChannelConnector(sslContextFactory);
connector.setMaxIdleTime(30000);
connector.setPort(getServerPort());
connector.setAcceptors(4);
connector.setReuseAddress(false);
server.addConnector(connector);
final ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
context.setResourceBase("src/main/webapp");
context.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
context.getSessionHandler().getSessionManager().setMaxInactiveInterval(60 * 15);
context.getSessionHandler().getSessionManager().setSessionTrackingModes(newHashSet(SessionTrackingMode.COOKIE));
final FilterHolder openSessionInViewFilter = new FilterHolder(new OpenSessionInViewFilter());
context.addFilter(openSessionInViewFilter, "/", EnumSet.of(DispatcherType.REQUEST));
final FilterHolder wicketFilter = new FilterHolder(WicketFilter.class);
wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, "/*");
wicketFilter.setInitParameter("applicationFactoryClassName", "org.apache.wicket.spring.SpringWebApplicationFactory");
wicketFilter.setInitParameter("configuration", "development");
context.addFilter(wicketFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
context.getInitParams().put("contextConfigLocation", "classpath:spring/spring-config.xml");
context.addEventListener(new ContextLoaderListener());
try {
server.start();
server.join();
} catch (Exception exception) {
exception.printStackTrace();
System.exit(100);
}
}
When it comes to SSL and the web, you HAVE TO STAY CURRENT both in the JVM and the Server software. The rapid changes in what is supported in SSL in the browser, proxies, hardware, etc.. force you to.
For example, Jetty 8.1.4 was created back in the days of SSL v1, SSL v2 were popular. Today, SSL is pretty much deprecated (all the way to SSL v3), replaced with TLS with NPN/ALPN being increasingly popular.
Newer versions of Jetty have even deprecated various cipher suites that cause problems online.
Even the Certificate Authorities (CA) are moving in directions that render the older SSLs irrelevant, making it almost impossible to get a Certificate that will work with these old environments.
Related
I am trying to invoke a SOAP webservice in my spring boot application using spring-ws with a keystore which has multiple certs. The configuration always defaults to single cert.
Sample code below:
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
Merlin merlin = new Merlin();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream inputStream = new FileInputStream(ResourceUtils.getFile(("keystore.jks")));
keyStore.load(inputStream, "tester".toCharArray());
merlin.setKeyStore(keyStore);
wss4jSecurityInterceptor.setSecurementSignatureCrypto(merlin);
wss4jSecurityInterceptor.setSecurementUsername("test");
wss4jSecurityInterceptor.setSecurementPassword("");
webServiceTemplate.setInterceptors(new org.springframework.ws.client.support.interceptor.ClientInterceptor[]
{wss4jSecurityInterceptor});
When i checked the source code of the apache library class WSSecSignature class. I see there is a configuration for picking up multiple cert. But am not sure how to set the singleCert to be false in the wss4jSecurityInterceptor. It always goes to the else block in the below logic
if (!this.useSingleCert) {
this.secRef.addTokenType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1");
ref.setValueType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1");
} else {
ref.setValueType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
}
Is there a config i need to set while setting the keystore to Merin object, to make the useSingleCert as false?
Found a work around to override the Wss4jSecurityInterceptor, set the property to false and use the extended interceptor
class SecurityInterceptor extends Wss4jSecurityInterceptor
{
#Override
protected RequestData initializeRequestData(MessageContext messageContext) {
messageContext.setProperty(WSHandlerConstants.USE_SINGLE_CERTIFICATE, "false");
return super.initializeRequestData(messageContext);
}
}
I'm trying to use Micrometer with Elasticsearch over SSL.
I use Micrometer in version 1.8.0, Elasticsearch in version 7.16.3 and OpenJDK 11.0.2 .
Because I know that it's not possible to use a built-in configuration (link) I tried to inject a custom HttpUrlConnectionSender as in the following class SecureHttpSender:
public class SecureHttpSender extends HttpUrlConnectionSender {
...
public SecureHttpSender(ElasticProperties properties, SecureElasticProperties secureElasticProperties) {
super(properties.getConnectTimeout(), properties.getReadTimeout());
this.secureElasticProperties = secureElasticProperties;
this.sslSocketFactory = buildSslSocketFactory();
}
#Override
public Response send(Request request) throws IOException {
HttpURLConnection httpURLConnection = null;
try {
httpURLConnection = (HttpURLConnection) request.getUrl().openConnection();
// if the connection is an instance of the HttpsURLConnection class, the ssl configuration will always been applied.
if (httpURLConnection instanceof HttpsURLConnection) {
// - hostname verifier
if (!secureElasticProperties.isVerifyHostname()) {
logger.debug("setting the hostname verifier to: {}", NoopHostnameVerifier.INSTANCE);
((HttpsURLConnection) httpURLConnection).setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
}
// - trust store configuration
((HttpsURLConnection) httpURLConnection).setSSLSocketFactory(sslSocketFactory);
}
return super.send(request);
} finally {
try {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
} catch (Exception ignore) {
}
}
}
private SSLSocketFactory buildSslSocketFactory() {
SSLSocketFactory sslSocketFactory;
try (InputStream is = getInputStream(secureElasticProperties.getTrustStorePath())) {
KeyStore truststore = KeyStore.getInstance(secureElasticProperties.getTrustStoreType());
truststore.load(is, secureElasticProperties.getTrustStorePassword().toCharArray());
SSLContextBuilder sslBuilder = SSLContexts.custom().loadTrustMaterial(truststore, null);
final SSLContext sslContext = sslBuilder.build();
sslSocketFactory = sslContext.getSocketFactory();
} catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
String message = String.format("error while loading the security configuration from: %s", secureElasticProperties);
logger.error(message, e);
throw new RuntimeException("management.metrics.export.elastic.ssl");
}
return sslSocketFactory;
}
private InputStream getInputStream(String trustStorePathString) throws IOException {
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource resource = pathMatchingResourcePatternResolver.getResource(trustStorePathString);
return resource.getInputStream();
}
}
that I injected with Spring Boot so I can apply the desired configuration, but I got the following error:
ERROR 10912 --- [trics-publisher] i.m.elastic.ElasticMeterRegistry : failed to send metrics to elastic
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
The server certificate and the client truststore are valid because I already used them with success.
I also tried to force a specific version of the TLS protocol during the handshake phase: TLSv1.3 and TLSv1.2 but the error still occurs.
Anyone have any suggestions on how to fix it? thanks
Check what super.send does, it creates a new connection without using the one you created. I'm not recommending using a self-signed cert and a custom truststore but you can set a default HostnameVerifier using
HttpsURLConnection.setDefaultHostnameVerifier.
Since this is static, it will work for all HttpsURLConnection instances so you don't need to inject anything into Micrometer.
The right solution would be either using a non-self-signed cert or a proper truststore (e.g.: via javax.net.ssl.trustStore).
I did a test with a simple change to the code I had posted and I solved it:
I copied all code of the super.send() method, adding the additional code to set the custom SslSocketFactory and all was OK!
so the reason was that
it creates a new connection without using the one you created
as Jonatan said... a my trivial mistake. :)
I have enabled ssl in my spring boot application and anytime I want to use a Rest service like googleapis or facebook I have to add the certificate in the truststore.
I use openssl to get the certificate:
openssl s_client -connect googleapis.com:443
and I import it into the truststore :
keytool.exe -import -noprompt -trustcacerts -alias googleapis.com
-file googleapis.com.cer -keystore app-server.p12 -storepass *****
The problem is that it's very inconvenient to manage, when the certificate expires I have to update the certificates in the truststore everytime.
Also even if I add the right certificate sometimes I get an error:
Caused by: javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
I know these certificates are valid so, is there a possibility to trust them automatically without adding them in the truststore?
Here is my ssl configuration
ssl:
enabled: true
key-store: classpath:keystore/app-server.p12
key-store-password: ******
key-alias: app-server
key-store-type: PKCS12
trust-store: classpath:keystore/app-server.p12
trust-store-password: *****
trust-store-type: PKCS12
keyStorePath: config/keystore/app-server.p12
Also I had to add system properties in the main method
private static String keyStorePath;
private static String keyStorePassword;
#Value("${server.ssl.keyStorePath}")
public void setKeyStorePath(String keyStorePath) {
ClientUiApplication.keyStorePath = keyStorePath;
}
#Value("${server.ssl.key-store-password}")
public void setKeyStorePassword(String keyStorePassword) {
ClientUiApplication.keyStorePassword = keyStorePassword;
}
public static void main(String[] args) {
SpringApplication.run(ClientUiApplication.class, args);
System.setProperty("javax.net.ssl.trustStore", keyStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keyStorePassword);
}
Another important detail, I need a truststore because I use self-signed certificates generated with Keytool. Without, my services cannot communicate with each other.
You give the code:
public static void main(String[] args) {
SpringApplication.run(ClientUiApplication.class, args);
System.setProperty("javax.net.ssl.trustStore", keyStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", keyStorePassword);
}
This means that you force the java trust store to be the trust store you provide. The default trust store is not in use anymore.
So yes, you have to add every needed root certificates in this trust store to not have the issue you describe.
Why do you need to have a specific trust store?
If there is no use, remove that.
If you have a specific additional certificate you have to trust, you'd better add this certificate to the default trust store (the jre/lib/security/cacerts file)
You can also Bypass SSL Certificate Checking using CloseableHttpClient
public static CloseableHttpClient getCloseableHttpClient()
{
CloseableHttpClient httpClient = null;
try {
httpClient = HttpClients.custom().
setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).
setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy()
{
public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException
{
return true;
}
}).build()).build();
} catch (KeyManagementException e) {
LOGGER.error("KeyManagementException in creating http client instance", e);
} catch (NoSuchAlgorithmException e) {
LOGGER.error("NoSuchAlgorithmException in creating http client instance", e);
} catch (KeyStoreException e) {
LOGGER.error("KeyStoreException in creating http client instance", e);
}
return httpClient;
}
It will trust automatically all the certificates and saves you from SSLHandshakeException: sun.security.validator.ValidatorException
Note this method is not recommended in Production
I'm trying to connect to my company's active diretory to create an appplication capable of adding, updating and removing users from it, I configured may LdapContextSource with all the information my collegue who created de AD environment but when I try to do a simple search it doesn't work and give me this error:
org.springframework.ldap.AuthenticationNotSupportedException: [LDAP: error code 8 - BindSimple: Transport encryption required.]; nested exception is javax.naming.AuthenticationNotSupportedException: [LDAP: error code 8 - BindSimple: Transport encryption required.]
Here is my simple code:
public User getUserByLogin(String login) {
LdapContextSource contextSource = new LdapContextSource();
try {
contextSource.setUrl("secret");
contextSource.setBase("secret");
contextSource.setUserDn("secret");
contextSource.setPassword("secret");
contextSource.afterPropertiesSet();
LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
User user = ldapTemplate.findOne(query().where("uid").is(login), User.class);
return user;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
May be relevant to let you guys to know that we're using GSS-API
Thanks in advance, hope someone can help me
The error indicates that transport encryption is required -- this generally means you need to change the LDAP server URI from ldap://server.domain.gTLD to ldaps://server.domain.gTLD but since few LDAP servers use certs issued from a public certificate authority (CA), you'll also need to set up the certificate trust. I use a private CA to sign all certificates, so can import the CA public key into the Java truststore.
I am able to establish connection to QManager using unsecure channel and was able to do what ever I want to do.
But now I am trying to connect to the same QManage through secure channel; I have got the Security keys ( both public and private key ) generate from MQ server but I am not sure how to incorporate the key and establish MQ connection.
I googled and found some answer which suggested the below approach but it didn't work.
System.setProperty("javax.net.ssl.trustStore","path to public key");
System.setProperty("javax.net.ssl.keyStore","path to private key");
Exception I got.
com.ibm.mq.MQException : MQJE001: Completion Code '2', Reason '2537'.
I would appreciate some guidance or sample code on how to connect to QManager using security key.
#JoshMc -- please find answer to your question below
Are you using IBM MQ Classes for Java or IBM MQ Classes for JMS?
I am using IBM MQ Classes for Java
What version of IBM MQ are the jar files you are using from?
Version 7
What version of MQ is the queue manager you are attempting to connect to?
Version 8
Are you attempting to have MQ validate a client cert (Check the value of SSLCAUTH on the SVRCONN channel)
Yes. I have public and private key generated from MQ server by MQ Admin (I have no access to MQ server) and need to use it to connect to the MQ server.
Paste any errors that show up in the queue managers AMQERR01.LOG when you attempt to connect.
I don't have access to the log file.
Below is working code; I am able to connect to unsecure channel and send message
public void MQSender(){
MQQueueManager QMgr = null;
try {
MQEnvironment.hostname = "hostname";
MQEnvironment.channel = "UNSECURE";
//MQEnvironment.channel = "SECURE";
MQEnvironment.port = 8080;
QMgr = new MQQueueManager("QManager");
int openOptions = MQConstants.MQOO_OUTPUT;
MQQueue queue = QMgr.accessQueue("QNAME",openOptions);
MQPutMessageOptions pmo = new MQPutMessageOptions();
pmo.options = MQConstants.MQPMO_LOGICAL_ORDER | MQConstants.MQPMO_SYNCPOINT;
MQMessage message = new MQMessage();
message.writeString("TEST");
queue.put(message, pmo);
QMgr.commit();
} catch (Exception e) {
if(QMgr!=null){
try {
QMgr.backout();
} catch (MQException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}
}
You are missing the option specifying the ciphersuite to use from your code:
MQEnvironment.sslCipherSuite
This knowledge center article may help: https://www.ibm.com/support/knowledgecenter/SSFKSJ_7.5.0/com.ibm.mq.dev.doc/q031220_.htm
And do you have your trusted certs and private cert/key pair in JKS stores?
These should be more like:
System.setProperty("javax.net.ssl.trustStore","path to JKS file containing certificates required to validate server certificate");
System.setProperty("javax.net.ssl.keyStore","path to JKS file containing certificate and private key of the client");