4

I have a little Java Applet and I have an annoying issue. I have signed my JAR with my own keystore using jarsigner tool (following these instructions).

The Java Applet downloads a signed JAR and tries to launch it with an extended class of URLClassLoader. This JAR tries to execute this line of code:

ClassLoader.getSystemClassLoader().getResource("aResource");

It fails with a large stack trace finished by:

Caused by: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getClassLoader")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:366)
    at java.security.AccessController.checkPermission(AccessController.java:555)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1476)
    at test.SecondJAR.main(SecondJAR.java:8)

(Line 8 of test.SecondJAR corresponds to getResource(...) method

When the Java Applet is launched, the user is prompted to accept the certificate if he/she trusts the publisher:

Message to the user

Even if I accept it, the exception occurred. Even if I install the certificate, and the prompt message is automatically accepted, the exception occurred.

I have tried too this:

AccessController.doPrivileged(new PrivilegedAction<Object>() {
    public Object run() {
        ClassLoader.getSystemClassLoader().getResource("aResource");
        return null;
    }
});

And it fails with the same exception.

Any help would be appreciated!

logoff
  • 3,347
  • 5
  • 41
  • 58
  • You seem to be saying that the exception is coming from `getSystemClassLoader` or `getResource` but the stack trace says `getContextClassLoader` (use of which is rarely a good idea). Perhaps a bit more stack trace would be useful. – Tom Hawtin - tackline Jul 03 '12 at 09:50
  • So the exception is thrown by the downloaded code. By default `URLClassLoader` will use permissions (through `ProtectionDomain`) appropriate from where the code was downloaded (Same Origin Policy). It's very difficult to get this sort of thing right. Do you really need to do this? An apparently common reason to try this sort of thing is to avoid having to keep signing code. Mobile code needs to be signed to be trusted for good reason! – Tom Hawtin - tackline Jul 03 '12 at 10:02
  • @Tom I have a _Downloader Applet_ that obtains a generated JAR. this JAR is different each time and different for each user. I must download it each time and execute it. the second JAR is signed too! there must exist a way to do it ;-( – logoff Jul 03 '12 at 10:08
  • I have edited the question. now there is the complete stack trace. the exception is launched by the second JAR in the `getResource(...)` line. – logoff Jul 03 '12 at 10:20

2 Answers2

8

Finally I have found the answer!

I followed the guidelines of Andrew Thomson and I created a custom SecurityManager. My little security manager looks like this:

private class MySecurityManager extends SecurityManager {
    @Override
    public void checkPermission(Permission perm) {
        return;
    }
}

It is a neglected security manager that accepts all permissions. It should be improved allowing only getting system ClassLoader in runtime.

To use my ugly SecurityManager I added these lines at the beginning of Java Applet start() method:

SecurityManager sm = new MySecurityManager();
System.setSecurityManager(sm);

With this workaround, all the process worked as expected!

Maybe there exist other (better) solutions, but it worked for me.

Thank you all!

Community
  • 1
  • 1
logoff
  • 3,347
  • 5
  • 41
  • 58
  • 1
    Glad you got it sorted. :) But I suspect Tom (who knows 100 times more about security than I do), would recommend a more restricted security manager than that. From my limited knowledge, a security manager that allows dynamically loaded code 'all privileges' (which, BTW is a less restrictive SM than applied to a ***trusted applet***) is 'way too much' permission. – Andrew Thompson Jul 03 '12 at 15:03
  • ...as a 'for instance'. Imagine the code being launched, creates a `JFrame` that calls `setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)` - seemingly innocent, yet it ends the JVM that is running the frame, or probably also **the applet**. The typical trusted applet Security Manager (SM) would not permit calling `System.exit(n)`. That applet might be running in a web page where several applets share the **same** JVM. It is a 'guest' in that JVM, yet it is effectively 'burning the guest house down' by ending the JVM! – Andrew Thompson Jul 03 '12 at 15:18
  • I agree 100%, but anyway, as I mentioned, it is possible to create a SecurityManager allowing only specific permissions. I understand the drawbacks of adding these kind of privileges, but it is the only way I found. I expect somebody else show me a correct, better and standard way to achieve my objective: execute signed resources loaded at runtime. – logoff Jul 03 '12 at 15:21
  • 1
    With this security manager any applet (running in the same process) will be able to do anything. That's really quite bad. In Java permissions of code are generally given based on `CodeSource` delivered through `ProtectionDomain` (there's also the non-public `AppContext` mechanism which is really odd). – Tom Hawtin - tackline Jul 03 '12 at 15:22
  • OK. 1) check the code-base of the classes asking for the permission. Is it code that you expect? 2) What permissions does it require? If you are expecting local file-system and printer access, why is it trying to access a foreign site, or code-base..? – Andrew Thompson Jul 03 '12 at 15:25
  • I agree with all, but I need a way to execute my trusted signed code. My own code! – logoff Jul 03 '12 at 15:26
  • If it is code you wrote, restrict the SM to packages you expect to see.. (+ more, that is not all of it, since I can create packages of your spec.). I really feel I am doing less here than needed. I am a 'newbie' at security. :( – Andrew Thompson Jul 03 '12 at 15:28
  • @Andrew How do I check a my code ? It is generated each moment. It is signed, for me a good mechanism to trust it. – logoff Jul 04 '12 at 06:51
2

The problem is that the JRE only considers code in the original code-base to be trusted. Two possible solutions are:

  1. Set a custom security manager that allows the new code to have the privileges it requires.
  2. Wrap the new code in a PrivilegedAction & call it from AccessController.doPrivileged(..) method (just occurred to me as a possibility, not sure if I understand the scope of it, completely untested).
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433