Tomcat: java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens - tomcat8

I am getting below stack trace when I am deploying my application in a multi-server Apache Tomcat 8 environment. I am getting this error frequently, and it seems it is blocking the tomcat thread:
INFO [http-nio-80-exec-4461] org.apache.coyote.http11.AbstractHttp11Processor.process Error parsing HTTP request header
 Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
 java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
 at org.apache.coyote.http11.AbstractNioInputBuffer.parseRequestLine(AbstractNioInputBuffer.java:233)
 at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1017)
 at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684)
 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1524)
 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1480)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
 at java.lang.Thread.run(Unknown Source)
Can any one direct me how to troubleshoot or narrow down such an exeption? I am not getting any reference to any of my application source files. I tried to google around, and in of the links it said, you are trying to access http url through https, which seems unlikely. I am not getting this error, when the application runs on a single Tomcat 8 instance. I get this only in a multi-server environment.
I am also sharing the meta tags I have embedded on each page, if that helps to identify the cause.
<%
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Cache-Control", "no-store");
response.setDateHeader("Expires", 0);
response.setHeader("Pragma", "no-cache");
%>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1">
I am also using the following in a few pages, which basically is same as above:
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Expires" content="-1" />
<meta http-equiv="Cache-Control" content="private" />
<meta http-equiv="Cache-Control" content="no-store" />
<meta http-equiv="Pragma" content="no-cache" />
Even if anyone helps in giving a direction to my troubleshooting attempt, that will be useful, as currently I have no idea where to look into.

This exception can occur when you try to execute HTTPS request from client on endpoint which isn't HTTPS enabled. Client will encrypt request data when server is expecting raw data.

I got the same exception when I locally tested. The problem was a URL schema in my request.
Change https to http in your client URL.
Probably it will helps.

You are calling local server with http://localhost:8080/foo/bar. Call it with https://localhost:8080/foo/bar. This solves the problem

In case someone is using swagger:
Change the Scheme to HTTP or HTTPS, depend on needs, prior to hit the execute.
Postman:
Change the URL Path to http:// or https:// in the url address

I received this exception unrelated to any TLS issues.
In my case the Content-Length header value did not match the body length.

I know this is an old thread, but there is a particular case when this may happen:
If you are using AWS api gateway coupled with a VPC link, and if the Network Load Balancer has proxy protocol v2 enabled, a 400 Bad Request will happen as well.
Took me the whole afternoon to figure it out, so if it may help someone I'd be glad :)

I got this error resolved by doing 2 things in chrome browser:
Pressed Ctrl + Shift + Delete and cleared all browsing data from beginning.
Go to Chrome's : Settings ->Advanced Settings -> Open proxy settings -> Internet Properties then Go to the Content window and click on the Clear SSL State Button.
This site has this information and other options as well : https://www.thesslstore.com/blog/fix-err-ssl-protocol-error/

In my case I had to clear browser history/cookies to get rid of this error.

