Hi,
The problem I reached is quite specific. I've searched Internet for any
hint but without success. So I would like to ask you for any pointers.
I have two tomcat servers "A" and "B":
A - tomcat 7.0.41 (local machine Linux Mint 15)
B - tomcat 7.0.42 (Ubuntu 12.04.2 on AWS EC2)
************************************************************************************************************************
In B I have defined REST service:
web.xml:
...
<servlet>
<servlet-name>service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.systemincloud.machine.connector.service.cmd</param-value>
</init-param>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.systemincloud.machine.connector.service.Service</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>service</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
server.xml:
<Connector port="443" protocol="HTTP/1.1"
SSLEnabled="true" ciphers="SSL_RSA_WITH_RC4_128_SHA"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keystorePass="******"
truststoreFile="../.truststore" truststorePass="******"/>
The first REST request to B is coming from server A. I am using
jersey-client-2.0.jar:
public String newInstance(String dns, String fileName, InputStream file,
Map<String, String> parameters) {
WebTarget service = getService(dns);
FormDataMultiPart multiPart = new FormDataMultiPart();
multiPart.field("fileName", fileName);
multiPart.bodyPart(new FormDataBodyPart("model", file,
MediaType.APPLICATION_OCTET_STREAM_TYPE));
Form f = new Form(new MultivaluedHashMap<String, String>(parameters));
multiPart.field("parameters", f,
MediaType.APPLICATION_FORM_URLENCODED_TYPE);
NewInstanceResponse response =
service.path(PATH).path("newInstance").request(MediaType.APPLICATION_JSON).post(Entity.entity(multiPart,
MediaType.MULTIPART_FORM_DATA_TYPE), NewInstanceResponse.class);
return response.getSystemInstanceId();
}
In InputStream I have a file (jar) from local hardrive.
And I have no problems with such a configuration and application work as
expected. In wireshark I get:
After a lot of:
5173 77.673830000 192.168.30.144 54.229.*.* TCP 1514 [TCP segment of a
reassembled PDU]
I get (however only this POST wireshark is not decrypting):
52215 110.765747000 54.229.*.* 192.168.30.144 HTTP 312 HTTP/1.1 200 OK
(application/json)
****************************************************************************************************************************
Problem starts when I configure ssl client authentication for REST service:
web.xml
...
<security-constraint>
<web-resource-collection>
<web-resource-name>Service Api</web-resource-name>
<url-pattern>/service/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>sic-service</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>Service Api</realm-name>
</login-config>
<security-role>
<role-name>sic-service</role-name>
</security-role>
...
tomcat-users.xml:
...
<role rolename="sic-service"/>
<role rolename="manager-script"/>
<user username="CN=*.*.*.*, OU=System in Cloud, O=System in Cloud,
L=Cracow, ST=Malopolskie, C=PL"
password="null"
roles="sic-service"/>
...
In server A I have:
X509KeyManager dkms = null;
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(new File(keystore_path)),
keystore_password.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509",
"SunJSSE");
kmf.init(ks, keystore_password.toCharArray());
KeyManager kms[] = kmf.getKeyManagers();
for (int i = 0; i < kms.length; i++) {
if (kms[i] instanceof X509KeyManager) dkms = (X509KeyManager)
kms[i];
}
} catch (KeyStoreException | NoSuchAlgorithmException |
CertificateException| IOException | NoSuchProviderException |
UnrecoverableKeyException e1) {
e1.printStackTrace();
}
KeyManager defaultKms[] = new KeyManager[] { dkms };
TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers(){return null;}
public void checkClientTrusted(X509Certificate[] certs, String
authType){}
public void checkServerTrusted(X509Certificate[] certs, String
authType){}
}};
SSLContext sc = null;
try {
sc = SSLContext.getInstance("TLS");
sc.init(defaultKms, trustAllCerts, new SecureRandom());
} catch (Exception e) {
e.printStackTrace();
}
client = ClientBuilder.newBuilder()
.withConfig(config)
.hostnameVerifier(new HostnameVerifier() {
@Override public boolean verify(String hostname, SSLSession session) {
return true; } })
.sslContext(sc)
.build();
I tested authetication with WizTools REST Client 3.2. however with REST
requests without multipart form.
Now. When I run above application on server A
In wireshark I have:
around only 40
439 15.718628000 192.168.30.144 54.229.101.121 TCP 1514 [TCP segment of a
reassembled PDU]
then 7 times
475 15.808478000 192.168.30.144 54.229.101.121 TLSv1 1514 Ignored Unknown
Record
then
486 15.835327000 54.229.101.121 192.168.30.144 HTTP 1282 HTTP/1.1 401
Unauthorized (text/html)
487 15.851888000 54.229.101.121 192.168.30.144 HTTP 235 HTTP/1.1 400 Bad
Request
And there is one re-transmission of whole communication (with Key Exchange)
with the same result.
In server A, I have error:
SEVERE: Servlet.service() for servlet [API] in context with path [] threw
exception [javax.ws.rs.ProcessingException: java.io.IOException: Error
writing to server] with root cause
java.io.IOException: Error writing to server
at
sun.net.www.protocol.http.HttpURLConnection.writeRequests(HttpURLConnection.java:625)
at
sun.net.www.protocol.http.HttpURLConnection.writeRequests(HttpURLConnection.java:637)
at
sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1320)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:468)
at
sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
at
org.glassfish.jersey.client.HttpUrlConnector._apply(HttpUrlConnector.java:293)
at
org.glassfish.jersey.client.HttpUrlConnector.apply(HttpUrlConnector.java:200)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:215)
at
org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:650)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at
org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:421)
at
org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:646)
at
org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:402)
at
org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:305)
at
com.systemincloud.service.cmd.MachineClient.newInstance(MachineClient.java:117)
at
com.systemincloud.service.cmd.NewInstanceExecutor.execute(NewInstanceExecutor.java:28)
at
com.systemincloud.service.connector.cmd.NewInstanceCmd.post(NewInstanceCmd.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at
org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
...
In server B I have:
catalina.out:
...
Aug 21, 2013 1:28:37 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 10522 ms
Aug 21, 2013 1:29:38 PM org.apache.coyote.http11.AbstractHttp11Processor
process
INFO: Error parsing HTTP request header
Note: further occurrences of HTTP header parsing errors will be logged at
DEBUG level.
localhost.2013-08-21.log
Aug 21, 2013 1:28:34 PM org.apache.catalina.core.StandardContext
listenerStart
FINE: Sending application start events
Aug 21, 2013 1:28:34 PM org.apache.catalina.core.StandardContext filterStart
FINE: Starting filters
Aug 21, 2013 1:29:38 PM org.apache.catalina.authenticator.SSLAuthenticator
authenticate
FINE: Looking up certificates
Aug 21, 2013 1:29:38 PM org.apache.catalina.authenticator.SSLAuthenticator
authenticate
FINE: Looking up certificates
localhost_access_log.2013-08-21.txt:
91.198.177.112 - - [21/Aug/2013:13:29:38 +0000] "POST /service/newInstance
HTTP/1.1" 401 1037
91.198.177.112 - - [21/Aug/2013:13:29:38 +0000] "??
)????>H??^Tj??)c??^S??\%^R?????*??;,? ^D0(? ^DsE??+?^NlZF2^L?Bk?" 400 -
91.198.177.112 - - [21/Aug/2013:13:29:38 +0000] "POST /service/newInstance
HTTP/1.1" 401 1037
91.198.177.112 - - [21/Aug/2013:13:29:38 +0000] "??
)????>H??^Tj??)c??^S??\%^R?????*??;,? ^D0(? ^DsE??+?^NlZF2^L?Bk?" 400 -
**********************************************************************************
Other observations:
+ Non multipart requests work with no problems.
+ problems do not depend on type of file content (tried txt, jar), name or
size (tried from 800kB to 50 MB);
+ If just before multipart request I do simple request:
WebTarget service = getService(dns);
service.path(PATH).path("newToken").request(MediaType.APPLICATION_JSON)
.post(Entity.entity(new NewTokenRequest(token),
MediaType.APPLICATION_JSON), NewTokenResponse.class);
the next multipart request is correctly received on server B. And this is
for me a workaround for today.
My knowledge of SSL is basic so I still haven't done deeper analyse in
protocol. Can you give me some directions what can I try to solve my
problem.
Thank you,
Marek
--
Marek Jagielski
+48 513 402 596