In a software for a customer we have to read given URLs to parse their content. Also the customer needs to activate Tomcat-Security-Manager to let Java-Policies control what the program does.
Now, with reading URLs the exception "javax.net.ssl.SSLKeyException: RSA premaster secret error" happens, but only under certain conditions:
The Security-violation happens somewhere in Java-Code, an AllPermission restricted to our codebase doesn't prevent the error.
So, does someone has an idea, which permission to set for Java 6, so that it can process HTTPS?
Other information: It's running inside a tomcat, on a Debian-Linux with OpenJDK.
EDIT: I added the Java-Param "-Djava.security.debug=access,failure" to Tomcats /etc/default/tomcat6 in the variable JAVA_OPTS. But in the Logs I have no additional messages. Might it be possible the code asks the permissions before triggering them?
EDIT2: I found the correct place and got the full stacktrace (removed specific customer parts):
javax.net.ssl.SSLKeyException: RSA premaster secret error
at [...]
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at javax.security.auth.Subject.doAsPrivileged(Subject.java:537)
at java.lang.Thread.run(Thread.java:701)
Caused by: java.security.NoSuchAlgorithmException: SunTlsRsaPremasterSecret KeyGenerator not available
at javax.crypto.KeyGenerator.<init>(KeyGenerator.java:141)
at javax.crypto.KeyGenerator.getInstance(KeyGenerator.java:191)
... 14 more
EDIT3: So far I was under the assumption that the Java-class URL was used to access the contents of the resource. But that is untrue. It is using from Grails-Code the Groovy-URL-object with the getText()-method:
new URL(params.url).text
The error is happening on this line. It's Grails-version 2.2.4.
The root cause is the presence of sunjce_provider.jar
in multiple locations. This was discovered by the OP after it was suggested as one of a number of possible root causes (see the very end of this answer and the comment trail). As per OP's comment:
I have the sunjce_provider.jar in multiple directories. I tried to give all three locations for Java 6 the rights, although clearly only one is JAVA_HOME - and it worked. Somehow one of the other locations is used, although it isn't in the java.ext.dirs-property
Therefore the resolution in this case was to ensure that the app had rights to access the correct copy of sunjce_provider.jar
I've left the main points from my original answer and the comments that helped with diagnosis below for anyone who finds this later
This will not happen with http
because your web app (that is the client for this connection, even though it's running in tomcat), does not need to generate a key in this configuration.
The fact it only occurs when SecurityManager is enabled, but not if disabled or with global AllPermission suggests its a file permission error. It suggests that it is not a problem with the key length (e.g. the one mentioned here)
Other similar reports on the web indicate that the likely root cause is a missing jar (usually sunjce_provider.jar is cited). The stack trace confirms that the root cause exception is a NoSuchAlgorithmException
where the KeyGenerator
is looking for algorithm SunTlsRsaPremasterSecret
and can't find it. In your case, as this only occurs with a particular SecurityManager
configuration, it could be an inaccessible jar (due to security permissions).
My guess would be that you have not enabled grant codeBase
permissions to the correct directory that contains the jar needed for your RSA keygen algorithm. My suggested course of action would be to go through your webapp and JRE directory structure to find where runtime jars are kept and ensure that they have permissions grant
ed in catalina.policy
.
In the default configuration - for instance - you should see somewhere
// These permissions apply to all shared system extensions
grant codeBase "file:${java.home}/jre/lib/ext/-" {
permission java.security.AllPermission;
};
For details of what this means exactly see this section of the tomcat security manager "How To". You need to check a few things - make sure that ${java.home}/jre/lib/ext/
is where your runtime jars are. If not - alter the path to point to the right place (it is where they are on my version of OpenJDK 6 - build 27). In particular you should see sunjce_provider.jar
and sunpkcs11.jar
in there. Make sure the above section exists in your policy file.
It may be that the code is depending on some jars that are within your webapp - e.g. in ${catalina.base}/path/to/your/webapp/WEB-INF/classes/
- in which case you need to grant permissions to that directory.
The app being unable to find or accesssunpkcs11.jar
will give the same error message (verified on Ubuntu+openjdk6+tomcat6 system). It is likely that duplicate copies of that jar will, too.
check /etc/java-6-openjdk/security/java.security
. It should list providers in there - check there's a line something like security.provider.n = sun.security.pkcs11.SunPKCS11
in there - if that line is missing you also get this error (verified on same system)
This Debian bug report talks about problems with the location of jars when running under a SecurityManager
As per the other answer, you might try adding -Djava.security.debug=access,failure
to CATALINA_OPTS
or JAVA_OPTS
in your catalina.sh to enable debugging - which should log to catalina.out
by default (or wherever you have set your logging to via CATALINA_OUT
in catalina.sh. You should see output from the SecurityManager
there.
You can also try -Djava.security.debug=all. You will get a huge
catalina.out`, but you can grep for words that might help (like "fail"!!!)
Your exception is being thrown here. Looking at how that exception could be thrown - this method must return null. It swallows Exception
s - which isn't nice and makes it hard to diagnose exactly which part of that code is failing. My money would be on this line - where canUseProvider()
might return false
. This all points back to the provider jar being incaccessible for some reason.
I'm assuming you didn't see any access violations in the output, even with -Djava.security.debug=access,failure
. You could try -Djava.security.debug=all
, although that may well simply produce more irrelevant logging. If there is no access violation, you may have two versions of that jar on your classpath somehow and the runtime is accessing (or trying to access the wrong one). A case similar to this is described in this Q/A.