I had this issue when working on a Java Project in Debian 10 with Tomcat as the application server.
The issue was that the application already had https defined as it's default protocol while I was using http to call the application in the browser. So when I try running the application I get this error in my log file:
INFO [http-nio-80-exec-4461] org.apache.coyote.http11.AbstractHttp11Processor.process Error parsing HTTP request header
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
I however tried using the https protocol in the browser but it didn't connect throwing the error:
Here's how I solved it:
You need a certificate to setup the https protocol for the application. You can obtain certificates from Let's Encrypt. For me the easiest route was creating a obtaining a self-signed certificate. .
I first had to create a keystore file for the application, more like a self-signed certificate for the https protocol:
sudo keytool -genkey -keyalg RSA -alias tomcat -keystore /usr/share/tomcat.keystore
Note: You need to have Java installed on the server to be able to do this. Java can be installed using sudo apt install default-jdk.
Next, I added a https Tomcat server connector for the application in the Tomcat server configuration file (/opt/tomcat/conf/server.xml):
sudo nano /opt/tomcat/conf/server.xml
Add the following to the configuration of the application. Notice that the keystore file location and password are specified. Also a port for the https protocol is defined, which is different from the port for the http protocol:
<Connector protocol="org.apache.coyote.http11.Http11Protocol"
port="8443" maxThreads="200" scheme="https"
secure="true" SSLEnabled="true"
keystoreFile="/usr/share/tomcat.keystore"
keystorePass="my-password"
clientAuth="false" sslProtocol="TLS"
URIEncoding="UTF-8"
compression="force"
compressableMimeType="text/html,text/xml,text/plain,text/javascript,text/css"/>
So the full server configuration for the application looked liked this in the Tomcat server configuration file (/opt/tomcat/conf/server.xml):
<Service name="my-application">
<Connector protocol="org.apache.coyote.http11.Http11Protocol"
port="8443" maxThreads="200" scheme="https"
secure="true" SSLEnabled="true"
keystoreFile="/usr/share/tomcat.keystore"
keystorePass="my-password"
clientAuth="false" sslProtocol="TLS"
URIEncoding="UTF-8"
compression="force"
compressableMimeType="text/html,text/xml,text/plain,text/javascript,text/css"/>
<Connector port="8009" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="my-application" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
This time when I tried accessing the application from the browser using:
https://my-server-ip-address:https-port
In my case it was:
https:35.123.45.6:8443
it worked fine. Although, I had to accept a warning which added a security exception for the website since the certificate used is a self-signed one.
That's all.
I hope this helps

