I want to create a servlet using 2 way ssl connector.
I created test2wayssl.jks and initiated SslSelectChannelConnector
When i send request from postman with client certificate, the response in postman is
There was an error connecting to 127.0.0.1:29226/2wayssl.
Here is my code below. But it does not work.
Server server = new Server(29226);
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath("2-way-ssl-authentication/test2wayssl.jks");
sslContextFactory.setKeyStorePassword("123456");
sslContextFactory.setKeyManagerPassword("123456");
sslContextFactory.setTrustAll(true);
SslSelectChannelConnector sslConnector = new SslSelectChannelConnector(sslContextFactory);
sslConnector.setAllowRenegotiate(true);
sslConnector.setHost("localhost");
sslConnector.setServer(server);
server.addConnector(sslConnector);
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(HelloServlet.class, "/2wayssl");
server.setHandler(handler);
try {
server.start();
} catch (Exception e) {
e.printStackTrace(); // TODO impl
}
Below is my servlet class
#SuppressWarnings("serial")
public static class HelloServlet extends HttpServlet
{
#Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException
{
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
response.getWriter().println("<h1>2 Way SSL Authentication</h1>");
}
}
Any help is appreciated.
SslSelectChannelConnector is from Jetty 8 and older which are now EOL/End of Life, and does not support client certificates, upgrade to supported and stable version of Jetty first.
How this is done with Jetty 9.4.27.v20200227 is by using the SslContextFactory.Server and one (or both) of the options
setWantClientAuth(true) which turns on JVM features on the SSL connection related to javax.net.ssl.SSLParameters.getWantClientAuth()
setNeedClientAuth(true) which turns on JVM features on the SSL connection related to javax.net.ssl.SSLParameters.getNeedClientAuth()
Example:
Server server = new Server();
int httpsPort = 8443;
// Setup HTTP Connector
HttpConfiguration httpConf = new HttpConfiguration();
httpConf.setSecurePort(httpsPort);
httpConf.setSecureScheme("https");
// Setup SSL
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStoreResource(findKeyStorePath());
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
sslContextFactory.setWantClientAuth(true); // Option 1
sslContextFactory.setNeedClientAuth(true); // Option 2
// Setup HTTPS Configuration
HttpConfiguration httpsConf = new HttpConfiguration();
httpsConf.setSecureScheme("https");
httpsConf.setSecurePort(httpsPort);
httpsConf.addCustomizer(new SecureRequestCustomizer()); // adds ssl info to request object
// Establish the HTTPS ServerConnector
ServerConnector httpsConnector = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory,"http/1.1"),
new HttpConnectionFactory(httpsConf));
httpsConnector.setPort(httpsPort);
server.addConnector(httpsConnector);
// Add a Handlers for requests
HandlerList handlers = new HandlerList();
handlers.addHandler(new SecuredRedirectHandler());
handlers.addHandler(new HelloHandler("Hello Secure World"));
server.setHandler(handlers);
server.start();
server.join();
Related
I am trying to add proxy settings to a Java Swing client app, which connects and gets data over https from an external server. However the ClientResource (restlet:2.4.0) ignores all efforts with parameters and connects directly to the url? If the syntax is correct, what are the correct parameters?
Further, how can I use system proxy settings?
private static ClientResource getClientResource(String url) {
ClientResource clientResource = null;
try {
// test
Client client = new Client(new Context(), Protocol.HTTPS);
client.getContext().getParameters().add("https.proxyHost", "PROXY_IP");
client.getContext().getParameters().add("https.proxyPort", "PROXY_PORT");
clientResource = new ClientResource(url);
// test
clientResource.setNext(client);
} catch (Exception e) {
e.printStackTrace();
}
return clientResource;
}
private static Response sendGetRequest(String url) {
ClientResource resource = getClientResource(BASE_URL + url);
try {
resource.get();
} catch (ResourceException e){
e.printStackStrace();
return null;
}
return getResponse();
}
EDIT added compiles:
compile 'org.restlet.jse:org.restlet:2.3.12'
compile 'org.restlet.jse:org.restlet.ext.jackson:2.3.12'
// switch to Apache Http Client, enable proxy'
compile 'org.restlet.jse:org.restlet.ext.httpclient:2.3.12'
// httpClient for Class Definitions
compile 'org.apache.httpcomponents:httpclient:4.3'
CURRENT EXCEPTION:
Starting the Apache HTTP client
An error occurred during the communication with the remote HTTP server.
org.apache.http.client.ClientProtocolException
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:867)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
at org.restlet.ext.httpclient.internal.HttpMethodCall.sendRequest(HttpMethodCall.java:339)
at org.restlet.engine.adapter.ClientAdapter.commit(ClientAdapter.java:105)
at org.restlet.engine.adapter.HttpClientHelper.handle(HttpClientHelper.java:119)
at org.restlet.Client.handle(Client.java:153)
I think this is only supported with the httpClient extension, that relies on the Apache HTTP client library (maven artifact id: org.restlet.ext.httpclient).
You can then either use the system environment properties: http.proxyHost and http.proxyPort, or set these parameters on the client instance (as you did, but names are distinct and documented here ).
Client client = new Client(new Context(), Protocol.HTTPS);
client.getContext().getParameters().add("proxyHost", "PROXY_IP");
client.getContext().getParameters().add("proxyPort", "PROXY_PORT");
I am trying to authenticate against a SOAP webservice using NTLM authentication as mentioned at Apache CXF with stack as following -
jcifs-1.3.17.jar
cxf-2.7.11
NTLM + SSL
JDK 5 (I possibly cannot change this)
Every time I try and connect it refuses with 401 unauthorized access because it uses my underlying NT credentials which are not authorised instead of valid ones that I configured in code. (I had to modify jCIFS as it doesnt support SSL + NTLM to return HTTPs version of NtlmHttpURLConnection). Similar result when used HTTP Async mechanism.
String domainController = "xxx.xxx.xxx";
UniAddress dc = UniAddress.getByName(domainController, true);
jcifs.Config.setProperty("http.auth.ntlm.domain", "xxx.xxx.xxx");
jcifs.Config.setProperty("jcifs.smb.client.domain", "domain");
jcifs.Config.setProperty("jcifs.netbios.wins", dc.getHostAddress());
jcifs.Config.setProperty("jcifs.smb.client.soTimeout", "300000"); // 5 minutes
jcifs.Config.setProperty("jcifs.netbios.cachePolicy", "1200"); // 20 minutes
jcifs.Config.setProperty("jcifs.smb.client.username", USER);
jcifs.Config.setProperty("jcifs.smb.client.password", PWD);
//Register the jcifs URL handler to enable NTLM
jcifs.Config.registerSmbURLHandler();
HelloWorld src = new HelloWorld();
ClientProxyFactoryBean factory = new ClientProxyFactoryBean(new JaxWsClientFactoryBean());
factory.setServiceClass( IHelloWorld.class );
factory.setAddress(SERVICE_URL);
factory.setUsername(USER);
factory.setPassword(PWD);
IHelloWorld service = (IHelloWorld ) factory.create();
Client client = ClientProxy.getClient(service);
HTTPConduit http = (HTTPConduit) client.getConduit();
System.out.println(http.getClass().getName());
//org.apache.cxf.transport.http.URLConnectionHTTPConduit
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(36000);
httpClientPolicy.setAllowChunking(false);
http.setClient(httpClientPolicy);
http.getAuthorization().setAuthorizationType("NTLM");
http.getAuthorization().setUserName(USER);
http.getAuthorization().setPassword(PWD);
http.getClient().setAllowChunking( false );
http.getClient().setAutoRedirect( true );
TLSClientParameters tcp = new TLSClientParameters();
tcp.setTrustManagers( new TrustManager[]{ new TrustAllX509TrustManager() } );
http.setTlsClientParameters( tcp );
System.out.println("Invoking service...");
String msg= "echo";
try {
String res = service.readMessage(msg);
System.out.println("readMessage.result=" + res);
} catch (Exception e) {
e.printStackTrace();
}
Upong running this code I get following exception trace
: domain\ is unauthorized user at
sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown
Source) at
sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown
Source) at java.lang.reflect.Constructor.newInstance(Unknown Source)
at
org.apache.cxf.interceptor.ClientFaultConverter.processFaultDetail(ClientFaultConverter.java:175)
at
org.apache.cxf.interceptor.ClientFaultConverter.handleMessage(ClientFaultConverter.java:78)
at
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
at
org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:113)
at
org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69)
at
org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34)
at
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:845)
at
org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1624)
at
org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1513)
at
org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1318)
at
org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
at
org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:632)
at
org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
at
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:570)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:479) at
org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:382) at
org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:335) at
org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
at org.apache.cxf.frontend.ClientProxy.invoke(ClientProxy.java:81)
at com.sun.proxy.$Proxy44.readMessage(Unknown Source)
CXF 2.7.x does not support JDK 5. From the CXF FAQ:
Can CXF run with JDK 1.5?
Yes for CXF 2.6.x and older. Keep in mind though that Java 2 SE 5.0
with JDK 1.5 has reached end of life (EOL). CXF 2.7.x no longer
supports Java 5. In order to upgrade to 2.7.x, you must be using Java
6 (or newer).
After hours of struggle trying to juggle between JDK 5 stack and other SOAP framework like Axis2 and CXF, I finally manged to do with a raw SOAP client that can pretty much do the job I neeed. Following is code that helped get it done even with custom NT logins than underlying ones.
public final class JCIFSEngine implements NTLMEngine {
private static final int TYPE_1_FLAGS = NtlmFlags.NTLMSSP_NEGOTIATE_56
| NtlmFlags.NTLMSSP_NEGOTIATE_128
| NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2
| NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
| NtlmFlags.NTLMSSP_REQUEST_TARGET;
public String generateType1Msg(final String domain, final String workstation)
throws NTLMEngineException {
final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS,
domain, workstation);
return Base64.encode(type1Message.toByteArray());
}
public String generateType3Msg(final String username,
final String password, final String domain,
final String workstation, final String challenge)
throws NTLMEngineException {
Type2Message type2Message;
try {
type2Message = new Type2Message(Base64.decode(challenge));
} catch (final IOException exception) {
throw new NTLMEngineException("Invalid NTLM type 2 message",
exception);
}
final int type2Flags = type2Message.getFlags();
final int type3Flags = type2Flags
& (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
final Type3Message type3Message = new Type3Message(type2Message,
password, domain, username, workstation, type3Flags);
return Base64.encode(type3Message.toByteArray());
}
}
public class JCIFSNTLMSchemeFactory implements AuthSchemeProvider {
public AuthScheme create(final HttpContext context) {
return new NTLMScheme(new JCIFSEngine());
}
}
The use HttpClient object to register custom NTLM Engine and Auth scheme registry -
protected Registry<AuthSchemeProvider> getAuthRegistry() {
Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder
.<AuthSchemeProvider> create()
.register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
.build();
return authSchemeRegistry;
}
protected CredentialsProvider getCredentialsProvider(String user,
String pass, String domain) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST,
AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.NTLM),
new NTCredentials(user, pass, null, domain));
return credsProvider;
}
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setDefaultAuthSchemeRegistry(getAuthRegistry());
httpClientBuilder
.setDefaultCredentialsProvider(getCredentialsProvider(
config.getUserName(), config.getPassword(),
config.getDomain()));
if (config.isProxy()) {
HttpHost proxy = new HttpHost(config.getProxyHost(),
config.getPort());
httpClientBuilder.setProxy(proxy);
}
httpClientBuilder.build();
Hope this helps someone with similar issue. Cheers
One more to the list of the mysterious "peer not authenticated".
I have an apache httpclient using 4.2 lib. I have explicitly set to trust all certificates in the code.
I have a Tomcat server (JRE 1.7U45), serving the requests on Linux. The server has a self signed certificate.
Client side code:
private DefaultHttpClient getHttpsClient() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
final SSLSocketFactory sf;
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
}
} }, new SecureRandom());
sf = new SSLSocketFactory(sslContext,
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme(url.getScheme(), url.getPort(), sf));
ClientConnectionManager cm = new BasicClientConnectionManager(
registry);
return new MyDefaultHttpClient(cm);
} catch (Exception e) {
return new MyDefaultHttpClient();
}
}
This error is only seen intermittently on "Solaris 5.10" (32 bit JRE 1.7.0u45) clients talking to the server.
Sometime, the request on the same box go thru fine, but at other times, this just throws "Peer Not Authenticate"
I have other flavors of OS clients, where the call is going thru just fine.
Would any of have any suggestions/pointers to look into this issue?
More Update:
Ran the ssl debug on the server and we see that intermittently, it throws
http-bio-8443-exec-7, handling exception: javax.net.ssl.SSLHandshakeException: Invalid Padding length: 105
http-bio-8443-exec-7, IOException in getSession(): javax.net.ssl.SSLHandshakeException: Invalid Padding length: 105
This was due the following bug in JRE 1.7 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8013059
Also, the apache httpclient 4.2 added to the confusion, where it masking the actual exception thrown instead throwing the generic "Peer not authenticated"
In the server.xml of tom-cat, for connector element, add the cipher attribute with a list of non-DH ciphers
E.g.
ciphers="SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"
This solved the issue.
Hope this is useful to someone.
Thanks
We have an issue in our environment when using Websphere to attempt to connect to an external system with HttpClient 4.x (current version is 4.2.1). Connecting to the external system is fine with their certificate being installed in Websphere with no additional configuration of HttpClient. However, when they enabled mutual authentication, it no longer works and we get a SSLPeerUnverifiedException exception:
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated,
at com.ibm.jsse2.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:105),
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128),
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572),
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180),
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294),
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640),
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479),
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906),
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:1066),
I was provided the following code sample, and I was wondering if there's any way to configure HttpClient to use an explicit alias like this code sample does. I've tried to find good documentation on using SSL mutual authentication with HttpClient 4 and haven't been able to find much.
Here's the code sample:
private HttpURLConnection getConnection(String server, String machine,
String port) throws Exception {
URL u = new URL(server);
HttpsURLConnection connection = (HttpsURLConnection) u.openConnection();
String alias = "CellDefaultSSLSettings";
final HashMap connectionInfo = new HashMap();
connectionInfo.put(JSSEHelper.CONNECTION_INFO_DIRECTION,
JSSEHelper.DIRECTION_OUTBOUND);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_HOST, machine);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_PORT, port);
javax.net.ssl.SSLSocketFactory sslFact = JSSEHelper.getInstance()
.getSSLSocketFactory(alias, connectionInfo, null);
connection.setSSLSocketFactory(sslFact);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
return connection;
}
Basically, how do I make HttpClient use "CellDefaultSSLSettings"?
Fundamentally this problem has nothing to do with HttpClient. HttpClient can be configured to establish HTTPS connections using any custom SSLContext or SSLSocketFactory instance. This is basically about how to use JSSE APIs to configure SSLContext in the right way. In your particular case JSSEHelper does all the hard work for you.
// JSSE socket factory
javax.net.ssl.SSLSocketFactory jssesf = JSSEHelper.getInstance().getSSLSocketFactory(alias, connectionInfo, null);
// HC socket factory
SSLSocketFactory hcsf = new SSLSocketFactory(jssesf, SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
This will give a connection socket factory that can be registered with the connection manager.
HttpClient 4.3 also comes with SSLContextBuilder class which can be used to assemble custom SSL configurations using fluid builder API.
https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/conn/ssl/SSLContextBuilder.java
oleg's answer helped me out.
What I did was extend the DefaultHttpClient, and each constructor takes a String argument for the destination URL and calls a method setupScheme:
private void setupScheme(final String url) throws Exception {
Scheme scheme = new Scheme("https", 443, retrieveWebsphereSSLConnectionFactory(url));
getConnectionManager().getSchemeRegistry().register(scheme);
}
The method retrieveWebsphereSSLConnectionFactory essentially combines the code from the sample with the code oleg provided:
private SchemeSocketFactory retrieveWebsphereSSLConnectionFactory(final String url)
throws SSLException, URISyntaxException {
final String alias = "CellDefaultSSLSettings";
final HashMap<String, String> connectionInfo = new HashMap<String, String>();
connectionInfo.put(JSSEHelper.CONNECTION_INFO_DIRECTION, JSSEHelper.DIRECTION_OUTBOUND);
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_HOST,
URIUtils.extractHost(new URI(url)).getHostName());
connectionInfo.put(JSSEHelper.CONNECTION_INFO_REMOTE_PORT, "443");
return new SSLSocketFactory(JSSEHelper.getInstance().getSSLSocketFactory(alias, connectionInfo, null),
SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
}
What is the best way to process HTTP GET Method with SSL using HTTP Components HTTPClient 4 Project?
what is the best way to parametrized certification info? properties file? reload method to Daemon Service?
HttpClient httpClient = new DefaultHttpClient();
String url = "https://xxx.190.2.45/index.jsp";
HttpGet get = new HttpGet(url);
try {
//TODO
HTTPHelper.addSSLSupport(httpClient);
HttpResponse response = httpClient.execute(get);
BasicResponseHandler responseHandler = new BasicResponseHandler();
String responseString = responseHandler.handleResponse(response);
} catch (ClientProtocolException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
You'll need to enable the SSL support, see the tutorial for more information
I'm under the impression that you're using a self-signed certificate for the server. What you probably should do is look at getting openssl, generate yourself a CA & server certificate. Put the CA certificate (not the private key) in a "trust store" and configure the socket factory.
If you need more detail on how to do this, just comment on this and I'll flesh out some more. I've had great success with simple local projects!