Answering this old question (for others which may help)
Configuring your httpd conf correctly will make the problem solved. Install any httpd server, if you don't have one.
Listing my config here.
[smilyface#box002 ~]$ cat /etc/httpd/conf/httpd.conf | grep shirts | grep -v "#"
ProxyPass /shirts-service http://local.box002.com:16743/shirts-service
ProxyPassReverse /shirts-service http://local.box002.com:16743/shirts-service
ProxyPass /shirts http://local.box002.com:16443/shirts
ProxyPassReverse /shirts http://local.box002.com:16443/shirts
...
...
...
edit the file as above and then restart httpd as below
[smilyface#box002 ~]$ sudo service httpd restart
And then request with with https will work without exception.
Also request with http will forward to https ! No worries.

I was getting the same exception, whenever a page was getting loaded,
NFO: Error parsing HTTP request header
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
at org.apache.coyote.http11.InternalInputBuffer.parseRequestLine(InternalInputBuffer.java:139)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1028)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
I found that one of my page URL was https instead of http, when I changed the same, error was gone.

This usually happens when you are using a URI scheme that is not supported by the server in which the app is deployed. So, you might either want to check what all schemes your server supports and modify your request URI accordingly, or, you might want to add the support for that scheme in your server. The scope of your application should help you decide on this.

It happened to me when I had a same port used in ssh tunnel SOCKS to run Proxy in 8080 port and my server and my firefox browser proxy was set to that port and got this issue.

You can also try turning off the SSL option in settings, in case you are sending it through POSTMAN

I solved this problem by simply turning off my vpn.

Related

Client certificate based authentication HAProxy and a general questions

I want to add to a Tomcat servlet (which is behind a HAProxy server) client based authentication so what I did was
I've updated Tomcat configuration by adding
<Connector port="18443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="want" sslProtocol="TLS"
keystoreFile="conf/cert/server-keystore.jks"
keystorePass="changeit"
truststoreFile="conf/cert/server-truststore.jks"
truststorePass="changeit" />
P.S more info on https://docs.bmc.com/docs/rsso1908/configuring-the-tomcat-server-for-certificate-based-authentication-907302408.html
I also updated my HAProxy configuration by updating several lines, i.e
listen VIP
bind 172.16.200.85:443 transparent ssl crt /etc/haproxy/cert/server.pem ca-file /etc/haproxy/cert/ca.crt verify required crl-file /etc/haproxy/cert/root_crl.pem
P.S more info on https://www.loadbalancer.org/blog/client-certificate-authentication-with-haproxy/
and when I navigate to a page I get prompted for a certificate immediately, and I don't want that. What I want is a specific path, i.e /login/me to prompt for a certificate to choose. Basically, I want the same solution for client certificate authentication as on
https://secure.login.gov/
--> select "Sign in with your government employee ID"
--> click on the button "Insert your PIV/CAC" [you get a certificate list to choose from]
My questions:
How they are able to to accomplish client certificate based authentication via (it seems) a path "/login/piv_cac"?
I'm asking this, because I've found answers on a stackoverflow configure tomcat for client authentication only for specific URL patterns that this is not possible to accomplish. I've also tried my self, but I get always prompted for a client certificate upon connecting to a Tomcat instance (before navigating to an authentication url)
Is it doable with two Tomcat instances behind a HAProxy?
If so, what would be a general HAProxy configuration [or steps] for this?
If not, what do I need to make it happen?
Do I need one Tomcat instance for "casual" human beings and other Tomcat instance for "certified" human beings?

Tomcat 9.x.x Client Authentication using X.509 Certificates

I’m using Tomcat 9.0.19 and trying to enable X.509 cert.-based client authentication (AKA I&A) for a particular Web application.
In summary, the Tomcat works for an application that has basic I&A enabled over one-way TLS. When accessing the Web application that has certificate-based I&A, Tomcat does not seem to request a client certificate as part of the Server Hello message, prior to sending Server Hello Done and it later fails the authentication check:
02-Jan-2020 13:00:40.371 FINE [https-jsse-nio-443-exec-10] org.apache.catalina.authenticator.SSLAuthenticator.doAuthenticate Looking up certificates
02-Jan-2020 13:00:40.830 FINE [https-jsse-nio-443-exec-10] org.apache.catalina.authenticator.SSLAuthenticator.doAuthenticate No certificates included with this request
Traced the TLS flow in Wireshark and saw the TLS 1.2 handshake. Shortly after encrypted data is exchanged, the Tomcat sends an “Encrypted Alert” message and the socket is closed. Trying to contact the Tomcat from the browser, doing a GET. The browser does not prompt me to select a certificate, which also seems to point to Tomcat not requesting it from the browser.
Any help will be greatly appreciated!
More Details:
We have a set of certificates for the Tomcat and the client, issued by an Intermediate CA, which is signed (issued) by a Root CA. The trust stores have been setup on both sides (client and server) as well as key stores with the right certs/keys in them. The Web application is setup to require certificate I&A (web.xml):
<security-constraint>
<web-resource-collection>
<web-resource-name>All by default</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>OTService</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>certificate</realm-name>
</login-config>
The OTService role is setup in the Tomcat-Users.xml, along with a single user account:
Now, the Connector in server.xml is configured as follows:
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="100" SSLEnabled="true" scheme="https" secure="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="/apache-tomcat-9.0.19/conf/km/keyStore.jks"
certificateKeystorePassword="PASSWORD"
certificateKeyAlias="tomcat"
type="RSA" />
truststoreFile="/apache-tomcat-9.0.19/conf/km/trust_store.jks"
truststorePass="PASSWORD"
truststoreType="JKS"
certificateVerification="required"
clientAuth="true"
protocols="TLSv1.2"
</SSLHostConfig>
</Connector>
Any ideas why Tomcat would not request a client certificate?
The first issue that I discovered was that Tomcat ignored the Connector->SSLHostConfig settings for the trust store and used the JRE default trust store anyway. The way I discovered it was to have a browser save the negotiated TLS session key to a file (Google SSLKEYLOGFILE), then configured the Wireshark to use that file, captured the browser-Tomcat session and then was able to see every message in plaintext.
Next, I discovered that Tomcat was actually asking for a client cert., but the list of accepted Root CAs it was sending was from the default JRE cacerts file, not from the file specified by the truststoreFile attribute. Can have Tomcat use a different file across the board by adding a setenv.sh file to the Tomcat bin directory with Java properties to override default trust store location.
Now, I was in business, the browser was able to complete the TLS handshake, but then the authentication and authorization steps were failing. I finally determinate that the proper way to provide the cert. subject field in tomcat_users.xml file was not "CN=OU Client, OU=Control Systems, O=IoTOY, L=Scottsdale, S=AZ, C=US", but "CN=OU Client, OU=Control Systems, O=IoTOY, L=Scottsdale, ST=AZ, C=US". Finally, I had 2-way TLS working.
One thing to keep in mind is if anything running on the Tomcat attempts to connect over TLS to another system that uses commercial CA certs, it will fail because the truststore you're using now does not have commercial Root CAs' certs. One way to remediate it is to make a copy of the default JRE cacerts file and add your system-specific CA cert(s) to it and point to it from the setenv.sh file noted above.
When you have:
<Connector ...>
<SSLHostConfig>
<Certificate A=1 B=2 C=3 />
D=4 E=5 F=6
</SSLHostConfig>
</Connector>
then A,B,C are attributes of the Certificate object but D,E,F are NOT attributes of the SSLHostConfig object -- they are XML content which is different. Attributes need to be put IN THE TAG:
<Connector ... >
<SSLHostConfig certificateVerification="required" truststoreFile=... >
<Certificate ...keystore... />
</SSLHostConfig>
</Connector>
and that does cert-request on the initial handshake as desired (for me, tested on tomcat 9.0.14).

org.apache.coyote.http11.AbstractHttp11Processor.process Error parsing HTTP request header

24-Oct-2013 21:53:05.288 INFO [http-apr-8443-exec-3] org.apache.coyote.http11.AbstractHttp11Processor.process Error parsing HTTP request header
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
24-Oct-2013 21:53:05.288 INFO [http-apr-8443-exec-4] org.apache.coyote.http11.AbstractHttp11Processor.process Error parsing HTTP request header
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
I am getting the above error when using HTTPS configuration in Tomcat 8.0 0r Tomcat 7.
Please help. My tomcat HTTPS configuration goes like this.....
<Connector
className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8443"
enableLookups="false"
acceptCount="10"
connectionTimeout="60000"
scheme="https" secure="true" >
<Factory
className="org.apache.coyote.tomcat4.CoyoteServerSocketFactory"
clientAuth="false" protocol="TLS"
keystoreFile="D:\Samplekey.key" keystorePass="PASSWORD" />
</Connector>
Your configuration is from Tomcat 4 but the error message has to be from at least Tomcat 7. The chances of Tomcat 4 SSL configuration working in Tomcat 7 are zero. Follow the Tomcat docs and correctly configure SSL for which ever version of Tomcat you are using.

Setting up SSL in JBoss AS 7

I am attempting to get SSL set up in JBoss Application Server 7. I want http and https so I added:
<connector name="https" scheme="https" protocol="HTTP/1.1" secure ="true" socket- binding="https"/>
I created a jsse element as directed by https://docs.jboss.org/author/display/AS7/Security+subsystem+configuration
Where do I put this jsse element in standalone.xml and how do I tie it to the https connector?
Okay, I figured it out after searching for "Jboss 7" and https together. http://community.jboss.org/message/625454
and
http://docs.jboss.org/jbossweb/7.0.x/config/ssl.html were helpful resources.
A jsse element is not necessary in this case, all I needed to do was add
<ssl key-alias="<alias>" password="<password>" certificate-key-file="<path>" verify-client="true" ca-certificate-file="<path>"/>
Although there is a bug, https://issues.jboss.org/browse/AS7-1680, in which the value of ca-certificate-file is ignored. I order to get client authentication the truststore has to be passed a different way, either through standalone.conf or
<system-properties>
<property name="javax.net.ssl.trustStore" value="<path to truststore file>"/>
</system-properties>

How can I get client certificate authentication working in JBoss 5.1.0.GA when I'm using APR, and not all web deployments use CLIENT-CERT auth?

Note: I will be answering my own question... just wanted to add this tidbit to the collective wisdom of The Internets.
I've successfully configured certificate authentication on my JBoss 5.1.0.GA server, largely with the help of the information on this page: http://docs.jboss.org/jbossas/jboss4guide/r1/html/ch8.chapter.html
I have one context (let's call it /openContext) that doesn't require any authentication, and another context (let's call it /securedContext) that requires client certificate authentication (i.e., it's configured to use CLIENT-CERT in web.xml). When using JBoss's default web connector, this works splendidly. I can hit http://myhost/openContext and I'm not prompted for a certificate, but when I hit http://myhost/securedContext, I'm prompted for a client certificate as I'd expect.
However, when I install JBossWeb Native and use APR as my web connector, I'm no longer prompted for a certificate when I hit http://myhost/securedContext.
My APR connector config in server.xml looks like:
<Connector protocol="HTTP/1.1" SSLEnabled="true"
port="8443" address="${jboss.bind.address}"
scheme="https" secure="true" clientAuth="false"
SSLProtocol="SSLv3+TLSv1"
SSLCipherSuite="ALL:!ADH:!SSLv2:!EXPORT40:!EXP:!LOW"
SSLRandomSeed="/dev/urandom"
SSLCertificateFile="/etc/pki/tls/certs/mycert.crt"
SSLCertificateKeyFile="/etc/pki/tls/private/mycert.key"
SSLPassword="mypasswordwhichiassureyouisbetterthanthisone"
SSLCACertificateFile="/etc/pki/tls/certs/clientCAs.crt"
/>
I've also tried adding the SSLVerifyClient parameter to that configuration and setting it to optional, but that prompts for a certificate in both /openContext and /securedContext, which isn't the behavior I want.
How can I get JBoss with APR to require certificate authentication for one web context, but not another web context?
What worked for me was to just add a whole new web connector, and have clients use that alternate port for the secured web context. My connectors config now looks like:
<Connector protocol="HTTP/1.1" SSLEnabled="true"
port="8443" address="${jboss.bind.address}"
scheme="https" secure="true" clientAuth="false"
SSLProtocol="SSLv3+TLSv1"
SSLCipherSuite="ALL:!ADH:!SSLv2:!EXPORT40:!EXP:!LOW"
SSLRandomSeed="/dev/urandom"
SSLCertificateFile="/etc/pki/tls/certs/mycert.crt"
SSLCertificateKeyFile="/etc/pki/tls/private/mycert.key"
SSLPassword="mypasswordwhichiassureyouisbetterthanthisone"
/>
<Connector protocol="HTTP/1.1" SSLEnabled="true"
port="8543" address="${jboss.bind.address}"
scheme="https" secure="true" clientAuth="true"
SSLProtocol="SSLv3+TLSv1"
SSLCipherSuite="ALL:!ADH:!SSLv2:!EXPORT40:!EXP:!LOW"
SSLRandomSeed="/dev/urandom"
SSLCertificateFile="/etc/pki/tls/certs/mycert.crt"
SSLCertificateKeyFile="/etc/pki/tls/private/mycert.key"
SSLPassword="mypasswordwhichiassureyouisbetterthanthisone"
SSLCACertificateFile="/etc/pki/tls/certs/clientCAs.crt"
SSLVerifyClient="require"
/>
Now, if I hit http://myhost:8443/openContext, I'm not prompted for a certificate, but when I hit http://myhost:8543/securedContext, I am prompted for a certificate. Of course, I can still access either web app with the "wrong" port, but the consequences are negligible for my purposes. If a client hits http://myhost:8443/securedContext, they simply get an HTTP authentication error. If a client hits http://myhost:8543/openContext, they're prompted for a client certificate. If they provide one, great (though I don't care who you are), and if they don't provide one or provide an invalid one, they get an HTTP auth error (they should have used the correct port in the first place).
I'm pretty sure there's an alternative way to get this working without requiring a second connector by putting httpd in front of JBoss and doing some clever configuration there, but this worked well enough for my purposes.